diff --git a/internal/aghnet/net.go b/internal/aghnet/net.go index 268380bd..2de9c630 100644 --- a/internal/aghnet/net.go +++ b/internal/aghnet/net.go @@ -154,10 +154,13 @@ func GetValidNetInterfacesForWeb() (netIfaces []*NetInterface, err error) { return netIfaces, nil } -// GetInterfaceByIP returns the name of interface containing provided ip. +// InterfaceByIP returns the name of the interface bound to ip. // -// TODO(e.burkov): See TODO on GetValidInterfacesForWeb. -func GetInterfaceByIP(ip net.IP) string { +// TODO(a.garipov, e.burkov): This function is technically incorrect, since one +// IP address can be shared by multiple interfaces in some configurations. +// +// TODO(e.burkov): See TODO on GetValidNetInterfacesForWeb. +func InterfaceByIP(ip net.IP) (ifaceName string) { ifaces, err := GetValidNetInterfacesForWeb() if err != nil { return "" @@ -177,7 +180,7 @@ func GetInterfaceByIP(ip net.IP) string { // GetSubnet returns pointer to net.IPNet for the specified interface or nil if // the search fails. // -// TODO(e.burkov): See TODO on GetValidInterfacesForWeb. +// TODO(e.burkov): See TODO on GetValidNetInterfacesForWeb. func GetSubnet(ifaceName string) *net.IPNet { netIfaces, err := GetValidNetInterfacesForWeb() if err != nil { diff --git a/internal/aghnet/net_test.go b/internal/aghnet/net_test.go index 40d395ba..d4ee59ee 100644 --- a/internal/aghnet/net_test.go +++ b/internal/aghnet/net_test.go @@ -132,7 +132,7 @@ func TestGatewayIP(t *testing.T) { } } -func TestGetInterfaceByIP(t *testing.T) { +func TestInterfaceByIP(t *testing.T) { ifaces, err := GetValidNetInterfacesForWeb() require.NoError(t, err) require.NotEmpty(t, ifaces) @@ -142,7 +142,7 @@ func TestGetInterfaceByIP(t *testing.T) { require.NotEmpty(t, iface.Addresses) for _, ip := range iface.Addresses { - ifaceName := GetInterfaceByIP(ip) + ifaceName := InterfaceByIP(ip) require.Equal(t, iface.Name, ifaceName) } }) diff --git a/internal/home/controlinstall.go b/internal/home/controlinstall.go index 5304b794..c46f3459 100644 --- a/internal/home/controlinstall.go +++ b/internal/home/controlinstall.go @@ -216,7 +216,7 @@ func (web *Web) handleInstallCheckConfig(w http.ResponseWriter, r *http.Request) func handleStaticIP(ip net.IP, set bool) staticIPJSON { resp := staticIPJSON{} - interfaceName := aghnet.GetInterfaceByIP(ip) + interfaceName := aghnet.InterfaceByIP(ip) resp.Static = "no" if len(interfaceName) == 0 { diff --git a/internal/v1/cmd/cmd.go b/internal/v1/cmd/cmd.go index 1f1cc64e..2f61509b 100644 --- a/internal/v1/cmd/cmd.go +++ b/internal/v1/cmd/cmd.go @@ -8,12 +8,11 @@ import ( "context" "io/fs" "math/rand" - "net" + "net/netip" "time" "github.com/AdguardTeam/AdGuardHome/internal/v1/websvc" "github.com/AdguardTeam/golibs/log" - "github.com/AdguardTeam/golibs/netutil" ) // Main is the entry point of application. @@ -32,12 +31,9 @@ func Main(clientBuildFS fs.FS) { // TODO(a.garipov): Make configurable. web := websvc.New(&websvc.Config{ - Addresses: []*netutil.IPPort{{ - IP: net.IP{127, 0, 0, 1}, - Port: 3001, - }}, - Start: start, - Timeout: 60 * time.Second, + Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:3001")}, + Start: start, + Timeout: 60 * time.Second, }) err := web.Start() diff --git a/internal/v1/websvc/websvc.go b/internal/v1/websvc/websvc.go index 9af22a15..1a9a6a09 100644 --- a/internal/v1/websvc/websvc.go +++ b/internal/v1/websvc/websvc.go @@ -10,13 +10,13 @@ import ( "io" "net" "net/http" + "net/netip" "sync" "time" "github.com/AdguardTeam/AdGuardHome/internal/v1/agh" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/log" - "github.com/AdguardTeam/golibs/netutil" httptreemux "github.com/dimfeld/httptreemux/v5" ) @@ -27,11 +27,11 @@ type Config struct { TLS *tls.Config // Addresses are the addresses on which to serve the plain HTTP API. - Addresses []*netutil.IPPort + Addresses []netip.AddrPort // SecureAddresses are the addresses on which to serve the HTTPS API. If // SecureAddresses is not empty, TLS must not be nil. - SecureAddresses []*netutil.IPPort + SecureAddresses []netip.AddrPort // Start is the time of start of AdGuard Home. Start time.Time diff --git a/internal/v1/websvc/websvc_test.go b/internal/v1/websvc/websvc_test.go index 459ffd14..de4a9f5d 100644 --- a/internal/v1/websvc/websvc_test.go +++ b/internal/v1/websvc/websvc_test.go @@ -3,14 +3,13 @@ package websvc_test import ( "context" "io" - "net" "net/http" + "net/netip" "net/url" "testing" "time" "github.com/AdguardTeam/AdGuardHome/internal/v1/websvc" - "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -30,11 +29,8 @@ func newTestServer(t testing.TB) (svc *websvc.Service, addr string) { t.Helper() c := &websvc.Config{ - TLS: nil, - Addresses: []*netutil.IPPort{{ - IP: net.IP{127, 0, 0, 1}, - Port: 0, - }}, + TLS: nil, + Addresses: []netip.AddrPort{netip.MustParseAddrPort("127.0.0.1:0")}, SecureAddresses: nil, Timeout: testTimeout, Start: testStart, diff --git a/openapi/v1.yaml b/openapi/v1.yaml index ff515692..bdf49383 100644 --- a/openapi/v1.yaml +++ b/openapi/v1.yaml @@ -144,6 +144,13 @@ '/health-check': 'get': 'operationId': 'HealthCheck' + 'responses': + '200': + 'description': > + An OK response. + 'content': + 'text/plain': + 'example': 'OK' 'servers': - 'url': '/' 'summary': 'Check if the server is up.' @@ -874,6 +881,26 @@ 'tags': - 'settings' + '/settings/http': + 'patch': + 'operationId': 'PatchV1SettingsHttp' + 'requestBody': + '$ref': '#/components/requestBodies/PatchV1SettingsHttpReq' + 'responses': + '200': + '$ref': '#/components/responses/PatchV1SettingsHttpResp' + '400': + '$ref': '#/components/responses/BadRequestResp' + '401': + '$ref': '#/components/responses/UnauthorizedResp' + '422': + '$ref': '#/components/responses/UnprocessableEntityResp' + '500': + '$ref': '#/components/responses/InternalServerErrorResp' + 'summary': 'Update web interface settings.' + 'tags': + - 'settings' + '/settings/log': 'patch': 'operationId': 'PatchV1SettingsLog' @@ -1209,6 +1236,13 @@ '$ref': '#/components/schemas/PatchV1SettingsDnsReq' 'required': true + 'PatchV1SettingsHttpReq': + 'content': + 'application/json': + 'schema': + '$ref': '#/components/schemas/PatchV1SettingsHttpReq' + 'required': true + 'PatchV1SettingsLogReq': 'content': 'application/json': @@ -1604,6 +1638,14 @@ 'description': > A successful response to a `PATCH /api/v1/settings/dns` request. + 'PatchV1SettingsHttpResp': + 'content': + 'application/json': + 'schema': + '$ref': '#/components/schemas/PatchV1SettingsHttpResp' + 'description': > + A successful response to a `PATCH /api/v1/settings/http` request. + 'PatchV1SettingsLogResp': 'content': 'application/json': @@ -2229,6 +2271,9 @@ - 'description': > DNS server settings. 'example': + 'addresses': + - '127.0.0.1:53' + - '192.168.1.1:53' 'blocking_mode': 'default' 'bootstrap_servers': - '9.9.9.10' @@ -2244,7 +2289,9 @@ 'upstream_servers': - '1.1.1.1' - '8.8.8.8' + 'upstream_timeout': '1s' 'required': + - 'addresses' - 'blocking_mode' - 'bootstrap_servers' - 'cache_size' @@ -2256,6 +2303,7 @@ - 'rate_limit' - 'upstream_mode' - 'upstream_servers' + - 'upstream_timeout' 'DnsSettingsPatch': 'description': > @@ -2265,6 +2313,13 @@ 'upstream_servers': - '1.1.1.1' 'properties': + 'addresses': + 'description': > + Addresses on which to serve plain DNS, in ip:port format. Empty + array disables plain DNS. + 'items': + 'type': 'string' + 'type': 'array' 'blocking_ipv4': 'description': > IPv4 address to respond with when `blocking_mode` is `custom_ip`. @@ -2340,6 +2395,10 @@ 'items': '$ref': '#/components/schemas/UpstreamServerAddr' 'type': 'array' + 'upstream_timeout': + 'description': > + Upstream request timeout, as a human readable duration. + 'type': 'string' 'type': 'object' 'DnsType': @@ -3038,6 +3097,8 @@ '$ref': '#/components/schemas/DhcpSettings' 'dns': '$ref': '#/components/schemas/DnsSettings' + 'http': + '$ref': '#/components/schemas/HttpSettings' 'log': '$ref': '#/components/schemas/LogSettings' 'protection': @@ -3049,6 +3110,7 @@ 'required': - 'dhcp' - 'dns' + - 'http' - 'log' - 'protection' - 'stats' @@ -3432,6 +3494,53 @@ - 'version' 'type': 'object' + 'HttpSettings': + 'allOf': + - '$ref': '#/components/schemas/HttpSettingsPatch' + - 'description': > + HTTP interface server settings. + + **TODO(a.garipov): Finish, split from TLS settings.** + 'example': + 'addresses': + - '127.0.0.1:80' + - '192.168.1.1:80' + 'secure_addresses': + - '127.0.0.1:443' + - '192.168.1.1:443' + 'force_https': true + 'required': + - 'addresses' + - 'secure_addresses' + - 'force_https' + + 'HttpSettingsPatch': + 'description': > + HTTP server settings update object. + 'example': + 'force_https': false + 'properties': + 'addresses': + 'description': > + Addresses on which to serve the plain-HTTP web interface and API, in + ip:port format. Empty array disables the web interface over plain + HTTP. + 'items': + 'type': 'string' + 'type': 'array' + 'force_https': + 'description': > + If `true`, enabled the HTTP-to-HTTPS redirect. + 'type': 'boolean' + 'secure_addresses': + 'description': > + Addresses on which to serve the HTTPS web interface and API, in + ip:port format. Empty array disables the web interface over HTTPS. + 'items': + 'type': 'string' + 'type': 'array' + 'type': 'object' + 'InternalServerErrorResp': 'example': 'code': 'RNT000' @@ -3725,6 +3834,12 @@ 'PatchV1SettingsDnsResp': '$ref': '#/components/schemas/DnsSettings' + 'PatchV1SettingsHttpReq': + '$ref': '#/components/schemas/HttpSettingsPatch' + + 'PatchV1SettingsHttpResp': + '$ref': '#/components/schemas/HttpSettings' + 'PatchV1SettingsLogReq': '$ref': '#/components/schemas/LogSettingsPatch' @@ -4750,7 +4865,6 @@ 'example': 'certificate_path': '/etc/ssl/example.com.cert' 'enabled': true - 'force_https': true 'port_dns_over_quic': 784 'port_dns_over_tls': 853 'port_https': 443 @@ -4758,7 +4872,6 @@ 'server_name': 'dns.example.com' 'required': - 'enabled' - - 'force_https' - 'port_dns_over_quic' - 'port_dns_over_tls' - 'port_https' @@ -4793,10 +4906,6 @@ over HTTPS, and the DNS server will listen requests over DNS-over-TLS and other protocols. 'type': 'boolean' - 'force_https': - 'description': > - If `true`, enabled the HTTP-to-HTTPS redirect. - 'type': 'boolean' 'port_dns_over_quic': 'default': 784 'description': >