Pull request: 3172 fix mobileconfig
Merge in DNS/adguard-home from 3172-mobileconfig to master
Updates #3172.
Updates #2497.
Squashed commit of the following:
commit 30549ef4eda9d88f0738089e901492d7369caa25
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Jun 1 21:00:17 2021 +0300
all: log changes
commit 9b9429447430a8e5656b992c04c4a74606dc5f9f
Author: Ildar Kamalov <ik@adguard.com>
Date: Tue Jun 1 17:56:59 2021 +0300
client: always show port input
commit 6d6a0bdfaa849220a5ddb4a17502ab05379d7a1c
Merge: 13a3bffd 77946a7f
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Tue Jun 1 17:50:41 2021 +0300
Merge branch 'master' into 3172-mobileconfig
commit 13a3bffd4dd6ccabf3d261f17b2c758a5c61eb9c
Author: Ildar Kamalov <ik@adguard.com>
Date: Tue Jun 1 17:20:17 2021 +0300
client: add port to mobile config form
commit f6abe0b6044572f3801c31b683e76f90c4a28487
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Mon May 31 19:43:37 2021 +0300
home: imp cyclo
commit c304a0bacdca6f8b5ffd21f3d00c8244ea9e4e36
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Mon May 31 18:19:46 2021 +0300
home: reduce allocs
commit 10a7678861079b710bb0ef14569c60a09612ec70
Author: Eugene Burkov <e.burkov@adguard.com>
Date: Mon May 24 20:05:08 2021 +0300
all: make the host parameter required
This commit is contained in:
parent
77946a7f72
commit
3f1fd56b17
|
@ -9,6 +9,7 @@
|
||||||
*.db
|
*.db
|
||||||
*.log
|
*.log
|
||||||
*.snap
|
*.snap
|
||||||
|
/agh-backup/
|
||||||
/bin/
|
/bin/
|
||||||
/build/*
|
/build/*
|
||||||
/build2/*
|
/build2/*
|
||||||
|
|
|
@ -15,6 +15,8 @@ and this project adheres to
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Support for custom port in DNS-over-HTTPS profiles for Apple's devices
|
||||||
|
([#3172]).
|
||||||
- `darwin/arm64` support ([#2443]).
|
- `darwin/arm64` support ([#2443]).
|
||||||
- `freebsd/arm64` support ([#2441]).
|
- `freebsd/arm64` support ([#2441]).
|
||||||
- Output of the default addresses of the upstreams used for resolving PTRs for
|
- Output of the default addresses of the upstreams used for resolving PTRs for
|
||||||
|
@ -53,6 +55,7 @@ released by then.
|
||||||
[#2441]: https://github.com/AdguardTeam/AdGuardHome/issues/2441
|
[#2441]: https://github.com/AdguardTeam/AdGuardHome/issues/2441
|
||||||
[#2443]: https://github.com/AdguardTeam/AdGuardHome/issues/2443
|
[#2443]: https://github.com/AdguardTeam/AdGuardHome/issues/2443
|
||||||
[#3136]: https://github.com/AdguardTeam/AdGuardHome/issues/3136
|
[#3136]: https://github.com/AdguardTeam/AdGuardHome/issues/3136
|
||||||
|
[#3172]: https://github.com/AdguardTeam/AdGuardHome/issues/3172
|
||||||
[#3184]: https://github.com/AdguardTeam/AdGuardHome/issues/3184
|
[#3184]: https://github.com/AdguardTeam/AdGuardHome/issues/3184
|
||||||
[#3185]: https://github.com/AdguardTeam/AdGuardHome/issues/3185
|
[#3185]: https://github.com/AdguardTeam/AdGuardHome/issues/3185
|
||||||
[#3186]: https://github.com/AdguardTeam/AdGuardHome/issues/3186
|
[#3186]: https://github.com/AdguardTeam/AdGuardHome/issues/3186
|
||||||
|
|
|
@ -154,7 +154,8 @@ const getTabs = ({
|
||||||
tlsAddress,
|
tlsAddress,
|
||||||
httpsAddress,
|
httpsAddress,
|
||||||
showDnsPrivacyNotice,
|
showDnsPrivacyNotice,
|
||||||
server_name,
|
serverName,
|
||||||
|
portHttps,
|
||||||
t,
|
t,
|
||||||
}) => ({
|
}) => ({
|
||||||
Router: {
|
Router: {
|
||||||
|
@ -276,9 +277,10 @@ const getTabs = ({
|
||||||
</div>
|
</div>
|
||||||
<MobileConfigForm
|
<MobileConfigForm
|
||||||
initialValues={{
|
initialValues={{
|
||||||
host: server_name,
|
host: serverName,
|
||||||
clientId: '',
|
clientId: '',
|
||||||
protocol: MOBILE_CONFIG_LINKS.DOH,
|
protocol: MOBILE_CONFIG_LINKS.DOH,
|
||||||
|
port: portHttps,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -311,7 +313,8 @@ const renderContent = ({ title, list, getTitle }) => (
|
||||||
|
|
||||||
const Guide = ({ dnsAddresses }) => {
|
const Guide = ({ dnsAddresses }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const server_name = useSelector((state) => state.encryption?.server_name);
|
const serverName = useSelector((state) => state.encryption?.server_name);
|
||||||
|
const portHttps = useSelector((state) => state.encryption?.port_https);
|
||||||
const tlsAddress = dnsAddresses?.filter((item) => item.includes('tls://')) ?? '';
|
const tlsAddress = dnsAddresses?.filter((item) => item.includes('tls://')) ?? '';
|
||||||
const httpsAddress = dnsAddresses?.filter((item) => item.includes('https://')) ?? '';
|
const httpsAddress = dnsAddresses?.filter((item) => item.includes('https://')) ?? '';
|
||||||
const showDnsPrivacyNotice = httpsAddress.length < 1 && tlsAddress.length < 1;
|
const showDnsPrivacyNotice = httpsAddress.length < 1 && tlsAddress.length < 1;
|
||||||
|
@ -322,7 +325,8 @@ const Guide = ({ dnsAddresses }) => {
|
||||||
tlsAddress,
|
tlsAddress,
|
||||||
httpsAddress,
|
httpsAddress,
|
||||||
showDnsPrivacyNotice,
|
showDnsPrivacyNotice,
|
||||||
server_name,
|
serverName,
|
||||||
|
portHttps,
|
||||||
t,
|
t,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,17 @@ import i18next from 'i18next';
|
||||||
import cn from 'classnames';
|
import cn from 'classnames';
|
||||||
|
|
||||||
import { getPathWithQueryString } from '../../../helpers/helpers';
|
import { getPathWithQueryString } from '../../../helpers/helpers';
|
||||||
import { FORM_NAME, MOBILE_CONFIG_LINKS } from '../../../helpers/constants';
|
import { FORM_NAME, MOBILE_CONFIG_LINKS, STANDARD_HTTPS_PORT } from '../../../helpers/constants';
|
||||||
import {
|
import {
|
||||||
renderInputField,
|
renderInputField,
|
||||||
renderSelectField,
|
renderSelectField,
|
||||||
|
toNumber,
|
||||||
} from '../../../helpers/form';
|
} from '../../../helpers/form';
|
||||||
import {
|
import {
|
||||||
validateClientId,
|
validateClientId,
|
||||||
validateServerName,
|
validateServerName,
|
||||||
|
validatePort,
|
||||||
|
validateIsSafePort,
|
||||||
} from '../../../helpers/validators';
|
} from '../../../helpers/validators';
|
||||||
|
|
||||||
const getDownloadLink = (host, clientId, protocol, invalid) => {
|
const getDownloadLink = (host, clientId, protocol, invalid) => {
|
||||||
|
@ -53,7 +56,9 @@ const MobileConfigForm = ({ invalid }) => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { host, clientId, protocol } = formValues;
|
const {
|
||||||
|
host, clientId, protocol, port,
|
||||||
|
} = formValues;
|
||||||
|
|
||||||
const githubLink = (
|
const githubLink = (
|
||||||
<a
|
<a
|
||||||
|
@ -65,21 +70,52 @@ const MobileConfigForm = ({ invalid }) => {
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getHostName = () => {
|
||||||
|
if (port
|
||||||
|
&& port !== STANDARD_HTTPS_PORT
|
||||||
|
&& protocol === MOBILE_CONFIG_LINKS.DOH
|
||||||
|
) {
|
||||||
|
return `${host}:${port}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return host;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={(e) => e.preventDefault()}>
|
<form onSubmit={(e) => e.preventDefault()}>
|
||||||
<div>
|
<div>
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<label htmlFor="host" className="form__label">
|
<div className="row">
|
||||||
{i18next.t('dhcp_table_hostname')}
|
<div className="col">
|
||||||
</label>
|
<label htmlFor="host" className="form__label">
|
||||||
<Field
|
{i18next.t('dhcp_table_hostname')}
|
||||||
name="host"
|
</label>
|
||||||
type="text"
|
<Field
|
||||||
component={renderInputField}
|
name="host"
|
||||||
className="form-control"
|
type="text"
|
||||||
placeholder={i18next.t('form_enter_hostname')}
|
component={renderInputField}
|
||||||
validate={validateServerName}
|
className="form-control"
|
||||||
/>
|
placeholder={i18next.t('form_enter_hostname')}
|
||||||
|
validate={validateServerName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{protocol === MOBILE_CONFIG_LINKS.DOH && (
|
||||||
|
<div className="col">
|
||||||
|
<label htmlFor="port" className="form__label">
|
||||||
|
{i18next.t('encryption_https')}
|
||||||
|
</label>
|
||||||
|
<Field
|
||||||
|
name="port"
|
||||||
|
type="number"
|
||||||
|
component={renderInputField}
|
||||||
|
className="form-control"
|
||||||
|
placeholder={i18next.t('encryption_https')}
|
||||||
|
validate={[validatePort, validateIsSafePort]}
|
||||||
|
normalize={toNumber}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="form__group form__group--settings">
|
<div className="form__group form__group--settings">
|
||||||
<label htmlFor="clientId" className="form__label form__label--with-desc">
|
<label htmlFor="clientId" className="form__label form__label--with-desc">
|
||||||
|
@ -119,7 +155,7 @@ const MobileConfigForm = ({ invalid }) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{getDownloadLink(host, clientId, protocol, invalid)}
|
{getDownloadLink(getHostName(), clientId, protocol, invalid)}
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -186,7 +186,7 @@ func GetSubnet(ifaceName string) *net.IPNet {
|
||||||
|
|
||||||
// CheckPortAvailable - check if TCP port is available
|
// CheckPortAvailable - check if TCP port is available
|
||||||
func CheckPortAvailable(host net.IP, port int) error {
|
func CheckPortAvailable(host net.IP, port int) error {
|
||||||
ln, err := net.Listen("tcp", net.JoinHostPort(host.String(), strconv.Itoa(port)))
|
ln, err := net.Listen("tcp", JoinHostPort(host.String(), port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ func CheckPortAvailable(host net.IP, port int) error {
|
||||||
|
|
||||||
// CheckPacketPortAvailable - check if UDP port is available
|
// CheckPacketPortAvailable - check if UDP port is available
|
||||||
func CheckPacketPortAvailable(host net.IP, port int) error {
|
func CheckPacketPortAvailable(host net.IP, port int) error {
|
||||||
ln, err := net.ListenPacket("udp", net.JoinHostPort(host.String(), strconv.Itoa(port)))
|
ln, err := net.ListenPacket("udp", JoinHostPort(host.String(), port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -424,3 +424,9 @@ func CollectAllIfacesAddrs() (addrs []string, err error) {
|
||||||
|
|
||||||
return addrs, nil
|
return addrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JoinHostPort is a convinient wrapper for net.JoinHostPort with port of type
|
||||||
|
// int.
|
||||||
|
func JoinHostPort(host string, port int) (hostport string) {
|
||||||
|
return net.JoinHostPort(host, strconv.Itoa(port))
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ func CloneSlice(a []string) (b []string) {
|
||||||
|
|
||||||
// Coalesce returns the first non-empty string. It is named after the function
|
// Coalesce returns the first non-empty string. It is named after the function
|
||||||
// COALESCE in SQL except that since strings in Go are non-nullable, it uses an
|
// COALESCE in SQL except that since strings in Go are non-nullable, it uses an
|
||||||
// empty string as a NULL value. If strs is empty, it returns an empty string.
|
// empty string as a NULL value. If strs or all it's elements are empty, it
|
||||||
|
// returns an empty string.
|
||||||
func Coalesce(strs ...string) (res string) {
|
func Coalesce(strs ...string) (res string) {
|
||||||
for _, s := range strs {
|
for _, s := range strs {
|
||||||
if s != "" {
|
if s != "" {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/insomniacslk/dhcp/dhcpv4"
|
"github.com/insomniacslk/dhcp/dhcpv4"
|
||||||
|
@ -44,7 +45,7 @@ func CheckIfOtherDHCPServersPresentV4(ifaceName string) (ok bool, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
srcIP := ifaceIPNet[0]
|
srcIP := ifaceIPNet[0]
|
||||||
src := net.JoinHostPort(srcIP.String(), "68")
|
src := aghnet.JoinHostPort(srcIP.String(), 68)
|
||||||
dst := "255.255.255.255:67"
|
dst := "255.255.255.255:67"
|
||||||
|
|
||||||
hostname, _ := os.Hostname()
|
hostname, _ := os.Hostname()
|
||||||
|
@ -175,7 +176,7 @@ func CheckIfOtherDHCPServersPresentV6(ifaceName string) (ok bool, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
srcIP := ifaceIPNet[0]
|
srcIP := ifaceIPNet[0]
|
||||||
src := net.JoinHostPort(srcIP.String(), "546")
|
src := aghnet.JoinHostPort(srcIP.String(), 546)
|
||||||
dst := "[ff02::1:2]:547"
|
dst := "[ff02::1:2]:547"
|
||||||
|
|
||||||
req, err := dhcpv6.NewSolicit(iface.HardwareAddr)
|
req, err := dhcpv6.NewSolicit(iface.HardwareAddr)
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
|
@ -38,9 +37,11 @@ func httpError(w http.ResponseWriter, code int, format string, args ...interface
|
||||||
// addresses to a slice of strings.
|
// addresses to a slice of strings.
|
||||||
func appendDNSAddrs(dst []string, addrs ...net.IP) (res []string) {
|
func appendDNSAddrs(dst []string, addrs ...net.IP) (res []string) {
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
hostport := addr.String()
|
var hostport string
|
||||||
if config.DNS.Port != 53 {
|
if config.DNS.Port != 53 {
|
||||||
hostport = net.JoinHostPort(hostport, strconv.Itoa(config.DNS.Port))
|
hostport = aghnet.JoinHostPort(addr.String(), config.DNS.Port)
|
||||||
|
} else {
|
||||||
|
hostport = addr.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
dst = append(dst, hostport)
|
dst = append(dst, hostport)
|
||||||
|
@ -303,8 +304,7 @@ func handleHTTPSRedirect(w http.ResponseWriter, r *http.Request) (ok bool) {
|
||||||
if r.TLS == nil && web.forceHTTPS {
|
if r.TLS == nil && web.forceHTTPS {
|
||||||
hostPort := host
|
hostPort := host
|
||||||
if port := web.conf.PortHTTPS; port != defaultHTTPSPort {
|
if port := web.conf.PortHTTPS; port != defaultHTTPSPort {
|
||||||
portStr := strconv.Itoa(port)
|
hostPort = aghnet.JoinHostPort(host, port)
|
||||||
hostPort = net.JoinHostPort(host, portStr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
httpsURL := &url.URL{
|
httpsURL := &url.URL{
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -286,42 +285,48 @@ func shutdownSrv(ctx context.Context, cancel context.CancelFunc, srv *http.Serve
|
||||||
|
|
||||||
// Apply new configuration, start DNS server, restart Web server
|
// Apply new configuration, start DNS server, restart Web server
|
||||||
func (web *Web) handleInstallConfigure(w http.ResponseWriter, r *http.Request) {
|
func (web *Web) handleInstallConfigure(w http.ResponseWriter, r *http.Request) {
|
||||||
newSettings := applyConfigReq{}
|
req := applyConfigReq{}
|
||||||
err := json.NewDecoder(r.Body).Decode(&newSettings)
|
err := json.NewDecoder(r.Body).Decode(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, http.StatusBadRequest, "Failed to parse 'configure' JSON: %s", err)
|
httpError(w, http.StatusBadRequest, "Failed to parse 'configure' JSON: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if newSettings.Web.Port == 0 || newSettings.DNS.Port == 0 {
|
if req.Web.Port == 0 || req.DNS.Port == 0 {
|
||||||
httpError(w, http.StatusBadRequest, "port value can't be 0")
|
httpError(w, http.StatusBadRequest, "port value can't be 0")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
restartHTTP := true
|
restartHTTP := true
|
||||||
if config.BindHost.Equal(newSettings.Web.IP) && config.BindPort == newSettings.Web.Port {
|
if config.BindHost.Equal(req.Web.IP) && config.BindPort == req.Web.Port {
|
||||||
// no need to rebind
|
// no need to rebind
|
||||||
restartHTTP = false
|
restartHTTP = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate that hosts and ports are bindable
|
// validate that hosts and ports are bindable
|
||||||
if restartHTTP {
|
if restartHTTP {
|
||||||
err = aghnet.CheckPortAvailable(newSettings.Web.IP, newSettings.Web.Port)
|
err = aghnet.CheckPortAvailable(req.Web.IP, req.Web.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, http.StatusBadRequest, "Impossible to listen on IP:port %s due to %s",
|
httpError(
|
||||||
net.JoinHostPort(newSettings.Web.IP.String(), strconv.Itoa(newSettings.Web.Port)), err)
|
w,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
"can not listen on IP:port %s: %s",
|
||||||
|
aghnet.JoinHostPort(req.Web.IP.String(), req.Web.Port),
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = aghnet.CheckPacketPortAvailable(newSettings.DNS.IP, newSettings.DNS.Port)
|
err = aghnet.CheckPacketPortAvailable(req.DNS.IP, req.DNS.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, http.StatusBadRequest, "%s", err)
|
httpError(w, http.StatusBadRequest, "%s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = aghnet.CheckPortAvailable(newSettings.DNS.IP, newSettings.DNS.Port)
|
err = aghnet.CheckPortAvailable(req.DNS.IP, req.DNS.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, http.StatusBadRequest, "%s", err)
|
httpError(w, http.StatusBadRequest, "%s", err)
|
||||||
return
|
return
|
||||||
|
@ -331,10 +336,10 @@ func (web *Web) handleInstallConfigure(w http.ResponseWriter, r *http.Request) {
|
||||||
copyInstallSettings(&curConfig, &config)
|
copyInstallSettings(&curConfig, &config)
|
||||||
|
|
||||||
Context.firstRun = false
|
Context.firstRun = false
|
||||||
config.BindHost = newSettings.Web.IP
|
config.BindHost = req.Web.IP
|
||||||
config.BindPort = newSettings.Web.Port
|
config.BindPort = req.Web.Port
|
||||||
config.DNS.BindHosts = []net.IP{newSettings.DNS.IP}
|
config.DNS.BindHosts = []net.IP{req.DNS.IP}
|
||||||
config.DNS.Port = newSettings.DNS.Port
|
config.DNS.Port = req.DNS.Port
|
||||||
|
|
||||||
// TODO(e.burkov): StartMods() should be put in a separate goroutine at
|
// TODO(e.burkov): StartMods() should be put in a separate goroutine at
|
||||||
// the moment we'll allow setting up TLS in the initial configuration or
|
// the moment we'll allow setting up TLS in the initial configuration or
|
||||||
|
@ -349,8 +354,8 @@ func (web *Web) handleInstallConfigure(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
u := User{}
|
u := User{}
|
||||||
u.Name = newSettings.Username
|
u.Name = req.Username
|
||||||
Context.auth.UserAdd(&u, newSettings.Password)
|
Context.auth.UserAdd(&u, req.Password)
|
||||||
|
|
||||||
err = config.write()
|
err = config.write()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -361,8 +366,8 @@ func (web *Web) handleInstallConfigure(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
web.conf.firstRun = false
|
web.conf.firstRun = false
|
||||||
web.conf.BindHost = newSettings.Web.IP
|
web.conf.BindHost = req.Web.IP
|
||||||
web.conf.BindPort = newSettings.Web.Port
|
web.conf.BindPort = req.Web.Port
|
||||||
|
|
||||||
registerControlHandlers()
|
registerControlHandlers()
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||||
|
@ -254,7 +253,7 @@ func getDNSEncryption() (de dnsEncryption) {
|
||||||
if tlsConf.PortHTTPS != 0 {
|
if tlsConf.PortHTTPS != 0 {
|
||||||
addr := hostname
|
addr := hostname
|
||||||
if tlsConf.PortHTTPS != 443 {
|
if tlsConf.PortHTTPS != 443 {
|
||||||
addr = net.JoinHostPort(addr, strconv.Itoa(tlsConf.PortHTTPS))
|
addr = aghnet.JoinHostPort(addr, tlsConf.PortHTTPS)
|
||||||
}
|
}
|
||||||
|
|
||||||
de.https = (&url.URL{
|
de.https = (&url.URL{
|
||||||
|
@ -267,14 +266,14 @@ func getDNSEncryption() (de dnsEncryption) {
|
||||||
if tlsConf.PortDNSOverTLS != 0 {
|
if tlsConf.PortDNSOverTLS != 0 {
|
||||||
de.tls = (&url.URL{
|
de.tls = (&url.URL{
|
||||||
Scheme: "tls",
|
Scheme: "tls",
|
||||||
Host: net.JoinHostPort(hostname, strconv.Itoa(tlsConf.PortDNSOverTLS)),
|
Host: aghnet.JoinHostPort(hostname, tlsConf.PortDNSOverTLS),
|
||||||
}).String()
|
}).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if tlsConf.PortDNSOverQUIC != 0 {
|
if tlsConf.PortDNSOverQUIC != 0 {
|
||||||
de.quic = (&url.URL{
|
de.quic = (&url.URL{
|
||||||
Scheme: "quic",
|
Scheme: "quic",
|
||||||
Host: net.JoinHostPort(hostname, strconv.Itoa(int(tlsConf.PortDNSOverQUIC))),
|
Host: aghnet.JoinHostPort(hostname, tlsConf.PortDNSOverQUIC),
|
||||||
}).String()
|
}).String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
@ -630,7 +629,28 @@ func loadOptions() options {
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
// printHTTPAddresses prints the IP addresses which user can use to open the
|
// printWebAddrs prints addresses built from proto, addr, and an appropriate
|
||||||
|
// port. At least one address is printed with the value of port. If the value
|
||||||
|
// of betaPort is 0, the second address is not printed. The output example:
|
||||||
|
//
|
||||||
|
// Go to http://127.0.0.1:80
|
||||||
|
// Go to http://127.0.0.1:3000 (BETA)
|
||||||
|
//
|
||||||
|
func printWebAddrs(proto, addr string, port, betaPort int) {
|
||||||
|
const (
|
||||||
|
hostMsg = "Go to %s://%s"
|
||||||
|
hostBetaMsg = hostMsg + " (BETA)"
|
||||||
|
)
|
||||||
|
|
||||||
|
log.Printf(hostMsg, proto, aghnet.JoinHostPort(addr, port))
|
||||||
|
if betaPort == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf(hostBetaMsg, proto, aghnet.JoinHostPort(addr, config.BetaBindPort))
|
||||||
|
}
|
||||||
|
|
||||||
|
// printHTTPAddresses prints the IP addresses which user can use to access the
|
||||||
// admin interface. proto is either schemeHTTP or schemeHTTPS.
|
// admin interface. proto is either schemeHTTP or schemeHTTPS.
|
||||||
func printHTTPAddresses(proto string) {
|
func printHTTPAddresses(proto string) {
|
||||||
tlsConf := tlsConfigSettings{}
|
tlsConf := tlsConfigSettings{}
|
||||||
|
@ -638,45 +658,40 @@ func printHTTPAddresses(proto string) {
|
||||||
Context.tls.WriteDiskConfig(&tlsConf)
|
Context.tls.WriteDiskConfig(&tlsConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
port := strconv.Itoa(config.BindPort)
|
port := config.BindPort
|
||||||
if proto == schemeHTTPS {
|
if proto == schemeHTTPS {
|
||||||
port = strconv.Itoa(tlsConf.PortHTTPS)
|
port = tlsConf.PortHTTPS
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostStr string
|
// TODO(e.burkov): Inspect and perhaps merge with the previous
|
||||||
|
// condition.
|
||||||
if proto == schemeHTTPS && tlsConf.ServerName != "" {
|
if proto == schemeHTTPS && tlsConf.ServerName != "" {
|
||||||
if tlsConf.PortHTTPS == 443 {
|
printWebAddrs(proto, tlsConf.ServerName, tlsConf.PortHTTPS, 0)
|
||||||
log.Printf("Go to https://%s", tlsConf.ServerName)
|
|
||||||
} else {
|
|
||||||
log.Printf("Go to https://%s:%s", tlsConf.ServerName, port)
|
|
||||||
}
|
|
||||||
} else if config.BindHost.IsUnspecified() {
|
|
||||||
log.Println("AdGuard Home is available on the following addresses:")
|
|
||||||
ifaces, err := aghnet.GetValidNetInterfacesForWeb()
|
|
||||||
if err != nil {
|
|
||||||
// That's weird, but we'll ignore it
|
|
||||||
hostStr = config.BindHost.String()
|
|
||||||
log.Printf("Go to %s://%s", proto, net.JoinHostPort(hostStr, port))
|
|
||||||
if config.BetaBindPort != 0 {
|
|
||||||
log.Printf("Go to %s://%s (BETA)", proto, net.JoinHostPort(hostStr, strconv.Itoa(config.BetaBindPort)))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, iface := range ifaces {
|
return
|
||||||
for _, addr := range iface.Addresses {
|
}
|
||||||
hostStr = addr.String()
|
|
||||||
log.Printf("Go to %s://%s", proto, net.JoinHostPort(hostStr, strconv.Itoa(config.BindPort)))
|
bindhost := config.BindHost
|
||||||
if config.BetaBindPort != 0 {
|
if !bindhost.IsUnspecified() {
|
||||||
log.Printf("Go to %s://%s (BETA)", proto, net.JoinHostPort(hostStr, strconv.Itoa(config.BetaBindPort)))
|
printWebAddrs(proto, bindhost.String(), port, config.BetaBindPort)
|
||||||
}
|
|
||||||
}
|
return
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
hostStr = config.BindHost.String()
|
ifaces, err := aghnet.GetValidNetInterfacesForWeb()
|
||||||
log.Printf("Go to %s://%s", proto, net.JoinHostPort(hostStr, port))
|
if err != nil {
|
||||||
if config.BetaBindPort != 0 {
|
log.Error("web: getting iface ips: %s", err)
|
||||||
log.Printf("Go to %s://%s (BETA)", proto, net.JoinHostPort(hostStr, strconv.Itoa(config.BetaBindPort)))
|
// That's weird, but we'll ignore it.
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Find out when it happens.
|
||||||
|
printWebAddrs(proto, bindhost.String(), port, config.BetaBindPort)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, iface := range ifaces {
|
||||||
|
for _, addr := range iface.Addresses {
|
||||||
|
printWebAddrs(proto, addr.String(), config.BindPort, config.BetaBindPort)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||||
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
uuid "github.com/satori/go.uuid"
|
uuid "github.com/satori/go.uuid"
|
||||||
"howett.net/plist"
|
"howett.net/plist"
|
||||||
|
@ -53,27 +54,27 @@ const (
|
||||||
|
|
||||||
func getMobileConfig(d dnsSettings) ([]byte, error) {
|
func getMobileConfig(d dnsSettings) ([]byte, error) {
|
||||||
var dspName string
|
var dspName string
|
||||||
switch d.DNSProtocol {
|
switch proto := d.DNSProtocol; proto {
|
||||||
case dnsProtoHTTPS:
|
case dnsProtoHTTPS:
|
||||||
dspName = fmt.Sprintf("%s DoH", d.ServerName)
|
dspName = fmt.Sprintf("%s DoH", d.ServerName)
|
||||||
|
|
||||||
u := &url.URL{
|
u := &url.URL{
|
||||||
Scheme: schemeHTTPS,
|
Scheme: schemeHTTPS,
|
||||||
Host: d.ServerName,
|
Host: d.ServerName,
|
||||||
Path: "/dns-query",
|
Path: path.Join("/dns-query", d.clientID),
|
||||||
}
|
}
|
||||||
if d.clientID != "" {
|
|
||||||
u.Path = path.Join(u.Path, d.clientID)
|
|
||||||
}
|
|
||||||
|
|
||||||
d.ServerURL = u.String()
|
d.ServerURL = u.String()
|
||||||
|
// Empty the ServerName field since it is only must be presented
|
||||||
|
// in DNS-over-TLS configuration.
|
||||||
|
//
|
||||||
|
// See https://developer.apple.com/documentation/devicemanagement/dnssettings/dnssettings.
|
||||||
|
d.ServerName = ""
|
||||||
case dnsProtoTLS:
|
case dnsProtoTLS:
|
||||||
dspName = fmt.Sprintf("%s DoT", d.ServerName)
|
dspName = fmt.Sprintf("%s DoT", d.ServerName)
|
||||||
if d.clientID != "" {
|
if d.clientID != "" {
|
||||||
d.ServerName = d.clientID + "." + d.ServerName
|
d.ServerName = d.clientID + "." + d.ServerName
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("bad dns protocol %q", d.DNSProtocol)
|
return nil, fmt.Errorf("bad dns protocol %q", proto)
|
||||||
}
|
}
|
||||||
|
|
||||||
data := mobileConfig{
|
data := mobileConfig{
|
||||||
|
@ -99,25 +100,25 @@ func getMobileConfig(d dnsSettings) ([]byte, error) {
|
||||||
return plist.MarshalIndent(data, plist.XMLFormat, "\t")
|
return plist.MarshalIndent(data, plist.XMLFormat, "\t")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func respondJSONError(w http.ResponseWriter, status int, msg string) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
err := json.NewEncoder(w).Encode(&jsonError{
|
||||||
|
Message: msg,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("writing %d json response: %s", status, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const errEmptyHost errors.Error = "no host in query parameters and no server_name"
|
||||||
|
|
||||||
func handleMobileConfig(w http.ResponseWriter, r *http.Request, dnsp string) {
|
func handleMobileConfig(w http.ResponseWriter, r *http.Request, dnsp string) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
q := r.URL.Query()
|
q := r.URL.Query()
|
||||||
host := q.Get("host")
|
host := q.Get("host")
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = Context.tls.conf.ServerName
|
respondJSONError(w, http.StatusInternalServerError, string(errEmptyHost))
|
||||||
}
|
|
||||||
|
|
||||||
if host == "" {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
|
|
||||||
const msg = "no host in query parameters and no server_name"
|
|
||||||
err = json.NewEncoder(w).Encode(&jsonError{
|
|
||||||
Message: msg,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("writing 500 json response: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -126,14 +127,7 @@ func handleMobileConfig(w http.ResponseWriter, r *http.Request, dnsp string) {
|
||||||
if clientID != "" {
|
if clientID != "" {
|
||||||
err = dnsforward.ValidateClientID(clientID)
|
err = dnsforward.ValidateClientID(clientID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
respondJSONError(w, http.StatusBadRequest, err.Error())
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(&jsonError{
|
|
||||||
Message: err.Error(),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("writing 400 json response: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -147,7 +141,7 @@ func handleMobileConfig(w http.ResponseWriter, r *http.Request, dnsp string) {
|
||||||
|
|
||||||
mobileconfig, err := getMobileConfig(d)
|
mobileconfig, err := getMobileConfig(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, http.StatusInternalServerError, "plist.MarshalIndent: %s", err)
|
respondJSONError(w, http.StatusInternalServerError, err.Error())
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package home
|
package home
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -13,7 +15,7 @@ import (
|
||||||
func TestHandleMobileConfigDOH(t *testing.T) {
|
func TestHandleMobileConfigDOH(t *testing.T) {
|
||||||
t.Run("success", func(t *testing.T) {
|
t.Run("success", func(t *testing.T) {
|
||||||
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/doh.mobileconfig?host=example.org", nil)
|
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/doh.mobileconfig?host=example.org", nil)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
@ -22,39 +24,11 @@ func TestHandleMobileConfigDOH(t *testing.T) {
|
||||||
|
|
||||||
var mc mobileConfig
|
var mc mobileConfig
|
||||||
_, err = plist.Unmarshal(w.Body.Bytes(), &mc)
|
_, err = plist.Unmarshal(w.Body.Bytes(), &mc)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, mc.PayloadContent, 1)
|
require.Len(t, mc.PayloadContent, 1)
|
||||||
assert.Equal(t, "example.org DoH", mc.PayloadContent[0].Name)
|
assert.Equal(t, "example.org DoH", mc.PayloadContent[0].Name)
|
||||||
assert.Equal(t, "example.org DoH", mc.PayloadContent[0].PayloadDisplayName)
|
assert.Equal(t, "example.org DoH", mc.PayloadContent[0].PayloadDisplayName)
|
||||||
assert.Equal(t, "example.org", mc.PayloadContent[0].DNSSettings.ServerName)
|
|
||||||
assert.Equal(t, "https://example.org/dns-query", mc.PayloadContent[0].DNSSettings.ServerURL)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("success_no_host", func(t *testing.T) {
|
|
||||||
oldTLSConf := Context.tls
|
|
||||||
t.Cleanup(func() { Context.tls = oldTLSConf })
|
|
||||||
|
|
||||||
Context.tls = &TLSMod{
|
|
||||||
conf: tlsConfigSettings{ServerName: "example.org"},
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/doh.mobileconfig", nil)
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
handleMobileConfigDOH(w, r)
|
|
||||||
require.Equal(t, http.StatusOK, w.Code)
|
|
||||||
|
|
||||||
var mc mobileConfig
|
|
||||||
_, err = plist.Unmarshal(w.Body.Bytes(), &mc)
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
require.Len(t, mc.PayloadContent, 1)
|
|
||||||
assert.Equal(t, "example.org DoH", mc.PayloadContent[0].Name)
|
|
||||||
assert.Equal(t, "example.org DoH", mc.PayloadContent[0].PayloadDisplayName)
|
|
||||||
assert.Equal(t, "example.org", mc.PayloadContent[0].DNSSettings.ServerName)
|
|
||||||
assert.Equal(t, "https://example.org/dns-query", mc.PayloadContent[0].DNSSettings.ServerURL)
|
assert.Equal(t, "https://example.org/dns-query", mc.PayloadContent[0].DNSSettings.ServerURL)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -65,17 +39,24 @@ func TestHandleMobileConfigDOH(t *testing.T) {
|
||||||
Context.tls = &TLSMod{conf: tlsConfigSettings{}}
|
Context.tls = &TLSMod{conf: tlsConfigSettings{}}
|
||||||
|
|
||||||
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/doh.mobileconfig", nil)
|
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/doh.mobileconfig", nil)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err = json.NewEncoder(b).Encode(&jsonError{
|
||||||
|
Message: errEmptyHost.Error(),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
handleMobileConfigDOH(w, r)
|
handleMobileConfigDOH(w, r)
|
||||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||||
|
assert.JSONEq(t, w.Body.String(), b.String())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("client_id", func(t *testing.T) {
|
t.Run("client_id", func(t *testing.T) {
|
||||||
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/doh.mobileconfig?host=example.org&client_id=cli42", nil)
|
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/doh.mobileconfig?host=example.org&client_id=cli42", nil)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
@ -84,12 +65,11 @@ func TestHandleMobileConfigDOH(t *testing.T) {
|
||||||
|
|
||||||
var mc mobileConfig
|
var mc mobileConfig
|
||||||
_, err = plist.Unmarshal(w.Body.Bytes(), &mc)
|
_, err = plist.Unmarshal(w.Body.Bytes(), &mc)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, mc.PayloadContent, 1)
|
require.Len(t, mc.PayloadContent, 1)
|
||||||
assert.Equal(t, "example.org DoH", mc.PayloadContent[0].Name)
|
assert.Equal(t, "example.org DoH", mc.PayloadContent[0].Name)
|
||||||
assert.Equal(t, "example.org DoH", mc.PayloadContent[0].PayloadDisplayName)
|
assert.Equal(t, "example.org DoH", mc.PayloadContent[0].PayloadDisplayName)
|
||||||
assert.Equal(t, "example.org", mc.PayloadContent[0].DNSSettings.ServerName)
|
|
||||||
assert.Equal(t, "https://example.org/dns-query/cli42", mc.PayloadContent[0].DNSSettings.ServerURL)
|
assert.Equal(t, "https://example.org/dns-query/cli42", mc.PayloadContent[0].DNSSettings.ServerURL)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -97,7 +77,7 @@ func TestHandleMobileConfigDOH(t *testing.T) {
|
||||||
func TestHandleMobileConfigDOT(t *testing.T) {
|
func TestHandleMobileConfigDOT(t *testing.T) {
|
||||||
t.Run("success", func(t *testing.T) {
|
t.Run("success", func(t *testing.T) {
|
||||||
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/dot.mobileconfig?host=example.org", nil)
|
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/dot.mobileconfig?host=example.org", nil)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
@ -106,33 +86,7 @@ func TestHandleMobileConfigDOT(t *testing.T) {
|
||||||
|
|
||||||
var mc mobileConfig
|
var mc mobileConfig
|
||||||
_, err = plist.Unmarshal(w.Body.Bytes(), &mc)
|
_, err = plist.Unmarshal(w.Body.Bytes(), &mc)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, mc.PayloadContent, 1)
|
|
||||||
assert.Equal(t, "example.org DoT", mc.PayloadContent[0].Name)
|
|
||||||
assert.Equal(t, "example.org DoT", mc.PayloadContent[0].PayloadDisplayName)
|
|
||||||
assert.Equal(t, "example.org", mc.PayloadContent[0].DNSSettings.ServerName)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("success_no_host", func(t *testing.T) {
|
|
||||||
oldTLSConf := Context.tls
|
|
||||||
t.Cleanup(func() { Context.tls = oldTLSConf })
|
|
||||||
|
|
||||||
Context.tls = &TLSMod{
|
|
||||||
conf: tlsConfigSettings{ServerName: "example.org"},
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/dot.mobileconfig", nil)
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
|
||||||
|
|
||||||
handleMobileConfigDOT(w, r)
|
|
||||||
require.Equal(t, http.StatusOK, w.Code)
|
|
||||||
|
|
||||||
var mc mobileConfig
|
|
||||||
_, err = plist.Unmarshal(w.Body.Bytes(), &mc)
|
|
||||||
require.Nil(t, err)
|
|
||||||
|
|
||||||
require.Len(t, mc.PayloadContent, 1)
|
require.Len(t, mc.PayloadContent, 1)
|
||||||
assert.Equal(t, "example.org DoT", mc.PayloadContent[0].Name)
|
assert.Equal(t, "example.org DoT", mc.PayloadContent[0].Name)
|
||||||
|
@ -147,17 +101,25 @@ func TestHandleMobileConfigDOT(t *testing.T) {
|
||||||
Context.tls = &TLSMod{conf: tlsConfigSettings{}}
|
Context.tls = &TLSMod{conf: tlsConfigSettings{}}
|
||||||
|
|
||||||
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/dot.mobileconfig", nil)
|
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/dot.mobileconfig", nil)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
b := &bytes.Buffer{}
|
||||||
|
err = json.NewEncoder(b).Encode(&jsonError{
|
||||||
|
Message: errEmptyHost.Error(),
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
handleMobileConfigDOT(w, r)
|
handleMobileConfigDOT(w, r)
|
||||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||||
|
|
||||||
|
assert.JSONEq(t, w.Body.String(), b.String())
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("client_id", func(t *testing.T) {
|
t.Run("client_id", func(t *testing.T) {
|
||||||
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/dot.mobileconfig?host=example.org&client_id=cli42", nil)
|
r, err := http.NewRequest(http.MethodGet, "https://example.com:12345/apple/dot.mobileconfig?host=example.org&client_id=cli42", nil)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
@ -166,7 +128,7 @@ func TestHandleMobileConfigDOT(t *testing.T) {
|
||||||
|
|
||||||
var mc mobileConfig
|
var mc mobileConfig
|
||||||
_, err = plist.Unmarshal(w.Body.Bytes(), &mc)
|
_, err = plist.Unmarshal(w.Body.Bytes(), &mc)
|
||||||
require.Nil(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Len(t, mc.PayloadContent, 1)
|
require.Len(t, mc.PayloadContent, 1)
|
||||||
assert.Equal(t, "example.org DoT", mc.PayloadContent[0].Name)
|
assert.Equal(t, "example.org DoT", mc.PayloadContent[0].Name)
|
||||||
|
|
|
@ -243,7 +243,8 @@ func handleServiceInstallCommand(s service.Service) {
|
||||||
log.Printf(`Almost ready!
|
log.Printf(`Almost ready!
|
||||||
AdGuard Home is successfully installed and will automatically start on boot.
|
AdGuard Home is successfully installed and will automatically start on boot.
|
||||||
There are a few more things that must be configured before you can use it.
|
There are a few more things that must be configured before you can use it.
|
||||||
Click on the link below and follow the Installation Wizard steps to finish setup.`)
|
Click on the link below and follow the Installation Wizard steps to finish setup.
|
||||||
|
AdGuard Home is now available at the following addresses:`)
|
||||||
printHTTPAddresses(schemeHTTP)
|
printHTTPAddresses(schemeHTTP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -162,6 +161,8 @@ func (web *Web) TLSConfigChanged(ctx context.Context, tlsConf tlsConfigSettings)
|
||||||
|
|
||||||
// Start - start serving HTTP requests
|
// Start - start serving HTTP requests
|
||||||
func (web *Web) Start() {
|
func (web *Web) Start() {
|
||||||
|
log.Println("AdGuard Home is available at the following addresses:")
|
||||||
|
|
||||||
// for https, we have a separate goroutine loop
|
// for https, we have a separate goroutine loop
|
||||||
go web.tlsServerLoop()
|
go web.tlsServerLoop()
|
||||||
|
|
||||||
|
@ -174,7 +175,7 @@ func (web *Web) Start() {
|
||||||
// we need to have new instance, because after Shutdown() the Server is not usable
|
// we need to have new instance, because after Shutdown() the Server is not usable
|
||||||
web.httpServer = &http.Server{
|
web.httpServer = &http.Server{
|
||||||
ErrorLog: log.StdLog("web: plain", log.DEBUG),
|
ErrorLog: log.StdLog("web: plain", log.DEBUG),
|
||||||
Addr: net.JoinHostPort(hostStr, strconv.Itoa(web.conf.BindPort)),
|
Addr: aghnet.JoinHostPort(hostStr, web.conf.BindPort),
|
||||||
Handler: withMiddlewares(Context.mux, limitRequestBody),
|
Handler: withMiddlewares(Context.mux, limitRequestBody),
|
||||||
ReadTimeout: web.conf.ReadTimeout,
|
ReadTimeout: web.conf.ReadTimeout,
|
||||||
ReadHeaderTimeout: web.conf.ReadHeaderTimeout,
|
ReadHeaderTimeout: web.conf.ReadHeaderTimeout,
|
||||||
|
@ -187,7 +188,7 @@ func (web *Web) Start() {
|
||||||
if web.conf.BetaBindPort != 0 {
|
if web.conf.BetaBindPort != 0 {
|
||||||
web.httpServerBeta = &http.Server{
|
web.httpServerBeta = &http.Server{
|
||||||
ErrorLog: log.StdLog("web: plain", log.DEBUG),
|
ErrorLog: log.StdLog("web: plain", log.DEBUG),
|
||||||
Addr: net.JoinHostPort(hostStr, strconv.Itoa(web.conf.BetaBindPort)),
|
Addr: aghnet.JoinHostPort(hostStr, web.conf.BetaBindPort),
|
||||||
Handler: withMiddlewares(Context.mux, limitRequestBody, web.wrapIndexBeta),
|
Handler: withMiddlewares(Context.mux, limitRequestBody, web.wrapIndexBeta),
|
||||||
ReadTimeout: web.conf.ReadTimeout,
|
ReadTimeout: web.conf.ReadTimeout,
|
||||||
ReadHeaderTimeout: web.conf.ReadHeaderTimeout,
|
ReadHeaderTimeout: web.conf.ReadHeaderTimeout,
|
||||||
|
@ -248,7 +249,7 @@ func (web *Web) tlsServerLoop() {
|
||||||
web.httpsServer.cond.L.Unlock()
|
web.httpsServer.cond.L.Unlock()
|
||||||
|
|
||||||
// prepare HTTPS server
|
// prepare HTTPS server
|
||||||
address := net.JoinHostPort(web.conf.BindHost.String(), strconv.Itoa(web.conf.PortHTTPS))
|
address := aghnet.JoinHostPort(web.conf.BindHost.String(), web.conf.PortHTTPS)
|
||||||
web.httpsServer.server = &http.Server{
|
web.httpsServer.server = &http.Server{
|
||||||
ErrorLog: log.StdLog("web: https", log.DEBUG),
|
ErrorLog: log.StdLog("web: https", log.DEBUG),
|
||||||
Addr: address,
|
Addr: address,
|
||||||
|
|
|
@ -4,6 +4,12 @@
|
||||||
|
|
||||||
## v0.107: API changes
|
## v0.107: API changes
|
||||||
|
|
||||||
|
### The parameter `"host"` in `GET /apple/*.mobileconfig` is now required.
|
||||||
|
|
||||||
|
* The parameter `"host"` in `GET` requests for `/apple/doh.mobileconfig` and
|
||||||
|
`/apple/doh.mobileconfig` is now required to prevent unexpected server name's
|
||||||
|
value.
|
||||||
|
|
||||||
### The new field `"default_local_ptr_upstreams"` in `GET /control/dns_info`
|
### The new field `"default_local_ptr_upstreams"` in `GET /control/dns_info`
|
||||||
|
|
||||||
* The new optional field `"default_local_ptr_upstreams"` is the list of IP
|
* The new optional field `"default_local_ptr_upstreams"` is the list of IP
|
||||||
|
|
|
@ -1129,6 +1129,7 @@
|
||||||
'example': 'example.org'
|
'example': 'example.org'
|
||||||
'in': 'query'
|
'in': 'query'
|
||||||
'name': 'host'
|
'name': 'host'
|
||||||
|
'required': true
|
||||||
'schema':
|
'schema':
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
- 'description': >
|
- 'description': >
|
||||||
|
@ -1163,6 +1164,7 @@
|
||||||
'example': 'example.org'
|
'example': 'example.org'
|
||||||
'in': 'query'
|
'in': 'query'
|
||||||
'name': 'host'
|
'name': 'host'
|
||||||
|
'required': true
|
||||||
'schema':
|
'schema':
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
- 'description': >
|
- 'description': >
|
||||||
|
|
Loading…
Reference in New Issue