-
Notifications
You must be signed in to change notification settings - Fork 0
/
mux.go
167 lines (144 loc) · 4.84 KB
/
mux.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package main
import (
"net"
"strings"
"sync"
)
// match a listen pattern to an address string of the form HOST:PORT
func patternMatches(pattern string, addr net.Addr) bool {
if pattern == "*" {
return true
}
if strings.HasPrefix(pattern, ":") && strings.HasSuffix(addr.String(), pattern) {
return true
}
return false
}
// mux dispatches network connections to listeners according to patterns
type mux struct {
mu sync.Mutex
tcpHandlers []*tcpListener
udpHandlers []*udpMuxEntry
}
// tcpHandlerFunc is a function that receives TCP connections
type tcpHandlerFunc func(conn net.Conn)
// udpHandlerFunc is a function that receives UDP packets. Each call to w.Write
// will send a UDP packet back to the subprocess that looks as if it comes from
// the destination to which the original packet was sent. No matter what you put in
// the source or destination address, the
type udpHandlerFunc func(w udpResponder, packet *udpPacket)
// udpMuxEntry is a pattern and corresponding handler, for use in the mux table for the udp stack
type udpMuxEntry struct {
handler udpHandlerFunc
pattern string
}
// ListenTCP returns a net.Listener that intercepts connections according to a filter pattern.
//
// Pattern can a hostname, a :port, a hostname:port, or "*" for everything". For example:
// - "example.com"
// - "example.com:80"
// - ":80"
// - "*"
//
// Later this will be like net.ListenTCP
func (s *mux) ListenTCP(pattern string) net.Listener {
s.mu.Lock()
defer s.mu.Unlock()
listener := tcpListener{pattern: pattern, connections: make(chan net.Conn, 64)}
s.tcpHandlers = append(s.tcpHandlers, &listener)
return &listener
}
// HandleTCP calls the handler each time a new connection is intercepted mattching the
// given filter pattern.
//
// Pattern can a hostname, a :port, a hostname:port, or "*" for everything". For example:
// - "example.com"
// - "example.com:80"
// - ":80"
// - "*"
func (s *mux) HandleTCP(pattern string, handler tcpHandlerFunc) {
l := s.ListenTCP(pattern)
go func() {
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
verbosef("accept returned errror: %v, exiting HandleFunc(%v)", err, pattern)
return
}
go handler(conn)
}
}()
}
// HandleUDP registers a handler for UDP packets according to destination IP and/or por
//
// Pattern can a hostname, a port, a hostname:port, or "*" for everything". Ports are prepended
// with colons. Valid patterns are:
// - "example.com"
// - "example.com:80"
// - ":80"
// - "*"
//
// Later this will be like net.Listen
func (s *mux) HandleUDP(pattern string, handler udpHandlerFunc) {
s.mu.Lock()
defer s.mu.Unlock()
s.udpHandlers = append(s.udpHandlers, &udpMuxEntry{pattern: pattern, handler: handler})
}
// notifyListeners is called when a new stream is created. It finds the first listener
// that will accept the given stream. It never blocks.
func (s *mux) notifyTCP(stream net.Conn) {
s.mu.Lock()
defer s.mu.Unlock()
for _, listener := range s.tcpHandlers {
if patternMatches(listener.pattern, stream.LocalAddr()) {
listener.connections <- stream
return
}
}
verbosef("nobody listening for tcp to %v, dropping", stream.LocalAddr())
}
// notifyUDP is called when a new packet arrives. It finds the first handler
// with a pattern that matches the packet and delivers the packet to it
func (s *mux) notifyUDP(w udpResponder, packet *udpPacket) {
s.mu.Lock()
defer s.mu.Unlock()
for _, entry := range s.udpHandlers {
if patternMatches(entry.pattern, packet.dst) {
go entry.handler(w, packet)
return
}
}
verbosef("nobody listening for udp to %v, dropping!", packet.dst)
}
// udpResponder is the interface for writing back UDP packets
type udpResponder interface {
// write a UDP packet back to the subprocess
Write(payload []byte) (n int, err error)
}
// tcpListener implements net.Listener for connections dispatched by a mux
type tcpListener struct {
pattern string
connections chan net.Conn // the tcpStack sends streams here when they are created and they match the pattern above
}
// Accept accepts an intercepted connection. Later this will implement net.Listener.Accept
func (l *tcpListener) Accept() (net.Conn, error) {
stream := <-l.connections
if stream == nil {
// this means the channel is closed, which means the tcpStack was shut down
return nil, net.ErrClosed
}
return stream, nil
}
// for net.Listener interface
func (l *tcpListener) Close() error {
// TODO: unregister from the stack, then close(l.connections)
verbose("tcpListener.Close() not implemented, ignoring")
return nil
}
// for net.Listener interface, returns our side of the connection
func (l *tcpListener) Addr() net.Addr {
verbose("tcpListener.Addr() was called, returning bogus address 0.0.0.0:0")
// in truth we do not have a real address -- we listen for anything going anywhere
return &net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 0}
}