/
blowback.go
126 lines (108 loc) · 3.25 KB
/
blowback.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package blowback
import (
"context"
"errors"
"fmt"
"io"
"net"
"regexp"
"strconv"
"strings"
"github.com/coredns/coredns/plugin"
clog "github.com/coredns/coredns/plugin/pkg/log"
"github.com/coredns/coredns/request"
"github.com/miekg/dns"
)
var log = clog.NewWithPlugin("blowback")
var regIpDash = regexp.MustCompile(`^(\d{1,3}-\d{1,3}-\d{1,3}-\d{1,3})(-\d+)?\.`)
// Dump implement the plugin interface.
type Blowback struct {
Next plugin.Handler
// the url of the proxy server which we will initiate
// a callback to the dns client requestor
// todo(bonedaddy): require access control
ProxyServerURL string
}
// taken from https://github.com/wenerme/coredns-ipin
func (b Blowback) Resolve(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (*dns.Msg, int, error) {
state := request.Request{W: w, Req: r}
a := new(dns.Msg)
a.SetReply(r)
a.Compress = true
a.Authoritative = true
matches := regIpDash.FindStringSubmatch(state.QName())
if len(matches) > 1 {
ip := matches[1]
ip = strings.Replace(ip, "-", ".", -1)
var rr dns.RR
rr = new(dns.A)
rr.(*dns.A).Hdr = dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeA, Class: state.QClass()}
rr.(*dns.A).A = net.ParseIP(ip).To4()
a.Answer = []dns.RR{rr}
if len(matches[2]) > 0 {
srv := new(dns.SRV)
srv.Hdr = dns.RR_Header{Name: "_port." + state.QName(), Rrtype: dns.TypeSRV, Class: state.QClass()}
if state.QName() == "." {
srv.Hdr.Name = "_port." + state.QName()
}
port, _ := strconv.Atoi(matches[2][1:])
srv.Port = uint16(port)
srv.Target = "."
a.Extra = []dns.RR{srv}
}
} else {
// return empty
}
return a, 0, nil
}
// ServeDNS implements the plugin.Handler interface.
func (b Blowback) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
log.Info("resolving dns request")
a, i, err := b.Resolve(ctx, w, r)
if err != nil {
return i, err
}
if len(a.Answer) == 0 {
return 1, plugin.Error("blowback", errors.New("failed to find any answers"))
}
if len(a.Extra) == 0 {
return 1, plugin.Error("blowback", errors.New("failed to find srv record"))
}
aRecord, ok := a.Answer[0].(*dns.A)
if !ok {
log.Error("failed to parse answer into dns.A record")
return 1, plugin.Error("blowback", errors.New("failed to parse answer into A record"))
}
srvRecord, ok := a.Extra[0].(*dns.SRV)
if !ok {
log.Error("failed to parse extra into SRV record")
return 1, plugin.Error("blowback", errors.New("failed to parse into SRV record"))
}
log.Info("spawning background blowback task", "host ", aRecord.A.String(), " port ", srvRecord.Port)
go func(msg *dns.Msg) {
aAddr := aRecord.A.String()
port := fmt.Sprint(srvRecord.Port)
log.Info(aAddr + ":" + port)
// determine protocol based on the remoteADdr
incoming, err := net.Dial("tcp", aRecord.A.String()+":"+port)
if err != nil {
log.Error(err)
return
}
outgoing, err := net.Dial("tcp", b.ProxyServerURL)
if err != nil {
log.Error(err)
return
}
go func() {
defer incoming.Close()
defer outgoing.Close()
io.Copy(outgoing, incoming)
}()
log.Info("connecting to proxy server")
}(a.Copy())
return 0, w.WriteMsg(a)
}
// Name implements the Handler interface.
func (b Blowback) Name() string { return "blowback" }
func New() *Blowback { return &Blowback{} }