Skip to content

Commit 8f68ffa

Browse files
authored
Add relay command to allow tunneling of dns-sd over TCP
New cmdline tool I made (based on cmd/debug) to relay dns-sd to docker containers on darwin to help deal with some limitations of docker desktop (docker/for-mac#68).
2 parents 19335e8 + 5d06ef7 commit 8f68ffa

2 files changed

Lines changed: 266 additions & 0 deletions

File tree

cmd/relay/main.go

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
// Relay packets to/from a tunneled interface
2+
// Intended for use with Docker Deskstop on MacOSX which doesn't properly handle this
3+
package main
4+
5+
import (
6+
"context"
7+
"encoding/binary"
8+
"flag"
9+
"fmt"
10+
"log"
11+
"net"
12+
"os"
13+
"os/signal"
14+
"strconv"
15+
"time"
16+
17+
"github.com/brutella/dnssd"
18+
)
19+
20+
const (
21+
SERVER_TYPE = "tcp"
22+
MAXDGRAMSZ = 8192
23+
TIMEFORMAT = "15:04:05.000"
24+
)
25+
26+
var (
27+
mode = flag.String("mode", "server", "mode (client or server")
28+
debug = flag.Bool("d", false, "enable debug prints")
29+
dump = flag.Bool("dump", false, "dump all traffic")
30+
serverIp = flag.String("serverIp", "127.0.0.1", "IP to listen on for new clients")
31+
relayHost = flag.String("host", "host.docker.internal", "host the client mode connects to")
32+
relayPort = flag.Int("port", 53535, "port connected to for relay traffic")
33+
relayFPort = flag.Int("from", 35353, "port to send relayed messages from")
34+
dnssdAddr = flag.String("dnssdAddr", "224.0.0.251", "multicast address to use for dns-sd")
35+
dnssdPort = flag.Int("dnssdPort", 5353, "port to use for mDNS dns-sd")
36+
v = func(string, ...interface{}) {}
37+
)
38+
39+
func flags() {
40+
flag.Parse()
41+
if *debug {
42+
v = log.Printf
43+
}
44+
}
45+
46+
// provide a reliable data-gram service over TCP
47+
type TcpDGC struct {
48+
connection net.Conn
49+
}
50+
51+
func (c *TcpDGC) readN(msg []byte, total uint32) error {
52+
totalread := uint32(0)
53+
54+
for totalread < total {
55+
n, err := c.connection.Read(msg[totalread:total])
56+
if err != nil {
57+
v("readN: short read error")
58+
return err
59+
}
60+
if n < 0 {
61+
v("n returned from read is negative")
62+
return fmt.Errorf("negative return in readN")
63+
}
64+
totalread += uint32(n)
65+
}
66+
return nil
67+
}
68+
69+
func (c *TcpDGC) Send(msg []byte) (int, error) {
70+
hdr := make([]byte, 4)
71+
binary.LittleEndian.PutUint32(hdr, uint32(len(msg)))
72+
actualBuf := append(hdr[:], msg[:]...)
73+
v("Sending msg size %x", uint32(len(actualBuf)))
74+
n, err := c.connection.Write(actualBuf)
75+
76+
return (n - 4), err
77+
}
78+
79+
func (c *TcpDGC) Recv(msg []byte) (uint32, error) {
80+
hdr := make([]byte, 4)
81+
err := c.readN(hdr, 4)
82+
if err != nil {
83+
return 0, err
84+
}
85+
86+
buflen := binary.LittleEndian.Uint32(hdr)
87+
if buflen > uint32(len(msg)) {
88+
return 0, fmt.Errorf("buffer not big enough to store datagram size %x", buflen)
89+
}
90+
v("receiving message size %x", buflen)
91+
err = c.readN(msg, buflen)
92+
if err != nil {
93+
return 0, err
94+
}
95+
96+
return buflen, err
97+
}
98+
99+
func relay(connection net.Conn, tunChan chan *dnssd.Request, ctlChan chan int) {
100+
c := &TcpDGC{connection}
101+
102+
for {
103+
select {
104+
case <-ctlChan:
105+
v("Received request to stop in relay")
106+
os.Exit(1)
107+
case r := <-tunChan:
108+
if r.From().Port == *relayFPort {
109+
// Drop multicasts from me
110+
continue
111+
}
112+
m := r.Raw()
113+
rm, err := m.Pack()
114+
if err != nil {
115+
log.Printf("repacking DNS msg: %s\n", err.Error())
116+
return
117+
}
118+
119+
n, err := c.Send(rm)
120+
if err != nil {
121+
log.Printf("Forwarding repacked DNS msg: %s\n", err.Error())
122+
return
123+
}
124+
if n < len(rm) {
125+
log.Printf("Forwarding repacked DNS msg returned partial send of %d/%d bytes\n", n, len(rm))
126+
return
127+
}
128+
}
129+
}
130+
}
131+
132+
func relayToPeer(connection net.Conn, stop chan os.Signal) {
133+
tunChan := make(chan *dnssd.Request, 64)
134+
ctlChan := make(chan int, 1)
135+
136+
v("Relaying…\n")
137+
138+
go relay(connection, tunChan, ctlChan)
139+
140+
fn := func(req *dnssd.Request) {
141+
tunChan <- req
142+
if *dump {
143+
log.Printf("-------------------------------------------\n")
144+
log.Printf("%s\n%v\n", time.Now().Format(TIMEFORMAT), req)
145+
}
146+
}
147+
148+
ctx, cancel := context.WithCancel(context.Background())
149+
defer cancel()
150+
151+
if rsp, err := dnssd.NewResponder(); err != nil {
152+
log.Println(err)
153+
} else {
154+
rsp.Debug(ctx, fn)
155+
156+
<-stop
157+
158+
v("Interrupt detected")
159+
cancel()
160+
ctlChan <- 1
161+
os.Exit(1)
162+
}
163+
}
164+
165+
func server(stop chan os.Signal) {
166+
v("Server Running...")
167+
168+
server, err := net.Listen(SERVER_TYPE, *serverIp+":"+strconv.Itoa(*relayPort))
169+
if err != nil {
170+
log.Println("error listening:", err.Error())
171+
os.Exit(1)
172+
}
173+
defer server.Close()
174+
v("Listening on " + *serverIp + ":" + strconv.Itoa(*relayPort))
175+
v("Waiting for client...")
176+
go func() {
177+
<-stop
178+
server.Close()
179+
os.Exit(1)
180+
}()
181+
for {
182+
connection, err := server.Accept()
183+
if err != nil {
184+
log.Printf("error accepting: %s\n", err.Error())
185+
os.Exit(1)
186+
}
187+
log.Printf("new client connected %s\n", connection.RemoteAddr().String())
188+
go relayToPeer(connection, stop)
189+
go relayFromPeer(connection)
190+
}
191+
}
192+
193+
func relayFromPeer(connection net.Conn) {
194+
maddr, err := net.ResolveUDPAddr("udp", *dnssdAddr+":"+strconv.Itoa(*dnssdPort))
195+
if err != nil {
196+
log.Fatal(err)
197+
}
198+
myaddr, err := net.ResolveUDPAddr("udp", ":"+strconv.Itoa(*relayFPort))
199+
if err != nil {
200+
log.Fatal(err)
201+
}
202+
mc, err := net.DialUDP("udp", myaddr, maddr)
203+
if err != nil {
204+
log.Fatal(err)
205+
}
206+
defer mc.Close()
207+
208+
c := &TcpDGC{connection}
209+
for {
210+
buffer := make([]byte, MAXDGRAMSZ)
211+
212+
mLen, err := c.Recv(buffer)
213+
if err != nil {
214+
log.Printf("error reading from %s: %s\n", connection.RemoteAddr().String(), err.Error())
215+
return
216+
}
217+
218+
n, err := mc.Write(buffer[:mLen])
219+
if err != nil {
220+
log.Printf("error forwarding message as %s: %s\n", mc.LocalAddr().String(), err.Error())
221+
return
222+
}
223+
if uint32(n) < mLen {
224+
log.Printf("short write as %s: %d\n", mc.LocalAddr().String(), n)
225+
}
226+
}
227+
}
228+
229+
func client(stop chan os.Signal) {
230+
connection, err := net.Dial(SERVER_TYPE, *relayHost+":"+strconv.Itoa(*relayPort))
231+
if err != nil {
232+
log.Fatal(err)
233+
}
234+
defer connection.Close()
235+
go func() {
236+
<-stop
237+
connection.Close()
238+
os.Exit(1)
239+
}()
240+
241+
go relayToPeer(connection, stop)
242+
relayFromPeer(connection)
243+
}
244+
245+
func main() {
246+
flags()
247+
stop := make(chan os.Signal, 1)
248+
signal.Notify(stop, os.Interrupt)
249+
250+
if *mode == "server" {
251+
v("starting server")
252+
server(stop)
253+
}
254+
if *mode == "client" {
255+
v("starting client")
256+
client(stop)
257+
}
258+
}

mdns.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ func (r Request) String() string {
6363
return fmt.Sprintf("%s@%s\n%v", r.from.IP, r.IfaceName(), r.msg)
6464
}
6565

66+
func (r Request) Raw() *dns.Msg {
67+
return r.msg
68+
}
69+
70+
func (r Request) From() *net.UDPAddr {
71+
return r.from
72+
}
73+
6674
// IfaceName returns the name of the network interface where the request was received.
6775
// If the network interface is unknown, the string "?" is returned.
6876
func (r Request) IfaceName() string {

0 commit comments

Comments
 (0)