Skip to content

Commit e413e04

Browse files
author
Todd Blose
committed
update README and socks implementation
1 parent 22c9b14 commit e413e04

File tree

1 file changed

+311
-46
lines changed

1 file changed

+311
-46
lines changed
Lines changed: 311 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,336 @@
1-
// package interceptor which intercepts and forwards traffic between
2-
// a VPN tun interface (typically established via Android VpnService)
3-
// and Lantern
1+
// socks implements a SOCKS server that intercepts local host connections
2+
// and forwards them through Lantern's HTTP proxy
43
package interceptor
54

6-
// #cgo LDFLAGS: -I/usr/include
7-
85
import (
6+
"crypto/tls"
7+
"errors"
8+
"io"
9+
"io/ioutil"
10+
"net"
11+
"net/http"
12+
"net/url"
13+
"sync"
14+
"time"
15+
916
"github.com/getlantern/golog"
1017
"github.com/getlantern/lantern-mobile/protected"
18+
socks "github.com/getlantern/lantern-mobile/socks"
1119
)
1220

1321
const (
14-
_READ_BUF = 1024
22+
udpgwServer = "104.131.157.209:7300"
1523
)
1624

1725
var (
18-
log = golog.LoggerFor("lantern-android.interceptor")
26+
DIAL_TIMEOUT = 15 * time.Second
27+
log = golog.LoggerFor("lantern-android.interceptor")
1928
)
2029

