2018-08-30 14:25:33 +00:00
package main
import (
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"
"strconv"
"strings"
"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"
2018-12-29 16:12:22 +00:00
"github.com/hmage/golibs/log"
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
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
2018-10-10 12:47:08 +00:00
var client = & http . Client {
Timeout : time . Second * 30 ,
}
2018-08-30 14:25:33 +00:00
// -------------------
2018-12-05 17:29:00 +00:00
// dns run control
2018-08-30 14:25:33 +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 {
log . Printf ( "Couldn't write all configs: %s" , err )
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Couldn't write config file: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusInternalServerError )
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
}
2019-01-24 17:11:01 +00:00
func returnOK ( w http . ResponseWriter ) {
2018-10-10 17:13:03 +00:00
_ , err := fmt . Fprintf ( w , "OK\n" )
if err != nil {
2019-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Couldn't write body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusInternalServerError )
2018-10-10 17:13:03 +00:00
}
}
2018-08-30 14:25:33 +00:00
func handleStatus ( w http . ResponseWriter , r * http . Request ) {
data := map [ string ] interface { } {
2018-10-10 17:13:03 +00:00
"dns_address" : config . BindHost ,
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 ,
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Unable to marshal status json: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 500 )
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Unable to write response json: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 500 )
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 ) {
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 ) {
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 ) {
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 ) {
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
}
2018-09-26 14:47:23 +00:00
func httpError ( w http . ResponseWriter , code int , format string , args ... interface { } ) {
text := fmt . Sprintf ( format , args ... )
2018-10-11 15:32:23 +00:00
log . Println ( text )
2018-09-26 14:47:23 +00:00
http . Error ( w , text , code )
}
2018-08-30 14:25:33 +00:00
func handleSetUpstreamDNS ( w http . ResponseWriter , r * http . Request ) {
body , err := ioutil . ReadAll ( r . Body )
if err != nil {
2018-11-01 11:45:32 +00:00
errorText := fmt . Sprintf ( "Failed to read request body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusBadRequest )
2018-08-30 14:25:33 +00:00
return
}
// if empty body -- user is asking for default servers
2018-11-05 21:47:59 +00:00
hosts := strings . Fields ( string ( body ) )
2018-08-30 14:25:33 +00:00
if len ( hosts ) == 0 {
2018-12-05 17:29:00 +00:00
config . DNS . UpstreamDNS = defaultDNS
2018-08-30 14:25:33 +00:00
} else {
2018-12-05 17:29:00 +00:00
config . DNS . UpstreamDNS = hosts
2018-08-30 14:25:33 +00:00
}
err = writeAllConfigs ( )
if err != nil {
2018-11-01 11:45:32 +00:00
errorText := fmt . Sprintf ( "Couldn't write config file: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusInternalServerError )
2018-08-30 14:25:33 +00:00
return
}
2019-01-24 17:11:01 +00:00
err = reconfigureDNSServer ( )
if err != nil {
errorText := fmt . Sprintf ( "Couldn't reconfigure the DNS server: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusInternalServerError )
return
}
2018-09-14 13:50:56 +00:00
_ , err = fmt . Fprintf ( w , "OK %d servers\n" , len ( hosts ) )
if err != nil {
2018-11-01 11:45:32 +00:00
errorText := fmt . Sprintf ( "Couldn't write body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusInternalServerError )
2018-09-14 13:50:56 +00:00
}
2018-08-30 14:25:33 +00:00
}
2018-09-19 16:12:09 +00:00
func handleTestUpstreamDNS ( w http . ResponseWriter , r * http . Request ) {
body , err := ioutil . ReadAll ( r . Body )
if err != nil {
2018-11-01 11:45:32 +00:00
errorText := fmt . Sprintf ( "Failed to read request body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 400 )
2018-09-19 16:12:09 +00:00
return
}
hosts := strings . Fields ( string ( body ) )
if len ( hosts ) == 0 {
2018-11-01 11:45:32 +00:00
errorText := fmt . Sprintf ( "No servers specified" )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusBadRequest )
2018-09-19 16:12:09 +00:00
return
}
result := map [ string ] string { }
for _ , host := range hosts {
2018-10-07 20:43:24 +00:00
err = checkDNS ( host )
2018-09-19 16:12:09 +00:00
if err != nil {
log . Println ( err )
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 {
2018-11-01 11:45:32 +00:00
errorText := fmt . Sprintf ( "Unable to marshal status json: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusInternalServerError )
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 {
2018-11-01 11:45:32 +00:00
errorText := fmt . Sprintf ( "Couldn't write body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusInternalServerError )
2018-09-19 16:12:09 +00:00
}
}
2018-09-26 14:47:23 +00:00
func checkDNS ( input string ) error {
2018-12-05 16:17:17 +00:00
log . Printf ( "Checking if DNS %s works..." , input )
2018-12-24 22:59:38 +00:00
u , err := upstream . AddressToUpstream ( input , "" , 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
}
2018-12-05 16:17:17 +00:00
log . Printf ( "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 ) {
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Couldn't get version check json from %s: %T %s\n" , versionCheckURL , err , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusBadGateway )
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Couldn't read response body from %s: %s" , versionCheckURL , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusBadGateway )
2018-09-20 17:02:25 +00:00
return
}
w . Header ( ) . Set ( "Content-Type" , "application/json" )
_ , err = w . Write ( body )
if err != nil {
2019-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Couldn't write body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusInternalServerError )
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 ) {
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 ) {
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 ) {
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Unable to marshal status json: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 500 )
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Unable to write response json: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 500 )
2018-08-30 14:25:33 +00:00
return
}
}
func handleFilteringAddURL ( w http . ResponseWriter , r * http . Request ) {
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 {
2018-08-30 14:25:33 +00:00
http . Error ( w , "URL parameter was not specified" , 400 )
return
}
2019-01-25 13:01:27 +00:00
if valid := govalidator . IsRequestURL ( f . URL ) ; ! valid {
2018-08-30 14:25:33 +00:00
http . Error ( w , "URL parameter is not valid request URL" , 400 )
return
}
2018-09-14 01:33:54 +00:00
2018-10-30 09:24:59 +00:00
// Check for duplicates
2018-09-14 01:33:54 +00:00
for i := range config . Filters {
2019-01-25 13:01:27 +00:00
if config . Filters [ i ] . URL == f . URL {
errorText := fmt . Sprintf ( "Filter URL already added -- %s" , f . URL )
2018-10-30 09:24:59 +00:00
log . Println ( errorText )
http . Error ( w , errorText , http . StatusBadRequest )
2018-09-14 01:33:54 +00:00
return
}
}
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-01-25 13:01:27 +00:00
ok , err := f . update ( true )
2018-09-14 01:33:54 +00:00
if err != nil {
2019-01-25 13:01:27 +00:00
errorText := fmt . Sprintf ( "Couldn't fetch filter from url %s: %s" , f . URL , err )
2018-10-30 09:24:59 +00:00
log . Println ( errorText )
http . Error ( w , errorText , http . StatusBadRequest )
2018-09-14 01:33:54 +00:00
return
}
2019-01-25 13:01:27 +00:00
if f . RulesCount == 0 {
errorText := fmt . Sprintf ( "Filter at the url %s has no rules (maybe it points to blank page?)" , f . URL )
2018-10-30 09:24:59 +00:00
log . Println ( errorText )
http . Error ( w , errorText , http . StatusBadRequest )
2018-09-14 01:33:54 +00:00
return
}
if ! ok {
2019-01-25 13:01:27 +00:00
errorText := fmt . Sprintf ( "Filter at the url %s is invalid (maybe it points to blank page?)" , f . URL )
2018-10-30 09:24:59 +00:00
log . Println ( errorText )
http . Error ( w , errorText , http . StatusBadRequest )
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-01-25 13:01:27 +00:00
errorText := fmt . Sprintf ( "Failed to save filter %d due to %s" , f . ID , err )
2018-10-30 09:24:59 +00:00
log . Println ( errorText )
http . Error ( w , errorText , http . StatusBadRequest )
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-01-25 13:01:27 +00:00
config . Filters = append ( config . Filters , f )
2018-08-30 14:25:33 +00:00
err = writeAllConfigs ( )
if err != nil {
2018-10-30 09:24:59 +00:00
errorText := fmt . Sprintf ( "Couldn't write config file: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusInternalServerError )
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 {
errorText := fmt . Sprintf ( "Couldn't reconfigure the DNS server: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusInternalServerError )
}
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 {
2018-10-30 09:24:59 +00:00
errorText := fmt . Sprintf ( "Couldn't write body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , http . StatusInternalServerError )
2018-09-14 13:50:56 +00:00
}
2018-08-30 14:25:33 +00:00
}
func handleFilteringRemoveURL ( w http . ResponseWriter , r * http . Request ) {
parameters , err := parseParametersFromBody ( r . Body )
if err != nil {
2018-10-30 09:24:59 +00:00
errorText := fmt . Sprintf ( "failed to parse parameters from body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 400 )
2018-08-30 14:25:33 +00:00
return
}
url , ok := parameters [ "url" ]
if ! ok {
http . Error ( w , "URL parameter was not specified" , 400 )
return
}
if valid := govalidator . IsRequestURL ( url ) ; ! valid {
http . Error ( w , "URL parameter is not valid request URL" , 400 )
return
}
// go through each element and delete if url matches
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 ) {
2018-10-30 09:24:59 +00:00
errorText := fmt . Sprintf ( "Couldn't remove the filter file: %s" , err )
http . Error ( w , errorText , http . StatusInternalServerError )
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
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 ) {
parameters , err := parseParametersFromBody ( r . Body )
if err != nil {
2018-10-30 09:24:59 +00:00
errorText := fmt . Sprintf ( "failed to parse parameters from body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 400 )
2018-08-30 14:25:33 +00:00
return
}
url , ok := parameters [ "url" ]
if ! ok {
http . Error ( w , "URL parameter was not specified" , 400 )
return
}
if valid := govalidator . IsRequestURL ( url ) ; ! valid {
http . Error ( w , "URL parameter is not valid request URL" , http . StatusBadRequest )
return
}
found := false
for i := range config . Filters {
filter := & config . Filters [ i ] // otherwise we will be operating on a copy
if filter . URL == url {
filter . Enabled = true
found = true
}
}
if ! found {
http . Error ( w , "URL parameter was not previously added" , http . StatusBadRequest )
return
}
// kick off refresh of rules from new URLs
2019-01-24 17:11:01 +00:00
refreshFiltersIfNecessary ( false )
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 ) {
parameters , err := parseParametersFromBody ( r . Body )
if err != nil {
2018-10-30 09:24:59 +00:00
errorText := fmt . Sprintf ( "failed to parse parameters from body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 400 )
2018-08-30 14:25:33 +00:00
return
}
url , ok := parameters [ "url" ]
if ! ok {
http . Error ( w , "URL parameter was not specified" , 400 )
return
}
if valid := govalidator . IsRequestURL ( url ) ; ! valid {
http . Error ( w , "URL parameter is not valid request URL" , http . StatusBadRequest )
return
}
found := false
for i := range config . Filters {
filter := & config . Filters [ i ] // otherwise we will be operating on a copy
if filter . URL == url {
filter . Enabled = false
found = true
}
}
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 ) {
body , err := ioutil . ReadAll ( r . Body )
if err != nil {
2018-10-30 09:24:59 +00:00
errorText := fmt . Sprintf ( "Failed to read request body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 400 )
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 ) {
force := r . URL . Query ( ) . Get ( "force" )
2019-01-24 17:11:01 +00:00
updated := refreshFiltersIfNecessary ( force != "" )
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 ) {
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 ) {
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 ) {
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Unable to marshal status json: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 500 )
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Unable to write response json: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 500 )
2018-08-30 14:25:33 +00:00
return
}
}
// --------
// parental
// --------
func handleParentalEnable ( w http . ResponseWriter , r * http . Request ) {
parameters , err := parseParametersFromBody ( r . Body )
if err != nil {
2019-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "failed to parse parameters from body: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 400 )
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 ) {
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 ) {
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Unable to marshal status json: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 500 )
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Unable to write response json: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 500 )
2018-08-30 14:25:33 +00:00
return
}
}
// ------------
// safebrowsing
// ------------
func handleSafeSearchEnable ( w http . ResponseWriter , r * http . Request ) {
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 ) {
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 ) {
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Unable to marshal status json: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 500 )
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-01-24 17:11:01 +00:00
errorText := fmt . Sprintf ( "Unable to write response json: %s" , err )
log . Println ( errorText )
http . Error ( w , errorText , 500 )
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-01-29 17:41:57 +00:00
data := firstRunData { }
ifaces , err := getValidNetInterfaces ( )
if err != nil {
httpError ( w , http . StatusInternalServerError , "Couldn't get interfaces: %s" , err )
return
}
if len ( ifaces ) == 0 {
httpError ( w , http . StatusServiceUnavailable , "Couldn't find any legible interface, plase try again later" )
return
2019-01-17 16:29:54 +00:00
}
2019-01-17 14:16:49 +00:00
2019-01-31 11:56:34 +00:00
// fill out the fields
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-01-31 11:56:34 +00:00
data . Interfaces = make ( map [ string ] interface { } )
for _ , iface := range ifaces {
2019-02-07 15:07:38 +00:00
addrs , err := iface . Addrs ( )
if err != nil {
httpError ( w , http . StatusInternalServerError , "Failed to get addresses for interface %s: %s" , iface . Name , err )
return
}
jsonIface := netInterface {
Name : iface . Name ,
MTU : iface . MTU ,
HardwareAddr : iface . HardwareAddr . String ( ) ,
}
if iface . Flags != 0 {
jsonIface . Flags = iface . Flags . String ( )
}
// we don't want link-local addresses in json, so skip them
for _ , addr := range addrs {
ipnet , ok := addr . ( * net . IPNet )
if ! ok {
// not an IPNet, should not happen
httpError ( w , http . StatusInternalServerError , "SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet, it is %T" , addr , addr )
return
}
// ignore link-local
if ipnet . IP . IsLinkLocalUnicast ( ) {
2019-02-01 15:38:52 +00:00
continue
}
2019-02-07 15:07:38 +00:00
jsonIface . Addresses = append ( jsonIface . Addresses , ipnet . IP . String ( ) )
}
if len ( jsonIface . Addresses ) != 0 {
data . Interfaces [ iface . Name ] = jsonIface
2019-02-01 15:38:52 +00:00
}
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-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 {
httpError ( w , http . StatusBadRequest , "Failed to parse new DHCP config json: %s" , err )
return
}
2019-02-01 16:25:04 +00:00
// validate that hosts and ports are bindable
2019-02-07 11:22:08 +00:00
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 )
2019-02-01 16:25:04 +00:00
return
}
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
go func ( ) {
httpServer . Shutdown ( context . TODO ( ) )
} ( )
2019-01-17 14:16:49 +00:00
}
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 ) ) ) )
http . HandleFunc ( "/control/querylog" , postInstall ( optionalAuth ( ensureGET ( dnsforward . HandleQueryLog ) ) ) )
http . HandleFunc ( "/control/querylog_enable" , postInstall ( optionalAuth ( ensurePOST ( handleQueryLogEnable ) ) ) )
http . HandleFunc ( "/control/querylog_disable" , postInstall ( optionalAuth ( ensurePOST ( handleQueryLogDisable ) ) ) )
http . HandleFunc ( "/control/set_upstream_dns" , postInstall ( optionalAuth ( ensurePOST ( handleSetUpstreamDNS ) ) ) )
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 ) ) ) )
http . HandleFunc ( "/control/stats_top" , postInstall ( optionalAuth ( ensureGET ( dnsforward . HandleStatsTop ) ) ) )
http . HandleFunc ( "/control/stats" , postInstall ( optionalAuth ( ensureGET ( dnsforward . HandleStats ) ) ) )
http . HandleFunc ( "/control/stats_history" , postInstall ( optionalAuth ( ensureGET ( dnsforward . HandleStatsHistory ) ) ) )
http . HandleFunc ( "/control/stats_reset" , postInstall ( optionalAuth ( ensurePOST ( dnsforward . HandleStatsReset ) ) ) )
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 ) ) ) )
http . HandleFunc ( "/control/filtering/add_url" , postInstall ( optionalAuth ( ensurePUT ( handleFilteringAddURL ) ) ) )
http . HandleFunc ( "/control/filtering/remove_url" , postInstall ( optionalAuth ( ensureDELETE ( handleFilteringRemoveURL ) ) ) )
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 ) ) ) )
http . HandleFunc ( "/control/filtering/set_rules" , postInstall ( optionalAuth ( ensurePUT ( handleFilteringSetRules ) ) ) )
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 ) ) ) )
2018-08-30 14:25:33 +00:00
}