diff --git a/cmd/dnsp/main.go b/cmd/dnsp/main.go index 82012ae..29f4d19 100644 --- a/cmd/dnsp/main.go +++ b/cmd/dnsp/main.go @@ -59,6 +59,12 @@ func main() { Usage: "start a web-based UI on the given address (host:port, host or port)", EnvVar: "DNSP_HTTP", }, + cli.StringFlag{ + Name: "blockedip, i", + Value: "192.168.1.1", + Usage: "resolve blocked domains to this IPv4 IP address (e.g. 192.168.1.x)", + EnvVar: "DNSP_BLOCKEDIP", + }, } app.Action = func(c *cli.Context) { resolve := []string{} @@ -72,6 +78,7 @@ func main() { Poll: c.Duration("poll"), Whitelist: c.String("whitelist"), Blacklist: c.String("blacklist"), + BlockedIP: c.String("blockedip"), } s, err := dnsp.NewServer(*o) if err != nil { diff --git a/options.go b/options.go index 384eaa4..92200c4 100644 --- a/options.go +++ b/options.go @@ -19,6 +19,8 @@ type Options struct { Whitelist string Blacklist string + + BlockedIP string } // validate verifies that the options are correct. @@ -71,6 +73,12 @@ func (o *Options) validate() error { } } + if o.BlockedIP == "" { + return errors.New("--blockedip is a must.") + } else if net.ParseIP(o.BlockedIP) == nil { + return errors.New("--blockedip must be a valid IP") + } + return nil } diff --git a/options_test.go b/options_test.go index ac91ded..e60b555 100644 --- a/options_test.go +++ b/options_test.go @@ -15,14 +15,16 @@ func TestValidate(t *testing.T) { { // invalid Net dnsp.Options{ - Net: "something invalid", + Net: "something invalid", + BlockedIP: "192.168.1.117", }, true, }, { // valid bind dnsp.Options{ - Bind: "example.com:dns", + Bind: "example.com:dns", + BlockedIP: "192.168.1.117", }, false, }, @@ -32,6 +34,7 @@ func TestValidate(t *testing.T) { Resolve: []string{ "something.com", }, + BlockedIP: "192.168.1.117", }, true, }, @@ -41,13 +44,15 @@ func TestValidate(t *testing.T) { Resolve: []string{ "0.0.0.0:53", }, + BlockedIP: "192.168.1.117", }, false, }, { // Poll too short dnsp.Options{ - Poll: time.Millisecond * 900, + Poll: time.Millisecond * 900, + BlockedIP: "192.168.1.117", }, true, }, @@ -56,6 +61,7 @@ func TestValidate(t *testing.T) { dnsp.Options{ Whitelist: "wikipedia.com", Blacklist: "badsite.com", + BlockedIP: "192.168.1.117", }, true, }, @@ -63,6 +69,7 @@ func TestValidate(t *testing.T) { // invalid whitelist dnsp.Options{ Whitelist: "somethinginvalid", + BlockedIP: "192.168.1.117", }, true, }, @@ -70,6 +77,21 @@ func TestValidate(t *testing.T) { // invalid blacklist dnsp.Options{ Blacklist: "somethinginvalid", + BlockedIP: "192.168.1.117", + }, + true, + }, + { + // valid blockedip + dnsp.Options{ + BlockedIP: "192.168.1.117", + }, + false, + }, + { + // invalid blockedip + dnsp.Options{ + BlockedIP: "somethinginvalid", }, true, }, diff --git a/server.go b/server.go index 5cebb5d..42ab9cd 100644 --- a/server.go +++ b/server.go @@ -1,6 +1,8 @@ package dnsp import ( + "log" + "net" "regexp" "sync" "time" @@ -37,6 +39,12 @@ type Server struct { } } +const ( + notIPQuery = 0 + _IP4Query = 4 + _IP6Query = 6 +) + // NewServer creates a new Server with the given options. func NewServer(o Options) (*Server, error) { if err := o.validate(); err != nil { @@ -57,6 +65,9 @@ func NewServer(o Options) (*Server, error) { privateHostsRX: map[string]*regexp.Regexp{}, } + IPv4 := net.ParseIP(o.BlockedIP).To4() + IPv16 := net.ParseIP(o.BlockedIP).To16() + hostListPath := o.Whitelist if hostListPath == "" { hostListPath = o.Blacklist @@ -75,10 +86,42 @@ func NewServer(o Options) (*Server, error) { return } - // Filter Questions: - if r.Question = s.filter(r.Question); len(r.Question) == 0 { - w.WriteMsg(r) - return + if len(r.Question) > 0 { + q := r.Question[0] + + // Filter Questions: + if r.Question = s.filter(r.Question); len(r.Question) == 0 { + IPQuery := s.isIPQuery(q) + m := new(dns.Msg) + m.SetReply(r) + + switch IPQuery { + case _IP4Query: + rr_header := dns.RR_Header{ + Name: q.Name, + Rrtype: dns.TypeA, + Class: dns.ClassINET, + Ttl: 600, + } + + a := &dns.A{Hdr: rr_header, A: IPv4} + m.Answer = append(m.Answer, a) + + case _IP6Query: + rr_header := dns.RR_Header{ + Name: q.Name, + Rrtype: dns.TypeAAAA, + Class: dns.ClassINET, + Ttl: 600, + } + aaaa := &dns.AAAA{Hdr: rr_header, AAAA: IPv16} + m.Answer = append(m.Answer, aaaa) + } + + log.Printf("blocked: %s", q.Name) + w.WriteMsg(m) + return + } } // Proxy Query: @@ -95,6 +138,21 @@ func NewServer(o Options) (*Server, error) { return &s, nil } +func (h *Server) isIPQuery(q dns.Question) int { + if q.Qclass != dns.ClassINET { + return notIPQuery + } + + switch q.Qtype { + case dns.TypeA: + return _IP4Query + case dns.TypeAAAA: + return _IP6Query + default: + return notIPQuery + } +} + // ListenAndServe runs the server func (s *Server) ListenAndServe() error { return s.s.ListenAndServe() diff --git a/whitelist_test.go b/whitelist_test.go index c03f821..11865c9 100644 --- a/whitelist_test.go +++ b/whitelist_test.go @@ -23,6 +23,7 @@ func TestIsAllowedWhite(t *testing.T) { s, err := dnsp.NewServer(dnsp.Options{ Whitelist: tmp.Name(), + BlockedIP: "192.168.1.117", }) if err != nil { t.Fatal(err) @@ -59,6 +60,7 @@ func TestIsAllowedBlack(t *testing.T) { s, err := dnsp.NewServer(dnsp.Options{ Blacklist: tmp.Name(), + BlockedIP: "192.168.1.117", }) if err != nil { t.Fatal(err)