2018-08-30 14:25:33 +00:00
package main
import (
2019-02-10 17:47:43 +00:00
"bytes"
2019-02-06 13:47:17 +00:00
"context"
2018-08-30 14:25:33 +00:00
"encoding/json"
"fmt"
"io/ioutil"
2018-12-05 16:17:17 +00:00
"net"
2018-08-30 14:25:33 +00:00
"net/http"
"os"
2019-02-10 17:47:43 +00:00
"sort"
2018-08-30 14:25:33 +00:00
"strconv"
"strings"
2019-03-05 15:04:49 +00:00
"sync"
2018-08-30 14:25:33 +00:00
"time"
2018-12-05 11:21:25 +00:00
"github.com/AdguardTeam/AdGuardHome/dnsforward"
2018-12-29 14:23:42 +00:00
"github.com/AdguardTeam/dnsproxy/upstream"
2019-02-27 15:28:09 +00:00
"github.com/AdguardTeam/golibs/log"
2019-03-20 11:24:33 +00:00
"github.com/AdguardTeam/golibs/utils"
2019-04-15 13:21:12 +00:00
"github.com/NYTimes/gziphandler"
2018-12-05 16:17:17 +00:00
"github.com/miekg/dns"
2019-01-25 13:01:27 +00:00
govalidator "gopkg.in/asaskevich/govalidator.v4"
2018-08-30 14:25:33 +00:00
)
const updatePeriod = time . Minute * 30
2018-09-20 17:02:25 +00:00
// cached version.json to avoid hammering github.io for each page reload
var versionCheckJSON [ ] byte
var versionCheckLastTime time . Time
2019-03-07 13:32:52 +00:00
var protocols = [ ] string { "tls://" , "https://" , "tcp://" , "sdns://" }
2018-10-15 13:02:19 +00:00
const versionCheckURL = "https://adguardteam.github.io/AdGuardHome/version.json"
2018-09-20 17:02:25 +00:00
const versionCheckPeriod = time . Hour * 8
2019-04-17 11:53:52 +00:00
var transport = & http . Transport {
DialContext : customDialContext ,
}
2018-10-10 12:47:08 +00:00
var client = & http . Client {
2019-04-17 11:53:52 +00:00
Timeout : time . Minute * 5 ,
Transport : transport ,
2018-10-10 12:47:08 +00:00
}
2019-03-05 15:04:49 +00:00
var controlLock sync . Mutex
2019-02-10 17:47:43 +00:00
// ----------------
// helper functions
// ----------------
func returnOK ( w http . ResponseWriter ) {
_ , err := fmt . Fprintf ( w , "OK\n" )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Couldn't write body: %s" , err )
2019-02-10 17:47:43 +00:00
}
}
func httpError ( w http . ResponseWriter , code int , format string , args ... interface { } ) {
text := fmt . Sprintf ( format , args ... )
2019-02-25 13:44:22 +00:00
log . Info ( text )
2019-02-10 17:47:43 +00:00
http . Error ( w , text , code )
}
// ---------------
2018-12-05 17:29:00 +00:00
// dns run control
2019-02-10 17:47:43 +00:00
// ---------------
2018-12-05 17:29:00 +00:00
func writeAllConfigsAndReloadDNS ( ) error {
2018-08-30 14:25:33 +00:00
err := writeAllConfigs ( )
if err != nil {
2019-02-25 13:44:22 +00:00
log . Error ( "Couldn't write all configs: %s" , err )
2018-08-30 14:25:33 +00:00
return err
}
2019-01-24 17:11:01 +00:00
return reconfigureDNSServer ( )
2018-08-30 14:25:33 +00:00
}
2018-10-10 17:13:03 +00:00
func httpUpdateConfigReloadDNSReturnOK ( w http . ResponseWriter , r * http . Request ) {
2018-12-05 17:29:00 +00:00
err := writeAllConfigsAndReloadDNS ( )
2018-10-10 17:13:03 +00:00
if err != nil {
2019-02-15 14:06:55 +00:00
httpError ( w , http . StatusInternalServerError , "Couldn't write config file: %s" , err )
2018-10-10 17:13:03 +00:00
return
}
2019-01-24 17:11:01 +00:00
returnOK ( w )
2018-10-10 17:13:03 +00:00
}
2018-08-30 14:25:33 +00:00
func handleStatus ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-03-19 11:14:58 +00:00
dnsAddresses := [ ] string { }
if config . DNS . BindHost == "0.0.0.0" {
ifaces , e := getValidNetInterfacesForWeb ( )
if e != nil {
log . Error ( "Couldn't get network interfaces: %v" , e )
}
for _ , iface := range ifaces {
for _ , addr := range iface . Addresses {
dnsAddresses = append ( dnsAddresses , addr )
}
}
}
if len ( dnsAddresses ) == 0 {
dnsAddresses = append ( dnsAddresses , config . DNS . BindHost )
}
2018-08-30 14:25:33 +00:00
data := map [ string ] interface { } {
2019-03-19 11:14:58 +00:00
"dns_addresses" : dnsAddresses ,
2019-02-20 09:24:56 +00:00
"http_port" : config . BindPort ,
2018-12-05 17:29:00 +00:00
"dns_port" : config . DNS . Port ,
"protection_enabled" : config . DNS . ProtectionEnabled ,
"querylog_enabled" : config . DNS . QueryLogEnabled ,
2018-10-10 17:13:03 +00:00
"running" : isRunning ( ) ,
2018-12-05 17:29:00 +00:00
"bootstrap_dns" : config . DNS . BootstrapDNS ,
"upstream_dns" : config . DNS . UpstreamDNS ,
2019-02-26 15:19:05 +00:00
"all_servers" : config . DNS . AllServers ,
2018-10-10 17:13:03 +00:00
"version" : VersionString ,
2018-11-21 17:42:55 +00:00
"language" : config . Language ,
2018-08-30 14:25:33 +00:00
}
2018-10-20 16:58:39 +00:00
jsonVal , err := json . Marshal ( data )
2018-08-30 14:25:33 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to marshal status json: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2018-10-20 16:58:39 +00:00
_ , err = w . Write ( jsonVal )
2018-08-30 14:25:33 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to write response json: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
}
2018-10-10 17:13:03 +00:00
func handleProtectionEnable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-12-05 17:29:00 +00:00
config . DNS . ProtectionEnabled = true
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
}
func handleProtectionDisable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-12-05 17:29:00 +00:00
config . DNS . ProtectionEnabled = false
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
}
2018-08-30 14:25:33 +00:00
// -----
// stats
// -----
func handleQueryLogEnable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-12-05 17:29:00 +00:00
config . DNS . QueryLogEnabled = true
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleQueryLogDisable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-12-05 17:29:00 +00:00
config . DNS . QueryLogEnabled = false
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
2019-02-10 17:47:43 +00:00
func handleQueryLog ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-02-10 17:47:43 +00:00
data := dnsServer . GetQueryLog ( )
jsonVal , err := json . Marshal ( data )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Couldn't marshal data into json: %s" , err )
2019-02-10 17:47:43 +00:00
return
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
_ , err = w . Write ( jsonVal )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to write response json: %s" , err )
2019-02-10 17:47:43 +00:00
}
}
func handleStatsTop ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-02-10 17:47:43 +00:00
s := dnsServer . GetStatsTop ( )
// use manual json marshalling because we want maps to be sorted by value
statsJSON := bytes . Buffer { }
statsJSON . WriteString ( "{\n" )
gen := func ( json * bytes . Buffer , name string , top map [ string ] int , addComma bool ) {
json . WriteString ( " " )
json . WriteString ( fmt . Sprintf ( "%q" , name ) )
json . WriteString ( ": {\n" )
sorted := sortByValue ( top )
// no more than 50 entries
if len ( sorted ) > 50 {
sorted = sorted [ : 50 ]
}
for i , key := range sorted {
json . WriteString ( " " )
json . WriteString ( fmt . Sprintf ( "%q" , key ) )
json . WriteString ( ": " )
json . WriteString ( strconv . Itoa ( top [ key ] ) )
if i + 1 != len ( sorted ) {
json . WriteByte ( ',' )
}
json . WriteByte ( '\n' )
}
json . WriteString ( " }" )
if addComma {
json . WriteByte ( ',' )
}
json . WriteByte ( '\n' )
}
gen ( & statsJSON , "top_queried_domains" , s . Domains , true )
gen ( & statsJSON , "top_blocked_domains" , s . Blocked , true )
gen ( & statsJSON , "top_clients" , s . Clients , true )
statsJSON . WriteString ( " \"stats_period\": \"24 hours\"\n" )
statsJSON . WriteString ( "}\n" )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
_ , err := w . Write ( statsJSON . Bytes ( ) )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Couldn't write body: %s" , err )
2019-02-10 17:47:43 +00:00
}
}
// handleStatsReset resets the stats caches
func handleStatsReset ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-02-11 11:22:36 +00:00
dnsServer . PurgeStats ( )
2019-02-10 17:47:43 +00:00
_ , err := fmt . Fprintf ( w , "OK\n" )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Couldn't write body: %s" , err )
2019-02-10 17:47:43 +00:00
}
}
// handleStats returns aggregated stats data for the 24 hours
func handleStats ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-02-10 17:47:43 +00:00
summed := dnsServer . GetAggregatedStats ( )
statsJSON , err := json . Marshal ( summed )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to marshal status json: %s" , err )
2019-02-10 17:47:43 +00:00
return
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
_ , err = w . Write ( statsJSON )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to write response json: %s" , err )
2019-02-10 17:47:43 +00:00
return
}
2018-09-26 14:47:23 +00:00
}
2019-02-10 17:47:43 +00:00
// HandleStatsHistory returns historical stats data for the 24 hours
func handleStatsHistory ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-02-10 17:47:43 +00:00
// handle time unit and prepare our time window size
timeUnitString := r . URL . Query ( ) . Get ( "time_unit" )
var timeUnit time . Duration
switch timeUnitString {
case "seconds" :
timeUnit = time . Second
case "minutes" :
timeUnit = time . Minute
case "hours" :
timeUnit = time . Hour
case "days" :
timeUnit = time . Hour * 24
default :
http . Error ( w , "Must specify valid time_unit parameter" , http . StatusBadRequest )
return
}
// parse start and end time
startTime , err := time . Parse ( time . RFC3339 , r . URL . Query ( ) . Get ( "start_time" ) )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "Must specify valid start_time parameter: %s" , err )
2019-02-10 17:47:43 +00:00
return
}
endTime , err := time . Parse ( time . RFC3339 , r . URL . Query ( ) . Get ( "end_time" ) )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "Must specify valid end_time parameter: %s" , err )
2019-02-10 17:47:43 +00:00
return
}
data , err := dnsServer . GetStatsHistory ( timeUnit , startTime , endTime )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "Cannot get stats history: %s" , err )
2019-02-10 17:47:43 +00:00
return
}
statsJSON , err := json . Marshal ( data )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to marshal status json: %s" , err )
2019-02-10 17:47:43 +00:00
return
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
_ , err = w . Write ( statsJSON )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to write response json: %s" , err )
2019-02-10 17:47:43 +00:00
return
}
}
// sortByValue is a helper function for querylog API
func sortByValue ( m map [ string ] int ) [ ] string {
type kv struct {
k string
v int
}
var ss [ ] kv
for k , v := range m {
ss = append ( ss , kv { k , v } )
}
sort . Slice ( ss , func ( l , r int ) bool {
return ss [ l ] . v > ss [ r ] . v
} )
sorted := [ ] string { }
for _ , v := range ss {
sorted = append ( sorted , v . k )
}
return sorted
}
// -----------------------
// upstreams configuration
// -----------------------
2019-02-28 10:01:41 +00:00
// TODO this struct will become unnecessary after config file rework
type upstreamConfig struct {
2019-03-06 12:35:22 +00:00
Upstreams [ ] string ` json:"upstream_dns" ` // Upstreams
BootstrapDNS [ ] string ` json:"bootstrap_dns" ` // Bootstrap DNS
AllServers bool ` json:"all_servers" ` // --all-servers param for dnsproxy
2019-02-28 08:10:43 +00:00
}
2019-02-28 10:01:41 +00:00
func handleSetUpstreamConfig ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:49:53 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-02-28 10:01:41 +00:00
newconfig := upstreamConfig { }
err := json . NewDecoder ( r . Body ) . Decode ( & newconfig )
2018-08-30 14:25:33 +00:00
if err != nil {
2019-02-28 10:01:41 +00:00
httpError ( w , http . StatusBadRequest , "Failed to parse new upstreams config json: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
2018-11-05 21:47:59 +00:00
2019-03-20 11:24:33 +00:00
err = validateUpstreams ( newconfig . Upstreams )
if err != nil {
httpError ( w , http . StatusBadRequest , "wrong upstreams specification: %s" , err )
return
2019-03-06 15:24:14 +00:00
}
2019-02-28 12:06:30 +00:00
config . DNS . UpstreamDNS = defaultDNS
2019-03-06 12:35:22 +00:00
if len ( newconfig . Upstreams ) > 0 {
config . DNS . UpstreamDNS = newconfig . Upstreams
2019-02-28 12:06:30 +00:00
}
2019-02-28 10:01:41 +00:00
2019-03-06 15:24:14 +00:00
// bootstrap servers are plain DNS only.
2019-03-06 12:35:22 +00:00
for _ , host := range newconfig . BootstrapDNS {
2019-03-06 15:24:14 +00:00
if err := checkPlainDNS ( host ) ; err != nil {
2019-03-06 13:17:15 +00:00
httpError ( w , http . StatusBadRequest , "%s can not be used as bootstrap dns cause: %s" , host , err )
return
2019-02-27 09:58:42 +00:00
}
}
2019-02-28 12:06:30 +00:00
config . DNS . BootstrapDNS = defaultBootstrap
2019-03-06 13:17:15 +00:00
if len ( newconfig . BootstrapDNS ) > 0 {
config . DNS . BootstrapDNS = newconfig . BootstrapDNS
2019-02-27 09:58:42 +00:00
}
2019-03-06 12:35:22 +00:00
config . DNS . AllServers = newconfig . AllServers
2019-02-28 12:06:30 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2019-02-26 15:19:05 +00:00
}
2019-03-20 11:24:33 +00:00
// validateUpstreams validates each upstream and returns an error if any upstream is invalid or if there are no default upstreams specified
func validateUpstreams ( upstreams [ ] string ) error {
var defaultUpstreamFound bool
for _ , u := range upstreams {
d , err := validateUpstream ( u )
if err != nil {
return err
}
// Check this flag until default upstream will not be found
if ! defaultUpstreamFound {
defaultUpstreamFound = d
}
}
// Return error if there are no default upstreams
if ! defaultUpstreamFound {
return fmt . Errorf ( "no default upstreams specified" )
}
return nil
}
2019-03-20 12:19:34 +00:00
func validateUpstream ( u string ) ( bool , error ) {
2019-03-20 11:24:33 +00:00
// Check if user tries to specify upstream for domain
2019-03-20 12:19:34 +00:00
u , defaultUpstream , err := separateUpstream ( u )
2019-03-20 11:24:33 +00:00
if err != nil {
2019-03-20 12:19:34 +00:00
return defaultUpstream , err
2019-03-20 11:24:33 +00:00
}
// The special server address '#' means "use the default servers"
if u == "#" && ! defaultUpstream {
2019-03-20 12:19:34 +00:00
return defaultUpstream , nil
2019-03-20 11:24:33 +00:00
}
// Check if the upstream has a valid protocol prefix
2019-03-07 13:32:52 +00:00
for _ , proto := range protocols {
2019-03-20 11:24:33 +00:00
if strings . HasPrefix ( u , proto ) {
2019-03-20 12:19:34 +00:00
return defaultUpstream , nil
2019-03-07 13:32:52 +00:00
}
2019-03-06 15:24:14 +00:00
}
2019-03-20 11:24:33 +00:00
// Return error if the upstream contains '://' without any valid protocol
if strings . Contains ( u , "://" ) {
return defaultUpstream , fmt . Errorf ( "wrong protocol" )
2019-03-06 15:24:14 +00:00
}
2019-03-20 11:24:33 +00:00
// Check if upstream is valid plain DNS
return defaultUpstream , checkPlainDNS ( u )
}
// separateUpstream returns upstream without specified domains and a bool flag that indicates if no domains were specified
// error will be returned if upstream per domain specification is invalid
func separateUpstream ( upstream string ) ( string , bool , error ) {
defaultUpstream := true
if strings . HasPrefix ( upstream , "[/" ) {
defaultUpstream = false
// split domains and upstream string
domainsAndUpstream := strings . Split ( strings . TrimPrefix ( upstream , "[/" ) , "/]" )
if len ( domainsAndUpstream ) != 2 {
return "" , defaultUpstream , fmt . Errorf ( "wrong DNS upstream per domain specification: %s" , upstream )
}
// split domains list and validate each one
for _ , host := range strings . Split ( domainsAndUpstream [ 0 ] , "/" ) {
if host != "" {
if err := utils . IsValidHostname ( host ) ; err != nil {
return "" , defaultUpstream , err
}
}
}
upstream = domainsAndUpstream [ 1 ]
}
return upstream , defaultUpstream , nil
2019-03-06 15:24:14 +00:00
}
2019-03-06 15:06:26 +00:00
// checkPlainDNS checks if host is plain DNS
2019-03-06 15:24:14 +00:00
func checkPlainDNS ( upstream string ) error {
2019-02-27 09:58:42 +00:00
// Check if host is ip without port
2019-03-06 15:24:14 +00:00
if net . ParseIP ( upstream ) != nil {
2019-02-27 09:58:42 +00:00
return nil
}
// Check if host is ip with port
2019-03-06 15:24:14 +00:00
ip , port , err := net . SplitHostPort ( upstream )
2019-03-06 15:06:26 +00:00
if err != nil {
return err
}
if net . ParseIP ( ip ) == nil {
2019-03-06 15:24:14 +00:00
return fmt . Errorf ( "%s is not a valid IP" , ip )
2019-03-06 15:06:26 +00:00
}
_ , err = strconv . ParseInt ( port , 0 , 64 )
if err != nil {
2019-03-06 15:24:14 +00:00
return fmt . Errorf ( "%s is not a valid port: %s" , port , err )
2019-03-06 15:06:26 +00:00
}
return nil
2019-02-27 09:58:42 +00:00
}
2018-09-19 16:12:09 +00:00
func handleTestUpstreamDNS ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-03-06 13:17:15 +00:00
upstreamConfig := upstreamConfig { }
err := json . NewDecoder ( r . Body ) . Decode ( & upstreamConfig )
2018-09-19 16:12:09 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "Failed to read request body: %s" , err )
2018-09-19 16:12:09 +00:00
return
}
2019-03-06 13:17:15 +00:00
if len ( upstreamConfig . Upstreams ) == 0 {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "No servers specified" )
2018-09-19 16:12:09 +00:00
return
}
result := map [ string ] string { }
2019-03-06 13:17:15 +00:00
for _ , host := range upstreamConfig . Upstreams {
err = checkDNS ( host , upstreamConfig . BootstrapDNS )
2018-09-19 16:12:09 +00:00
if err != nil {
2019-02-25 13:44:22 +00:00
log . Info ( "%v" , err )
2018-09-19 16:12:09 +00:00
result [ host ] = err . Error ( )
} else {
result [ host ] = "OK"
}
}
2018-10-20 16:58:39 +00:00
jsonVal , err := json . Marshal ( result )
2018-09-19 16:12:09 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to marshal status json: %s" , err )
2018-09-19 16:12:09 +00:00
return
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2018-10-20 16:58:39 +00:00
_ , err = w . Write ( jsonVal )
2018-09-19 16:12:09 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Couldn't write body: %s" , err )
2018-09-19 16:12:09 +00:00
}
}
2019-03-06 13:17:15 +00:00
func checkDNS ( input string , bootstrap [ ] string ) error {
2019-03-20 11:24:33 +00:00
// separate upstream from domains list
input , defaultUpstream , err := separateUpstream ( input )
if err != nil {
return fmt . Errorf ( "wrong upstream format: %s" , err )
}
// No need to check this entrance
if input == "#" && ! defaultUpstream {
return nil
}
if _ , err := validateUpstream ( input ) ; err != nil {
2019-03-06 15:36:31 +00:00
return fmt . Errorf ( "wrong upstream format: %s" , err )
}
2019-03-06 13:17:15 +00:00
if len ( bootstrap ) == 0 {
bootstrap = defaultBootstrap
}
2019-02-25 13:44:22 +00:00
log . Debug ( "Checking if DNS %s works..." , input )
2019-03-06 13:17:15 +00:00
u , err := upstream . AddressToUpstream ( input , upstream . Options { Bootstrap : bootstrap , Timeout : dnsforward . DefaultTimeout } )
2018-09-26 14:47:23 +00:00
if err != nil {
2019-01-24 17:11:01 +00:00
return fmt . Errorf ( "failed to choose upstream for %s: %s" , input , err )
2018-09-19 16:12:09 +00:00
}
2018-09-26 14:47:23 +00:00
2018-12-05 16:17:17 +00:00
req := dns . Msg { }
req . Id = dns . Id ( )
req . RecursionDesired = true
req . Question = [ ] dns . Question {
{ Name : "google-public-dns-a.google.com." , Qtype : dns . TypeA , Qclass : dns . ClassINET } ,
}
reply , err := u . Exchange ( & req )
2018-09-19 16:12:09 +00:00
if err != nil {
2018-10-30 09:24:59 +00:00
return fmt . Errorf ( "couldn't communicate with DNS server %s: %s" , input , err )
2018-09-19 16:12:09 +00:00
}
2018-12-05 16:17:17 +00:00
if len ( reply . Answer ) != 1 {
return fmt . Errorf ( "DNS server %s returned wrong answer" , input )
}
if t , ok := reply . Answer [ 0 ] . ( * dns . A ) ; ok {
if ! net . IPv4 ( 8 , 8 , 8 , 8 ) . Equal ( t . A ) {
return fmt . Errorf ( "DNS server %s returned wrong answer: %v" , input , t . A )
}
2018-09-26 14:47:23 +00:00
}
2019-02-25 13:44:22 +00:00
log . Debug ( "DNS %s works OK" , input )
2018-11-05 21:47:59 +00:00
return nil
2018-08-30 14:25:33 +00:00
}
2018-09-20 17:02:25 +00:00
func handleGetVersionJSON ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-09-20 17:02:25 +00:00
now := time . Now ( )
if now . Sub ( versionCheckLastTime ) <= versionCheckPeriod && len ( versionCheckJSON ) != 0 {
// return cached copy
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . Write ( versionCheckJSON )
return
}
resp , err := client . Get ( versionCheckURL )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadGateway , "Couldn't get version check json from %s: %T %s\n" , versionCheckURL , err , err )
2018-09-20 17:02:25 +00:00
return
}
if resp != nil && resp . Body != nil {
defer resp . Body . Close ( )
}
// read the body entirely
body , err := ioutil . ReadAll ( resp . Body )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadGateway , "Couldn't read response body from %s: %s" , versionCheckURL , err )
2018-09-20 17:02:25 +00:00
return
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
_ , err = w . Write ( body )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Couldn't write body: %s" , err )
2018-09-20 17:02:25 +00:00
}
versionCheckLastTime = now
versionCheckJSON = body
}
2018-08-30 14:25:33 +00:00
// ---------
// filtering
// ---------
func handleFilteringEnable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-12-05 17:29:00 +00:00
config . DNS . FilteringEnabled = true
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleFilteringDisable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-12-05 17:29:00 +00:00
config . DNS . FilteringEnabled = false
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleFilteringStatus ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-08-30 14:25:33 +00:00
data := map [ string ] interface { } {
2018-12-05 17:29:00 +00:00
"enabled" : config . DNS . FilteringEnabled ,
2018-08-30 14:25:33 +00:00
}
2018-10-06 21:58:59 +00:00
config . RLock ( )
2018-08-30 14:25:33 +00:00
data [ "filters" ] = config . Filters
data [ "user_rules" ] = config . UserRules
2018-10-20 16:58:39 +00:00
jsonVal , err := json . Marshal ( data )
2018-10-06 21:58:59 +00:00
config . RUnlock ( )
2018-08-30 14:25:33 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to marshal status json: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2018-10-20 16:58:39 +00:00
_ , err = w . Write ( jsonVal )
2018-08-30 14:25:33 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to write response json: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
}
func handleFilteringAddURL ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-01-25 13:01:27 +00:00
f := filter { }
err := json . NewDecoder ( r . Body ) . Decode ( & f )
2018-08-30 14:25:33 +00:00
if err != nil {
2018-10-11 15:32:23 +00:00
httpError ( w , http . StatusBadRequest , "Failed to parse request body json: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
2019-01-25 13:01:27 +00:00
if len ( f . URL ) == 0 {
2019-02-27 14:28:10 +00:00
http . Error ( w , "URL parameter was not specified" , http . StatusBadRequest )
2018-08-30 14:25:33 +00:00
return
}
2019-01-25 13:01:27 +00:00
if valid := govalidator . IsRequestURL ( f . URL ) ; ! valid {
2019-02-27 14:28:10 +00:00
http . Error ( w , "URL parameter is not valid request URL" , http . StatusBadRequest )
2018-08-30 14:25:33 +00:00
return
}
2018-09-14 01:33:54 +00:00
2018-10-30 09:24:59 +00:00
// Check for duplicates
2019-03-18 11:41:38 +00:00
if filterExists ( f . URL ) {
httpError ( w , http . StatusBadRequest , "Filter URL already added -- %s" , f . URL )
return
2018-09-14 01:33:54 +00:00
}
2018-10-30 09:24:59 +00:00
// Set necessary properties
2019-01-25 13:01:27 +00:00
f . ID = assignUniqueFilterID ( )
f . Enabled = true
2018-10-30 09:24:59 +00:00
// Download the filter contents
2019-03-15 13:09:43 +00:00
ok , err := f . update ( )
2018-09-14 01:33:54 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "Couldn't fetch filter from url %s: %s" , f . URL , err )
2018-09-14 01:33:54 +00:00
return
}
2019-01-25 13:01:27 +00:00
if f . RulesCount == 0 {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "Filter at the url %s has no rules (maybe it points to blank page?)" , f . URL )
2018-09-14 01:33:54 +00:00
return
}
if ! ok {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "Filter at the url %s is invalid (maybe it points to blank page?)" , f . URL )
2018-10-30 09:24:59 +00:00
return
}
// Save the filter contents
2019-01-25 13:01:27 +00:00
err = f . save ( )
2018-10-30 09:24:59 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "Failed to save filter %d due to %s" , f . ID , err )
2018-09-14 01:33:54 +00:00
return
}
2018-12-05 17:29:00 +00:00
// URL is deemed valid, append it to filters, update config, write new filter file and tell dns to reload it
2019-01-24 17:11:01 +00:00
// TODO: since we directly feed filters in-memory, revisit if writing configs is always necessary
2019-03-18 11:41:38 +00:00
if ! filterAdd ( f ) {
httpError ( w , http . StatusBadRequest , "Filter URL already added -- %s" , f . URL )
return
2019-03-18 09:33:03 +00:00
}
2018-08-30 14:25:33 +00:00
err = writeAllConfigs ( )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Couldn't write config file: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
2018-10-29 23:17:24 +00:00
2019-01-25 13:01:27 +00:00
err = reconfigureDNSServer ( )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Couldn't reconfigure the DNS server: %s" , err )
2019-03-18 09:38:37 +00:00
return
2019-01-25 13:01:27 +00:00
}
2018-10-29 23:17:24 +00:00
2019-01-25 13:01:27 +00:00
_ , err = fmt . Fprintf ( w , "OK %d rules\n" , f . RulesCount )
2018-09-14 13:50:56 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Couldn't write body: %s" , err )
2018-09-14 13:50:56 +00:00
}
2018-08-30 14:25:33 +00:00
}
func handleFilteringRemoveURL ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-08-30 14:25:33 +00:00
parameters , err := parseParametersFromBody ( r . Body )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "failed to parse parameters from body: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
url , ok := parameters [ "url" ]
if ! ok {
2019-02-27 14:28:10 +00:00
http . Error ( w , "URL parameter was not specified" , http . StatusBadRequest )
2018-08-30 14:25:33 +00:00
return
}
if valid := govalidator . IsRequestURL ( url ) ; ! valid {
2019-02-27 14:28:10 +00:00
http . Error ( w , "URL parameter is not valid request URL" , http . StatusBadRequest )
2018-08-30 14:25:33 +00:00
return
}
// go through each element and delete if url matches
2019-03-18 09:33:03 +00:00
config . Lock ( )
2018-08-30 14:25:33 +00:00
newFilters := config . Filters [ : 0 ]
for _ , filter := range config . Filters {
if filter . URL != url {
newFilters = append ( newFilters , filter )
2018-10-29 23:17:24 +00:00
} else {
// Remove the filter file
2018-11-27 13:48:57 +00:00
err := os . Remove ( filter . Path ( ) )
2019-01-04 18:07:21 +00:00
if err != nil && ! os . IsNotExist ( err ) {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Couldn't remove the filter file: %s" , err )
2018-10-29 23:17:24 +00:00
return
}
2018-08-30 14:25:33 +00:00
}
}
2018-10-29 23:17:24 +00:00
// Update the configuration after removing filter files
2018-08-30 14:25:33 +00:00
config . Filters = newFilters
2019-03-18 09:33:03 +00:00
config . Unlock ( )
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleFilteringEnableURL ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-08-30 14:25:33 +00:00
parameters , err := parseParametersFromBody ( r . Body )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "failed to parse parameters from body: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
url , ok := parameters [ "url" ]
if ! ok {
2019-02-27 14:28:10 +00:00
http . Error ( w , "URL parameter was not specified" , http . StatusBadRequest )
2018-08-30 14:25:33 +00:00
return
}
if valid := govalidator . IsRequestURL ( url ) ; ! valid {
http . Error ( w , "URL parameter is not valid request URL" , http . StatusBadRequest )
return
}
2019-03-18 11:12:04 +00:00
found := filterEnable ( url , true )
2018-08-30 14:25:33 +00:00
if ! found {
http . Error ( w , "URL parameter was not previously added" , http . StatusBadRequest )
return
}
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleFilteringDisableURL ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-08-30 14:25:33 +00:00
parameters , err := parseParametersFromBody ( r . Body )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "failed to parse parameters from body: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
url , ok := parameters [ "url" ]
if ! ok {
2019-02-27 14:28:10 +00:00
http . Error ( w , "URL parameter was not specified" , http . StatusBadRequest )
2018-08-30 14:25:33 +00:00
return
}
if valid := govalidator . IsRequestURL ( url ) ; ! valid {
http . Error ( w , "URL parameter is not valid request URL" , http . StatusBadRequest )
return
}
2019-03-18 11:12:04 +00:00
found := filterEnable ( url , false )
2018-08-30 14:25:33 +00:00
if ! found {
http . Error ( w , "URL parameter was not previously added" , http . StatusBadRequest )
return
}
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleFilteringSetRules ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-08-30 14:25:33 +00:00
body , err := ioutil . ReadAll ( r . Body )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "Failed to read request body: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
config . UserRules = strings . Split ( string ( body ) , "\n" )
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleFilteringRefresh ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-03-19 11:28:12 +00:00
updated := refreshFiltersIfNecessary ( true )
2018-08-30 14:25:33 +00:00
fmt . Fprintf ( w , "OK %d filters updated\n" , updated )
}
// ------------
// safebrowsing
// ------------
func handleSafeBrowsingEnable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-12-05 17:29:00 +00:00
config . DNS . SafeBrowsingEnabled = true
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleSafeBrowsingDisable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-12-05 17:29:00 +00:00
config . DNS . SafeBrowsingEnabled = false
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleSafeBrowsingStatus ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-08-30 14:25:33 +00:00
data := map [ string ] interface { } {
2018-12-05 17:29:00 +00:00
"enabled" : config . DNS . SafeBrowsingEnabled ,
2018-08-30 14:25:33 +00:00
}
2018-10-20 16:58:39 +00:00
jsonVal , err := json . Marshal ( data )
2018-08-30 14:25:33 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to marshal status json: %s" , err )
2018-08-30 14:25:33 +00:00
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2018-10-20 16:58:39 +00:00
_ , err = w . Write ( jsonVal )
2018-08-30 14:25:33 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to write response json: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
}
// --------
// parental
// --------
func handleParentalEnable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-08-30 14:25:33 +00:00
parameters , err := parseParametersFromBody ( r . Body )
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusBadRequest , "failed to parse parameters from body: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
sensitivity , ok := parameters [ "sensitivity" ]
if ! ok {
http . Error ( w , "URL parameter was not specified" , 400 )
return
}
switch sensitivity {
case "3" :
break
case "EARLY_CHILDHOOD" :
sensitivity = "3"
case "10" :
break
case "YOUNG" :
sensitivity = "10"
case "13" :
break
case "TEEN" :
sensitivity = "13"
case "17" :
break
case "MATURE" :
sensitivity = "17"
default :
http . Error ( w , "Sensitivity must be set to valid value" , 400 )
return
}
i , err := strconv . Atoi ( sensitivity )
if err != nil {
http . Error ( w , "Sensitivity must be set to valid value" , 400 )
return
}
2018-12-05 17:29:00 +00:00
config . DNS . ParentalSensitivity = i
config . DNS . ParentalEnabled = true
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleParentalDisable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-12-05 17:29:00 +00:00
config . DNS . ParentalEnabled = false
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleParentalStatus ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-08-30 14:25:33 +00:00
data := map [ string ] interface { } {
2018-12-05 17:29:00 +00:00
"enabled" : config . DNS . ParentalEnabled ,
2018-08-30 14:25:33 +00:00
}
2018-12-05 17:29:00 +00:00
if config . DNS . ParentalEnabled {
data [ "sensitivity" ] = config . DNS . ParentalSensitivity
2018-08-30 14:25:33 +00:00
}
2018-10-20 16:58:39 +00:00
jsonVal , err := json . Marshal ( data )
2018-08-30 14:25:33 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to marshal status json: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2018-10-20 16:58:39 +00:00
_ , err = w . Write ( jsonVal )
2018-08-30 14:25:33 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to write response json: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
}
// ------------
// safebrowsing
// ------------
func handleSafeSearchEnable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-12-05 17:29:00 +00:00
config . DNS . SafeSearchEnabled = true
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleSafeSearchDisable ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-12-05 17:29:00 +00:00
config . DNS . SafeSearchEnabled = false
2018-10-10 17:13:03 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2018-08-30 14:25:33 +00:00
}
func handleSafeSearchStatus ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2018-08-30 14:25:33 +00:00
data := map [ string ] interface { } {
2018-12-05 17:29:00 +00:00
"enabled" : config . DNS . SafeSearchEnabled ,
2018-08-30 14:25:33 +00:00
}
2018-10-20 16:58:39 +00:00
jsonVal , err := json . Marshal ( data )
2018-08-30 14:25:33 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to marshal status json: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2018-10-20 16:58:39 +00:00
_ , err = w . Write ( jsonVal )
2018-08-30 14:25:33 +00:00
if err != nil {
2019-02-27 14:28:10 +00:00
httpError ( w , http . StatusInternalServerError , "Unable to write response json: %s" , err )
2018-08-30 14:25:33 +00:00
return
}
}
2019-01-29 17:41:57 +00:00
type ipport struct {
2019-02-01 15:59:42 +00:00
IP string ` json:"ip,omitempty" `
2019-02-01 15:18:08 +00:00
Port int ` json:"port" `
Warning string ` json:"warning" `
2019-01-29 17:41:57 +00:00
}
type firstRunData struct {
2019-01-31 11:56:34 +00:00
Web ipport ` json:"web" `
DNS ipport ` json:"dns" `
Username string ` json:"username,omitempty" `
Password string ` json:"password,omitempty" `
Interfaces map [ string ] interface { } ` json:"interfaces" `
2019-01-29 17:41:57 +00:00
}
2019-02-01 15:52:56 +00:00
func handleInstallGetAddresses ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-01-29 17:41:57 +00:00
data := firstRunData { }
2019-02-01 15:18:08 +00:00
// find out if port 80 is available -- if not, fall back to 3000
2019-02-07 11:22:08 +00:00
if checkPortAvailable ( "" , 80 ) == nil {
2019-02-01 15:18:08 +00:00
data . Web . Port = 80
} else {
data . Web . Port = 3000
}
// find out if port 53 is available -- if not, show a big warning
data . DNS . Port = 53
2019-02-07 11:22:08 +00:00
if checkPacketPortAvailable ( "" , 53 ) != nil {
2019-02-01 15:18:08 +00:00
data . DNS . Warning = "Port 53 is not available for binding -- this will make DNS clients unable to contact AdGuard Home."
}
2019-02-22 14:59:42 +00:00
ifaces , err := getValidNetInterfacesForWeb ( )
if err != nil {
httpError ( w , http . StatusInternalServerError , "Couldn't get interfaces: %s" , err )
return
}
2019-01-31 11:56:34 +00:00
data . Interfaces = make ( map [ string ] interface { } )
for _ , iface := range ifaces {
2019-02-22 14:59:42 +00:00
data . Interfaces [ iface . Name ] = iface
2019-01-31 11:56:34 +00:00
}
2019-01-17 14:16:49 +00:00
w . Header ( ) . Set ( "Content-Type" , "application/json" )
2019-01-29 17:41:57 +00:00
err = json . NewEncoder ( w ) . Encode ( data )
2019-01-17 14:16:49 +00:00
if err != nil {
httpError ( w , http . StatusInternalServerError , "Unable to marshal default addresses to json: %s" , err )
return
}
}
2019-02-01 15:52:56 +00:00
func handleInstallConfigure ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-01-29 17:41:57 +00:00
newSettings := firstRunData { }
2019-01-17 14:16:49 +00:00
err := json . NewDecoder ( r . Body ) . Decode ( & newSettings )
if err != nil {
2019-02-22 14:59:42 +00:00
httpError ( w , http . StatusBadRequest , "Failed to parse new config json: %s" , err )
2019-01-17 14:16:49 +00:00
return
}
2019-02-07 15:08:25 +00:00
restartHTTP := true
if config . BindHost == newSettings . Web . IP && config . BindPort == newSettings . Web . Port {
// no need to rebind
restartHTTP = false
}
2019-02-01 16:25:04 +00:00
// validate that hosts and ports are bindable
2019-02-07 15:08:25 +00:00
if restartHTTP {
err = checkPortAvailable ( newSettings . Web . IP , newSettings . Web . Port )
if err != nil {
httpError ( w , http . StatusBadRequest , "Impossible to listen on IP:port %s due to %s" , net . JoinHostPort ( newSettings . Web . IP , strconv . Itoa ( newSettings . Web . Port ) ) , err )
return
}
2019-02-01 16:25:04 +00:00
}
2019-02-07 11:22:08 +00:00
err = checkPacketPortAvailable ( newSettings . DNS . IP , newSettings . DNS . Port )
if err != nil {
httpError ( w , http . StatusBadRequest , "Impossible to listen on IP:port %s due to %s" , net . JoinHostPort ( newSettings . DNS . IP , strconv . Itoa ( newSettings . DNS . Port ) ) , err )
2019-02-01 16:25:04 +00:00
return
}
2019-01-29 17:41:57 +00:00
config . firstRun = false
config . BindHost = newSettings . Web . IP
config . BindPort = newSettings . Web . Port
config . DNS . BindHost = newSettings . DNS . IP
config . DNS . Port = newSettings . DNS . Port
config . AuthName = newSettings . Username
config . AuthPass = newSettings . Password
2019-02-01 16:42:33 +00:00
if config . DNS . Port != 0 {
err = startDNSServer ( )
if err != nil {
httpError ( w , http . StatusInternalServerError , "Couldn't start DNS server: %s" , err )
return
}
}
2019-01-29 17:41:57 +00:00
httpUpdateConfigReloadDNSReturnOK ( w , r )
2019-02-06 13:47:17 +00:00
// this needs to be done in a goroutine because Shutdown() is a blocking call, and it will block
// until all requests are finished, and _we_ are inside a request right now, so it will block indefinitely
2019-02-07 15:08:25 +00:00
if restartHTTP {
go func ( ) {
httpServer . Shutdown ( context . TODO ( ) )
} ( )
}
2019-01-17 14:16:49 +00:00
}
2019-02-22 12:52:12 +00:00
// --------------
// DNS-over-HTTPS
// --------------
func handleDOH ( w http . ResponseWriter , r * http . Request ) {
2019-02-27 15:09:57 +00:00
log . Tracef ( "%s %v" , r . Method , r . URL )
2019-02-22 12:52:12 +00:00
if r . TLS == nil {
httpError ( w , http . StatusNotFound , "Not Found" )
return
}
if ! isRunning ( ) {
httpError ( w , http . StatusInternalServerError , "DNS server is not running" )
return
}
dnsServer . ServeHTTP ( w , r )
}
2019-02-12 17:02:52 +00:00
// ------------------------
// registration of handlers
// ------------------------
2019-02-06 13:48:04 +00:00
func registerInstallHandlers ( ) {
http . HandleFunc ( "/control/install/get_addresses" , preInstall ( ensureGET ( handleInstallGetAddresses ) ) )
http . HandleFunc ( "/control/install/configure" , preInstall ( ensurePOST ( handleInstallConfigure ) ) )
}
2018-08-30 14:25:33 +00:00
func registerControlHandlers ( ) {
2019-01-29 17:41:57 +00:00
http . HandleFunc ( "/control/status" , postInstall ( optionalAuth ( ensureGET ( handleStatus ) ) ) )
http . HandleFunc ( "/control/enable_protection" , postInstall ( optionalAuth ( ensurePOST ( handleProtectionEnable ) ) ) )
http . HandleFunc ( "/control/disable_protection" , postInstall ( optionalAuth ( ensurePOST ( handleProtectionDisable ) ) ) )
2019-04-15 13:21:12 +00:00
http . Handle ( "/control/querylog" , postInstallHandler ( optionalAuthHandler ( gziphandler . GzipHandler ( ensureGETHandler ( handleQueryLog ) ) ) ) )
2019-01-29 17:41:57 +00:00
http . HandleFunc ( "/control/querylog_enable" , postInstall ( optionalAuth ( ensurePOST ( handleQueryLogEnable ) ) ) )
http . HandleFunc ( "/control/querylog_disable" , postInstall ( optionalAuth ( ensurePOST ( handleQueryLogDisable ) ) ) )
2019-02-28 08:10:43 +00:00
http . HandleFunc ( "/control/set_upstreams_config" , postInstall ( optionalAuth ( ensurePOST ( handleSetUpstreamConfig ) ) ) )
2019-01-29 17:41:57 +00:00
http . HandleFunc ( "/control/test_upstream_dns" , postInstall ( optionalAuth ( ensurePOST ( handleTestUpstreamDNS ) ) ) )
http . HandleFunc ( "/control/i18n/change_language" , postInstall ( optionalAuth ( ensurePOST ( handleI18nChangeLanguage ) ) ) )
http . HandleFunc ( "/control/i18n/current_language" , postInstall ( optionalAuth ( ensureGET ( handleI18nCurrentLanguage ) ) ) )
2019-02-10 17:47:43 +00:00
http . HandleFunc ( "/control/stats_top" , postInstall ( optionalAuth ( ensureGET ( handleStatsTop ) ) ) )
http . HandleFunc ( "/control/stats" , postInstall ( optionalAuth ( ensureGET ( handleStats ) ) ) )
http . HandleFunc ( "/control/stats_history" , postInstall ( optionalAuth ( ensureGET ( handleStatsHistory ) ) ) )
http . HandleFunc ( "/control/stats_reset" , postInstall ( optionalAuth ( ensurePOST ( handleStatsReset ) ) ) )
2019-01-29 17:41:57 +00:00
http . HandleFunc ( "/control/version.json" , postInstall ( optionalAuth ( handleGetVersionJSON ) ) )
http . HandleFunc ( "/control/filtering/enable" , postInstall ( optionalAuth ( ensurePOST ( handleFilteringEnable ) ) ) )
http . HandleFunc ( "/control/filtering/disable" , postInstall ( optionalAuth ( ensurePOST ( handleFilteringDisable ) ) ) )
2019-04-16 09:11:12 +00:00
http . HandleFunc ( "/control/filtering/add_url" , postInstall ( optionalAuth ( ensurePOST ( handleFilteringAddURL ) ) ) )
http . HandleFunc ( "/control/filtering/remove_url" , postInstall ( optionalAuth ( ensurePOST ( handleFilteringRemoveURL ) ) ) )
2019-01-29 17:41:57 +00:00
http . HandleFunc ( "/control/filtering/enable_url" , postInstall ( optionalAuth ( ensurePOST ( handleFilteringEnableURL ) ) ) )
http . HandleFunc ( "/control/filtering/disable_url" , postInstall ( optionalAuth ( ensurePOST ( handleFilteringDisableURL ) ) ) )
http . HandleFunc ( "/control/filtering/refresh" , postInstall ( optionalAuth ( ensurePOST ( handleFilteringRefresh ) ) ) )
http . HandleFunc ( "/control/filtering/status" , postInstall ( optionalAuth ( ensureGET ( handleFilteringStatus ) ) ) )
2019-04-16 09:11:12 +00:00
http . HandleFunc ( "/control/filtering/set_rules" , postInstall ( optionalAuth ( ensurePOST ( handleFilteringSetRules ) ) ) )
2019-01-29 17:41:57 +00:00
http . HandleFunc ( "/control/safebrowsing/enable" , postInstall ( optionalAuth ( ensurePOST ( handleSafeBrowsingEnable ) ) ) )
http . HandleFunc ( "/control/safebrowsing/disable" , postInstall ( optionalAuth ( ensurePOST ( handleSafeBrowsingDisable ) ) ) )
http . HandleFunc ( "/control/safebrowsing/status" , postInstall ( optionalAuth ( ensureGET ( handleSafeBrowsingStatus ) ) ) )
http . HandleFunc ( "/control/parental/enable" , postInstall ( optionalAuth ( ensurePOST ( handleParentalEnable ) ) ) )
http . HandleFunc ( "/control/parental/disable" , postInstall ( optionalAuth ( ensurePOST ( handleParentalDisable ) ) ) )
http . HandleFunc ( "/control/parental/status" , postInstall ( optionalAuth ( ensureGET ( handleParentalStatus ) ) ) )
http . HandleFunc ( "/control/safesearch/enable" , postInstall ( optionalAuth ( ensurePOST ( handleSafeSearchEnable ) ) ) )
http . HandleFunc ( "/control/safesearch/disable" , postInstall ( optionalAuth ( ensurePOST ( handleSafeSearchDisable ) ) ) )
http . HandleFunc ( "/control/safesearch/status" , postInstall ( optionalAuth ( ensureGET ( handleSafeSearchStatus ) ) ) )
http . HandleFunc ( "/control/dhcp/status" , postInstall ( optionalAuth ( ensureGET ( handleDHCPStatus ) ) ) )
http . HandleFunc ( "/control/dhcp/interfaces" , postInstall ( optionalAuth ( ensureGET ( handleDHCPInterfaces ) ) ) )
http . HandleFunc ( "/control/dhcp/set_config" , postInstall ( optionalAuth ( ensurePOST ( handleDHCPSetConfig ) ) ) )
http . HandleFunc ( "/control/dhcp/find_active_dhcp" , postInstall ( optionalAuth ( ensurePOST ( handleDHCPFindActiveServer ) ) ) )
2019-01-23 14:26:15 +00:00
2019-02-27 15:53:16 +00:00
RegisterTLSHandlers ( )
2019-03-19 15:47:22 +00:00
RegisterClientsHandlers ( )
2019-02-22 12:52:12 +00:00
http . HandleFunc ( "/dns-query" , postInstall ( handleDOH ) )
2018-08-30 14:25:33 +00:00
}