1 from __future__
import print_function
2 import socket
, select
, os
, threading
, hashlib
, rocksock
, time
, sys
, codecs
4 PY3
= sys
.version_info
[0] == 3
15 return codecs
.encode(os
.urandom(NONCE_LEN
), 'hex')
18 return _b(hashlib
.sha256(str).hexdigest(), 'utf-8')
20 def _format_addr(addr
):
23 return b
"%s:%d"%(ip
, port
)
26 return _b(time
.strftime('[%Y-%m-%d %H:%M:%S] ', time
.localtime(time
.time())), 'utf-8')
29 def __init__(self
, fds
, fdc
, caddr
):
32 self
.done
= threading
.Event()
35 if self
.fdc
: self
.fdc
.close()
36 if self
.fds
: self
.fds
.close()
39 def _threadfunc(self
):
41 a
,b
,c
= select
.select([self
.fds
, self
.fdc
], [], [])
58 self
.t
= threading
.Thread(target
=self
._threadfunc
)
62 return self
.done
.is_set()
67 def __init__(self
, secret
, upstream_ip
, upstream_port
, localserv_ip
, localserv_port
):
69 self
.localserv_ip
= localserv_ip
70 self
.localserv_port
= localserv_port
71 self
.upstream_ip
= upstream_ip
72 self
.upstream_port
= upstream_port
73 self
.controlsock
= None
74 self
.next_csock
= None
77 def _setup_sock(self
, cmd
):
78 sock
= rocksock
.Rocksock(host
=self
.upstream_ip
, port
=self
.upstream_port
)
80 nonce
= sock
.recv(NONCE_LEN
*2 + 1).rstrip(b
'\n')
81 sock
.send(_hash(cmd
+ self
.secret
+ nonce
) + b
'\n')
85 self
.controlsock
= self
._setup
_sock
(b
'adm')
86 self
.next_csock
= self
._setup
_sock
(b
'skt')
91 while i
< len(self
.threads
):
92 if self
.threads
[i
].finished():
93 self
.threads
[i
].reap()
98 l
= self
.controlsock
.recvline()
99 print(_timestamp() + l
.rstrip(b
'\n'))
100 if l
.startswith(b
'CONN:'):
101 addr
=l
.rstrip(b
'\n').split(b
':')[1]
102 local_conn
= rocksock
.Rocksock(host
=self
.localserv_ip
, port
=self
.localserv_port
)
104 thread
= Tunnel(local_conn
.sock
, self
.next_csock
.sock
, addr
)
106 self
.threads
.append(thread
)
107 self
.next_csock
= self
._setup
_sock
(b
'skt')
111 def _isnumericipv4(self
, ip
):
113 a
,b
,c
,d
= ip
.split('.')
114 if int(a
) < 256 and int(b
) < 256 and int(c
) < 256 and int(d
) < 256:
120 def _resolve(self
, host
, port
, want_v4
=True):
121 if self
._isnumericipv
4(host
):
122 return socket
.AF_INET
, (host
, port
)
123 for res
in socket
.getaddrinfo(host
, port
, \
124 socket
.AF_UNSPEC
, socket
.SOCK_STREAM
, 0, socket
.AI_PASSIVE
):
125 af
, socktype
, proto
, canonname
, sa
= res
126 if want_v4
and af
!= socket
.AF_INET
: continue
127 if af
!= socket
.AF_INET
and af
!= socket
.AF_INET6
: continue
132 def __init__(self
, secret
, upstream_listen_ip
, upstream_port
, client_listen_ip
, client_port
):
133 self
.up_port
= upstream_port
134 self
.up_ip
= upstream_listen_ip
135 self
.client_port
= client_port
136 self
.client_ip
= client_listen_ip
141 self
.control_socket
= None
142 self
.next_upstream_socket
= None
143 self
.hashlen
= len(_hash(b
''))
145 def _setup_listen_socket(self
, listenip
, port
):
146 af
, sa
= self
._resolve
(listenip
, port
)
147 s
= socket
.socket(af
, socket
.SOCK_STREAM
)
148 s
.setsockopt(socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
, 1)
149 s
.bind((sa
[0], sa
[1]))
154 self
.su
= self
._setup
_listen
_socket
(self
.up_ip
, self
.up_port
)
155 self
.sc
= self
._setup
_listen
_socket
(self
.client_ip
, self
.client_port
)
157 def wait_conn_up(self
):
158 conn
, addr
= self
.su
.accept()
160 print(_timestamp() + b
"CONN: %s (nonce: %s) ... "%(_format_addr(addr
), nonce
), end
='')
161 conn
.send(nonce
+ b
'\n')
162 cmd
= conn
.recv(1 + self
.hashlen
).rstrip(b
'\n')
163 if cmd
== _hash(b
'adm' + self
.secret
+ nonce
):
164 if self
.control_socket
:
165 self
.control_socket
.close()
166 self
.control_socket
= conn
168 elif cmd
== _hash(b
'skt' + self
.secret
+ nonce
):
170 if not self
.control_socket
:
173 self
.next_upstream_socket
= conn
178 def wait_conn_client(self
):
179 conn
, addr
= self
.sc
.accept()
180 self
.control_socket
.send(b
"CONN:%s\n"%_format_addr(addr
))
181 thread
= Tunnel(self
.next_upstream_socket
, conn
, addr
)
183 self
.threads
.append(thread
)
184 self
.next_upstream_socket
= None
189 while i
< len(self
.threads
):
190 if self
.threads
[i
].finished():
191 self
.threads
[i
].reap()
195 if not self
.control_socket
:
197 if not self
.next_upstream_socket
:
199 if self
.control_socket
and self
.next_upstream_socket
:
200 a
,b
,c
= select
.select([self
.sc
, self
.control_socket
, ], [], [])
201 if self
.control_socket
in a
:
202 print("lost control socket")
203 self
.control_socket
.close()
204 self
.control_socket
= None
206 if self
.next_upstream_socket
in a
:
207 print("lost spare upstream socket")
208 self
.next_upstream_socket
.close()
209 self
.next_upstream_socket
= None
212 self
.wait_conn_client()
215 if __name__
== "__main__":
220 "If you have access to a server with public IP and unfiltered ports\n"
221 "you can run NAT Tunnel (NT) server on the server, and NT client\n"
222 "on your box behind NAT.\n"
223 "the server requires 2 open ports: one for communication with the\n"
224 "NT client (--admin), the other for regular clients to connect to\n"
225 "(--public: this is the port you want your users to use).\n"
227 "The NT client opens a connection to the server's admin ip/port.\n"
228 "As soon as the server receives a new connection, it signals the\n"
229 "NT client, which then creates a new tunnel connection to the\n"
230 "server, which is then connected to the desired service on the\n"
231 "NT client's side (--local)\n"
233 "The connection between NT Client and NT Server on the admin\n"
234 "interface is protected by a shared secret against unauthorized use.\n"
235 "An adversary who can intercept packets could crack the secret\n"
236 "if it's of insufficient complexity. At least 10 random\n"
237 "characters and numbers are recommended.\n"
240 "You have a HTTP server listening on your local machine on port 80.\n"
241 "You want to make it available on your cloud server/VPS/etc's public\n"
243 "We use port 8000 on the cloud server for the control channel.\n"
246 " %s --mode server --secret s3cretP4ss --public 0.0.0.0:7000 --admin 0.0.0.0:8000\n"
248 " %s --mode client --secret s3cretP4ss --local localhost:80 --admin example.com:8000\n"
249 ) % (sys
.argv
[0], sys
.argv
[0])
250 if len(sys
.argv
) < 2 or (sys
.argv
[1] == '-h' or sys
.argv
[1] == '--help'):
252 parser
= argparse
.ArgumentParser(description
='')
253 parser
.add_argument('--secret', help='shared secret between natserver/client', type=str, default
='', required
=True)
254 parser
.add_argument('--mode', help='work mode: server or client', type=str, default
='server', required
=True)
255 parser
.add_argument('--public', help='(server only) ip:port where we will listen for regular clients', type=str, default
='0.0.0.0:8080', required
=False)
256 parser
.add_argument('--local', help='(client only) ip:port of the local target service', type=str, default
="localhost:80", required
=False)
257 parser
.add_argument('--admin', help='ip:port tuple for admin/upstream/control connection', type=str, default
="0.0.0.0:8081", required
=False)
258 args
= parser
.parse_args()
259 adminip
, adminport
= args
.admin
.split(':')
260 if args
.mode
== 'server':
261 clientip
, clientport
= args
.public
.split(':')
262 srv
= NATSrv(_b(args
.secret
, 'utf-8'), adminip
, int(adminport
), clientip
, int(clientport
))
266 localip
, localport
= args
.local
.split(':')
267 cl
= NATClient(_b(args
.secret
, 'utf-8'), adminip
, int(adminport
), localip
, int(localport
))