21-
// Here is the procedure for intercepting and forwarding a packet:
22-
// - lantern is started after the VPN connection is established
23-
// - Configure initializes the packet interception tool
24-
// - Process intercepts an incoming packet as a raw byte array
25-
// and decodes it using gopacket
26-
// - if its a TCP packet and non-masquerade check, we check
27-
// the connections map for an existing TCP stream using the
28-
// 5-tuple as key
29-
// - if a connection does not exist, we open a new protected
30-
// connection and send an HTTP CONNECT to Lantern for the
31-
// desired destination to begin tunneling the communication
32-
// - the packet is forwarded along the corresponding tunnel
33-
34-
type Interceptor struct {
35-
// Service for excluding TCP connections from VpnService
30+
type Tunneler struct {
31+
socksAddr string
32+
httpAddr string
3633
protector protected.SocketProtector
3734

38-
// callback to write packet back over tunnel
39-
writePacket func([]byte)
40-
// if request corresponds to a masquerade check
41-
isMasquerade func(string) bool
42-
// Address Lantern local proxy is running on
43-
httpAddr string
44-
socksAddr string
35+
mutex *sync.Mutex
36+
isClosed bool
37+
}
38+
39+
type dialResult struct {
40+
forwardConn net.Conn
41+
err error
42+
}
43+
44+
type SocksProxy struct {
45+
tunneler Tunneler
46+
listener *socks.SocksListener
47+
isMasquerade func(string) bool
48+
serveWaitGroup *sync.WaitGroup
49+
openConns *Conns
50+
conns map[string]net.Conn
51+
stopListeningBroadcast chan struct{}
52+
}
53+
54+
type Conns struct {
55+
mutex sync.Mutex
56+
isClosed bool
57+
conns map[net.Conn]bool
58+
}
59+
60+
func (conns *Conns) Reset() {
61+
conns.mutex.Lock()
62+
defer conns.mutex.Unlock()
63+
conns.isClosed = false
64+
conns.conns = make(map[net.Conn]bool)
65+
}
4566

46-
// whether or not to print all incoming packets
47-
logPackets bool
67+
func (conns *Conns) Add(conn net.Conn) bool {
68+
conns.mutex.Lock()
69+
defer conns.mutex.Unlock()
70+
if conns.isClosed {
71+
return false
72+
}
73+
if conns.conns == nil {
74+
conns.conns = make(map[net.Conn]bool)
75+
}
76+
conns.conns[conn] = true
77+
return true
4878
}
4979

50-
func New(protector protected.SocketProtector, logPackets bool,
51-
httpAddr string,
52-
socksAddr string,
53-
writePacket func([]byte), isMasquerade func(string) bool) *Interceptor {
54-
i := &Interceptor{
55-
protector: protector,
56-
httpAddr: httpAddr,
57-
socksAddr: socksAddr,
58-
logPackets: logPackets,
59-
writePacket: writePacket,
60-
isMasquerade: isMasquerade,
80+
func (conns *Conns) Remove(conn net.Conn) {
81+
conns.mutex.Lock()
82+
defer conns.mutex.Unlock()
83+
delete(conns.conns, conn)
84+
}
85+
86+
func (conns *Conns) CloseAll() {
87+
conns.mutex.Lock()
88+
defer conns.mutex.Unlock()
89+
conns.isClosed = true
90+
for conn, _ := range conns.conns {
91+
conn.Close()
6192
}
93+
conns.conns = make(map[net.Conn]bool)
94+
}
6295

63-
_, err := NewSocksProxy(i)
96+
func Relay(localConn, remoteConn net.Conn) {
97+
var wg sync.WaitGroup
98+
wg.Add(2)
99+
100+
go func() {
101+
io.Copy(localConn, remoteConn)
102+
wg.Done()
103+
}()
104+
go func() {
105+
io.Copy(remoteConn, localConn)
106+
wg.Done()
107+
}()
108+
109+
wg.Wait()
110+
}
111+
112+
// NewSocksProxy initializes a new SOCKS server. It begins listening for
113+
// connections, starts a goroutine that runs an accept loop, and returns
114+
// leaving the accept loop running.
115+
func NewSocksProxy(protector protected.SocketProtector, socksAddr, httpAddr string, isMasquerade func(string) bool) (proxy *SocksProxy, err error) {
116+
listener, err := socks.ListenSocks(
117+
"tcp", socksAddr)
64118
if err != nil {
65-
log.Errorf("Error starting SOCKS proxy: %v", err)
119+
log.Errorf("Could not start SOCKS server: %v", err)
120+
return nil, err
121+
}
122+
proxy = &SocksProxy{
123+
tunneler: Tunneler{
124+
mutex: new(sync.Mutex),
125+
isClosed: false,
126+
protector: protector,
127+
socksAddr: socksAddr,
128+
httpAddr: httpAddr,
129+
},
130+
isMasquerade: isMasquerade,
131+
listener: listener,
132+
serveWaitGroup: new(sync.WaitGroup),
133+
openConns: new(Conns),
134+
conns: map[string]net.Conn{},
135+
stopListeningBroadcast: make(chan struct{}),
136+
}
137+
proxy.serveWaitGroup.Add(1)
138+
go proxy.serve()
139+
log.Debugf("SOCKS proxy now listening on port: %v",
140+
proxy.listener.Addr().(*net.TCPAddr).Port)
141+
return proxy, nil
142+
}
143+
144+
// Close terminates the listener and waits for the accept loop
145+
// goroutine to complete.
146+
func (proxy *SocksProxy) Close() {
147+
close(proxy.stopListeningBroadcast)
148+
proxy.listener.Close()
149+
proxy.serveWaitGroup.Wait()
150+
proxy.openConns.CloseAll()
151+
}
152+
153+
func (tunnel *Tunneler) Dial(addr string, localConn net.Conn) (net.Conn, error) {
154+
155+
tunnel.mutex.Lock()
156+
isClosed := tunnel.isClosed
157+
tunnel.mutex.Unlock()
158+
159+
if isClosed {
160+
return nil, errors.New("tunnel is closed")
161+
}
162+
163+
conn, err := protected.New(tunnel.protector, tunnel.httpAddr)
164+
if err != nil {
165+
log.Errorf("Error creating protected connection: %v", err)
166+
return nil, err
167+
}
168+
169+
_, port, err := protected.SplitHostPort(addr)
170+
if err != nil {
171+
conn.Close()
172+
return nil, err
173+
}
174+
175+
resultCh := make(chan *dialResult, 2)
176+
time.AfterFunc(DIAL_TIMEOUT, func() {
177+
resultCh <- &dialResult{nil,
178+
errors.New("dial timoue to tunnel")}
179+
})
180+
181+
go func() {
182+
183+
log.Debugf("Creating CONNECT request to %s", addr)
184+
185+
scheme := "http"
186+
if port == 443 {
187+
scheme = "https"
188+
}
189+
190+
connReq := &http.Request{
191+
Method: "CONNECT",
192+
URL: &url.URL{Host: addr, Scheme: scheme},
193+
Host: addr,
194+
Header: make(http.Header),
195+
}
196+
197+
log.Debugf("Tunneling a new request to Lantern: %s", addr)
198+
client := &http.Client{
199+
Transport: &http.Transport{
200+
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
201+
Dial: func(netw, addr string) (net.Conn, error) {
202+
return conn.Dial()
203+
},
204+
ResponseHeaderTimeout: time.Second * 2,
205+
},
206+
}
207+
208+
resp, err := client.Do(connReq)
209+
if err != nil {
210+
log.Errorf("Error reading HTTP CONNECT request response: %v", err)
211+
conn.Close()
212+
resultCh <- &dialResult{nil, err}
213+
return
214+
}
215+
defer resp.Body.Close()
216+
217+
if resp.StatusCode != 200 {
218+
resp, _ := ioutil.ReadAll(resp.Body)
219+
conn.Close()
220+
resultCh <- &dialResult{nil, errors.New("proxy refused connection" + string(resp))}
221+
} else {
222+
log.Debugf("Successfully established an HTTP tunnel with remote end-point: %s", addr)
223+
resultCh <- &dialResult{conn, nil}
224+
}
225+
}()
226+
227+
result := <-resultCh
228+
if result.err != nil {
229+
log.Errorf("Error dialing new request: %v", result.err)
230+
return nil, result.err
231+
}
232+
return result.forwardConn, nil
233+
}
234+
235+
func (proxy *SocksProxy) httpConnectHandler(localConn *socks.SocksConn) (err error) {
236+
237+
defer localConn.Close()
238+
defer proxy.openConns.Remove(localConn)
239+
proxy.openConns.Add(localConn)
240+
241+
if localConn.Req.Target == udpgwServer {
242+
return proxy.directHandler(localConn)
243+
}
244+
245+
if proxy.isMasquerade(localConn.Req.Target) {
246+
log.Debugf("Masquerade check...")
66247
return nil
67248
}
68249

69-
log.Debugf("Configured interceptor; Ready to consume packets!")
70-
return i
250+
remoteConn, err := proxy.tunneler.Dial(localConn.Req.Target, localConn)
251+
if err != nil {
252+
log.Errorf("Error tunneling request: %v", err)
253+
return err
254+
}
255+
defer remoteConn.Close()
256+
err = localConn.Grant(&net.TCPAddr{IP: net.ParseIP("0.0.0.0"), Port: 0})
257+
if err != nil {
258+
log.Errorf("Error granting local connection: %v", err)
259+
return err
260+
}
261+
Relay(localConn, remoteConn)
262+
return nil
263+
}
264+
265+
func (proxy *SocksProxy) directHandler(localConn *socks.SocksConn) (err error) {
266+
267+
host, port, err := protected.SplitHostPort(localConn.Req.Target)
268+
if err != nil {
269+
log.Errorf("Could not extract IP Address: %v", err)
270+
return err
271+
}
272+
273+
conn, err := protected.New(proxy.tunneler.protector, localConn.Req.Target)
274+
if err != nil {
275+
log.Errorf("Error creating protected connection: %v", err)
276+
return err
277+
}
278+
defer conn.Close()
279+
log.Debugf("Connecting to %s:%d", host, port)
280+
281+
remoteConn, err := conn.Dial()
282+
if err != nil {
283+
log.Errorf("Error tunneling request: %v", err)
284+
return err
285+
}
286+
defer remoteConn.Close()
287+
addr, err := conn.Addr()
288+
if err != nil {
289+
log.Errorf("Could not resolve address: %v", err)
290+
return err
291+
}
292+
293+
err = localConn.Grant(addr)
294+
if err != nil {
295+
log.Errorf("Error granting access to connection: %v", err)
296+
return err
297+
}
298+
Relay(localConn, remoteConn)
299+
return nil
300+
}
301+
302+
func (proxy *SocksProxy) serve() {
303+
defer proxy.listener.Close()
304+
defer proxy.serveWaitGroup.Done()
305+
loop:
306+
for {
307+
// Note: will be interrupted by listener.Close() call made by proxy.Close()
308+
socksConnection, err := proxy.listener.AcceptSocks()
309+
// Can't check for the exact error that Close() will cause in Accept(),
310+
// (see: https://code.google.com/p/go/issues/detail?id=4373). So using an
311+
// explicit stop signal to stop gracefully.
312+
select {
313+
case <-proxy.stopListeningBroadcast:
314+
break loop
315+
default:
316+
}
317+
if err != nil {
318+
log.Errorf("SOCKS proxy accept error: %v", err)
319+
if e, ok := err.(net.Error); ok && e.Temporary() {
320+
// Temporary error, keep running
321+
continue
322+
}
323+
// Fatal error, stop the proxy
324+
log.Fatalf("Fatal component failure: %v", err)
325+
break loop
326+
}
327+
go func() {
328+
log.Debugf("Got a new connection: %v", socksConnection)
329+
err := proxy.httpConnectHandler(socksConnection)
330+
if err != nil {
331+
log.Errorf("%v", err)
332+
}
333+
}()
334+
}
335+
log.Debugf("SOCKS proxy stopped")
71336
}

0 commit comments

Comments
 (0)