Skip to content

Commit 88ad3ca

Browse files
author
Todd Blose
committed
use socks proxy
1 parent 5b44be1 commit 88ad3ca

File tree

1 file changed

+239
-0
lines changed
  • src/github.com/getlantern/lantern-mobile/interceptor

1 file changed

+239
-0
lines changed
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/*
2+
* Copyright (c) 2015, Psiphon Inc.
3+
* All rights reserved.
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*
18+
*/
19+
20+
package interceptor
21+
22+
import (
23+
"io"
24+
"net"
25+
"os"
26+
"sync"
27+
"syscall"
28+
29+
"github.com/getlantern/lantern-mobile/protected"
30+
31+
socks "github.com/Psiphon-Inc/goptlib"
32+
)
33+
34+
// SocksProxy is a SOCKS server that accepts local host connections
35+
// and, for each connection, establishes a port forward through
36+
// the tunnel SSH client and relays traffic through the port
37+
// forward.
38+
type SocksProxy struct {
39+
interceptor *Interceptor
40+
listener *socks.SocksListener
41+
serveWaitGroup *sync.WaitGroup
42+
openConns *Conns
43+
conns map[string]net.Conn
44+
stopListeningBroadcast chan struct{}
45+
}
46+
47+
type Conns struct {
48+
mutex sync.Mutex
49+
isClosed bool
50+
conns map[net.Conn]bool
51+
}
52+
53+
func (conns *Conns) Reset() {
54+
conns.mutex.Lock()
55+
defer conns.mutex.Unlock()
56+
conns.isClosed = false
57+
conns.conns = make(map[net.Conn]bool)
58+
}
59+
60+
func (conns *Conns) Add(conn net.Conn) bool {
61+
conns.mutex.Lock()
62+
defer conns.mutex.Unlock()
63+
if conns.isClosed {
64+
return false
65+
}
66+
if conns.conns == nil {
67+
conns.conns = make(map[net.Conn]bool)
68+
}
69+
conns.conns[conn] = true
70+
return true
71+
}
72+
73+
func (conns *Conns) Remove(conn net.Conn) {
74+
conns.mutex.Lock()
75+
defer conns.mutex.Unlock()
76+
delete(conns.conns, conn)
77+
}
78+
79+
func (conns *Conns) CloseAll() {
80+
conns.mutex.Lock()
81+
defer conns.mutex.Unlock()
82+
conns.isClosed = true
83+
for conn, _ := range conns.conns {
84+
conn.Close()
85+
}
86+
conns.conns = make(map[net.Conn]bool)
87+
}
88+
89+
func Relay(localConn, remoteConn net.Conn) {
90+
var wg sync.WaitGroup
91+
wg.Add(2)
92+
93+
go func() {
94+
io.Copy(localConn, remoteConn)
95+
wg.Done()
96+
}()
97+
go func() {
98+
io.Copy(remoteConn, localConn)
99+
wg.Done()
100+
}()
101+
102+
wg.Wait()
103+
}
104+
105+
// IsAddressInUseError returns true when the err is due to EADDRINUSE/WSAEADDRINUSE.
106+
func IsAddressInUseError(err error) bool {
107+
if err, ok := err.(*net.OpError); ok {
108+
if err, ok := err.Err.(*os.SyscallError); ok {
109+
if err.Err == syscall.EADDRINUSE {
110+
return true
111+
}
112+
// Special case for Windows (WSAEADDRINUSE = 10048)
113+
if errno, ok := err.Err.(syscall.Errno); ok {
114+
if 10048 == int(errno) {
115+
return true
116+
}
117+
}
118+
}
119+
}
120+
return false
121+
}
122+
123+
// NewSocksProxy initializes a new SOCKS server. It begins listening for
124+
// connections, starts a goroutine that runs an accept loop, and returns
125+
// leaving the accept loop running.
126+
func NewSocksProxy(i *Interceptor) (proxy *SocksProxy, err error) {
127+
listener, err := socks.ListenSocks(
128+
"tcp", i.socksAddr)
129+
if err != nil {
130+
if IsAddressInUseError(err) {
131+
log.Errorf("SOCKS proxy port in use")
132+
}
133+
return nil, err
134+
}
135+
proxy = &SocksProxy{
136+
interceptor: i,
137+
listener: listener,
138+
serveWaitGroup: new(sync.WaitGroup),
139+
openConns: new(Conns),
140+
conns: map[string]net.Conn{},
141+
stopListeningBroadcast: make(chan struct{}),
142+
}
143+
proxy.serveWaitGroup.Add(1)
144+
go proxy.serve()
145+
log.Debugf("SOCKS proxy now listening on port: %v",
146+
proxy.listener.Addr().(*net.TCPAddr).Port)
147+
return proxy, nil
148+
}
149+
150+
// Close terminates the listener and waits for the accept loop
151+
// goroutine to complete.
152+
func (proxy *SocksProxy) Close() {
153+
close(proxy.stopListeningBroadcast)
154+
proxy.listener.Close()
155+
proxy.serveWaitGroup.Wait()
156+
proxy.openConns.CloseAll()
157+
}
158+
159+
func (proxy *SocksProxy) socksConnectionHandler(localConn *socks.SocksConn) (err error) {
160+
defer localConn.Close()
161+
defer proxy.openConns.Remove(localConn)
162+
163+
//if proxy.conns[localConn.Req.Target] != nil {
164+
// existing connection
165+
// Relay(localConn, proxy.conns[localConn.Req.Target])
166+
// return nil
167+
//}
168+
169+
host, port, err := protected.SplitHostPort(localConn.Req.Target)
170+
if err != nil {
171+
log.Errorf("Could not extract IP Address: %v", err)
172+
return err
173+
}
174+
175+
conn, err := protected.New(proxy.interceptor.protector, localConn.Req.Target)
176+
if err != nil {
177+
log.Errorf("Error creating protected connection: %v", err)
178+
return err
179+
}
180+
defer conn.Close()
181+
log.Debugf("Connecting to %s:%d", host, port)
182+
183+
remoteConn, err := conn.Dial()
184+
if err != nil {
185+
log.Errorf("Error tunneling request: %v", err)
186+
return err
187+
}
188+
defer remoteConn.Close()
189+
addr, err := conn.Addr()
190+
if err != nil {
191+
log.Errorf("Could not resolve address: %v", err)
192+
return err
193+
}
194+
195+
//proxy.conns[localConn.Req.Target] = localConn
196+
err = localConn.Grant(addr)
197+
if err != nil {
198+
log.Errorf("Error granting access to connection: %v", err)
199+
return err
200+
}
201+
Relay(localConn, remoteConn)
202+
return nil
203+
}
204+
205+
func (proxy *SocksProxy) serve() {
206+
defer proxy.listener.Close()
207+
defer proxy.serveWaitGroup.Done()
208+
loop:
209+
for {
210+
// Note: will be interrupted by listener.Close() call made by proxy.Close()
211+
socksConnection, err := proxy.listener.AcceptSocks()
212+
// Can't check for the exact error that Close() will cause in Accept(),
213+
// (see: https://code.google.com/p/go/issues/detail?id=4373). So using an
214+
// explicit stop signal to stop gracefully.
215+
select {
216+
case <-proxy.stopListeningBroadcast:
217+
break loop
218+
default:
219+
}
220+
if err != nil {
221+
log.Errorf("SOCKS proxy accept error: %v", err)
222+
if e, ok := err.(net.Error); ok && e.Temporary() {
223+
// Temporary error, keep running
224+
continue
225+
}
226+
// Fatal error, stop the proxy
227+
log.Fatalf("Fatal component failure: %v", err)
228+
break loop
229+
}
230+
go func() {
231+
log.Debugf("Got a new connection: %v", socksConnection)
232+
err := proxy.socksConnectionHandler(socksConnection)
233+
if err != nil {
234+
log.Errorf("%v", err)
235+
}
236+
}()
237+
}
238+
log.Debugf("SOCKS proxy stopped")
239+
}

0 commit comments

Comments
 (0)