1 import socket
, select
, os
, threading
, hashlib
, rocksock
, time
, sys
6 return os
.urandom(NONCE_LEN
).encode('hex')
9 return hashlib
.sha256(str).hexdigest()
11 def _format_addr(addr
):
13 return "%s:%d"%(ip
, port
)
16 return time
.strftime('[%Y-%m-%d %H:%M:%S] ', time
.localtime(time
.time()))
19 def __init__(self
, fds
, fdc
, caddr
):
22 self
.done
= threading
.Event()
25 if self
.fdc
: self
.fdc
.close()
26 if self
.fds
: self
.fds
.close()
29 def _threadfunc(self
):
31 a
,b
,c
= select
.select([self
.fds
, self
.fdc
], [], [])
48 self
.t
= threading
.Thread(target
=self
._threadfunc
)
52 return self
.done
.is_set()
57 def __init__(self
, secret
, upstream_ip
, upstream_port
, localserv_ip
, localserv_port
):
59 self
.localserv_ip
= localserv_ip
60 self
.localserv_port
= localserv_port
61 self
.upstream_ip
= upstream_ip
62 self
.upstream_port
= upstream_port
63 self
.controlsock
= None
64 self
.next_csock
= None
67 def _setup_sock(self
, cmd
):
68 sock
= rocksock
.Rocksock(host
=self
.upstream_ip
, port
=self
.upstream_port
)
70 nonce
= sock
.recv(NONCE_LEN
*2 + 1).rstrip('\n')
71 sock
.send(_hash(cmd
+ self
.secret
+ nonce
) + '\n')
75 self
.controlsock
= self
._setup
_sock
('adm')
76 self
.next_csock
= self
._setup
_sock
('skt')
81 while i
< len(self
.threads
):
82 if self
.threads
[i
].finished():
83 self
.threads
[i
].reap()
88 l
= self
.controlsock
.recvline()
89 print(_timestamp() + l
.rstrip('\n'))
90 if l
.startswith('CONN:'):
91 addr
=l
.rstrip('\n').split(':')[1]
92 local_conn
= rocksock
.Rocksock(host
=self
.localserv_ip
, port
=self
.localserv_port
)
94 thread
= Tunnel(local_conn
.sock
, self
.next_csock
.sock
, addr
)
96 self
.threads
.append(thread
)
97 self
.next_csock
= self
._setup
_sock
('skt')
101 def _isnumericipv4(self
, ip
):
103 a
,b
,c
,d
= ip
.split('.')
104 if int(a
) < 256 and int(b
) < 256 and int(c
) < 256 and int(d
) < 256:
110 def _resolve(self
, host
, port
, want_v4
=True):
111 if self
._isnumericipv
4(host
):
112 return socket
.AF_INET
, (host
, port
)
113 for res
in socket
.getaddrinfo(host
, port
, \
114 socket
.AF_UNSPEC
, socket
.SOCK_STREAM
, 0, socket
.AI_PASSIVE
):
115 af
, socktype
, proto
, canonname
, sa
= res
116 if want_v4
and af
!= socket
.AF_INET
: continue
117 if af
!= socket
.AF_INET
and af
!= socket
.AF_INET6
: continue
122 def __init__(self
, secret
, upstream_listen_ip
, upstream_port
, client_listen_ip
, client_port
):
123 self
.up_port
= upstream_port
124 self
.up_ip
= upstream_listen_ip
125 self
.client_port
= client_port
126 self
.client_ip
= client_listen_ip
131 self
.control_socket
= None
132 self
.next_upstream_socket
= None
133 self
.hashlen
= len(_hash(""))
135 def _setup_listen_socket(self
, listenip
, port
):
136 af
, sa
= self
._resolve
(listenip
, port
)
137 s
= socket
.socket(af
, socket
.SOCK_STREAM
)
138 s
.setsockopt(socket
.SOL_SOCKET
, socket
.SO_REUSEADDR
, 1)
139 s
.bind((sa
[0], sa
[1]))
144 self
.su
= self
._setup
_listen
_socket
(self
.up_ip
, self
.up_port
)
145 self
.sc
= self
._setup
_listen
_socket
(self
.client_ip
, self
.client_port
)
147 def wait_conn_up(self
):
148 conn
, addr
= self
.su
.accept()
150 sys
.stdout
.write(_timestamp() + "CONN: %s (nonce: %s) ... "%(_format_addr(addr
), nonce
))
151 conn
.send(nonce
+ '\n')
152 cmd
= conn
.recv(1 + self
.hashlen
).rstrip('\n')
153 if cmd
== _hash('adm' + self
.secret
+ nonce
):
154 if self
.control_socket
:
155 self
.control_socket
.close()
156 self
.control_socket
= conn
158 elif cmd
== _hash('skt' + self
.secret
+ nonce
):
160 if not self
.control_socket
:
163 self
.next_upstream_socket
= conn
168 def wait_conn_client(self
):
169 conn
, addr
= self
.sc
.accept()
170 self
.control_socket
.send("CONN:%s\n"%_format_addr(addr
))
171 thread
= Tunnel(self
.next_upstream_socket
, conn
, addr
)
173 self
.threads
.append(thread
)
174 self
.next_upstream_socket
= None
179 while i
< len(self
.threads
):
180 if self
.threads
[i
].finished():
181 self
.threads
[i
].reap()
185 if not self
.control_socket
:
187 if not self
.next_upstream_socket
:
189 if self
.control_socket
and self
.next_upstream_socket
:
190 a
,b
,c
= select
.select([self
.sc
, self
.control_socket
, ], [], [])
191 if self
.control_socket
in a
:
192 print("lost control socket")
193 self
.control_socket
.close()
194 self
.control_socket
= None
196 if self
.next_upstream_socket
in a
:
197 print("lost spare upstream socket")
198 self
.next_upstream_socket
.close()
199 self
.next_upstream_socket
= None
202 self
.wait_conn_client()
205 if __name__
== "__main__":
210 "If you have access to a server with public IP and unfiltered ports\n"
211 "you can run NAT Tunnel (NT) server on the server, and NT client\n"
212 "on your box behind NAT.\n"
213 "the server requires 2 open ports: one for communication with the\n"
214 "NT client (--admin), the other for regular clients to connect to\n"
215 "(--public: this is the port you want your users to use).\n"
217 "The NT client opens a connection to the server's admin ip/port.\n"
218 "As soon as the server receives a new connection, it signals the\n"
219 "NT client, which then creates a new tunnel connection to the\n"
220 "server, which is then connected to the desired service on the\n"
221 "NT client's side (--local)\n"
223 "The connection between NT Client and NT Server on the admin\n"
224 "interface is protected by a shared secret against unauthorized use.\n"
225 "An adversary who can intercept packets could crack the secret\n"
226 "if it's of insufficient complexity. At least 10 random\n"
227 "characters and numbers are recommended.\n"
230 "You have a HTTP server listening on your local machine on port 80.\n"
231 "You want to make it available on your cloud server/VPS/etc's public\n"
233 "We use port 8000 on the cloud server for the control channel.\n"
236 " %s --mode server --secret s3cretP4ss --public 0.0.0.0:7000 --admin 0.0.0.0:8000\n"
238 " %s --mode client --secret s3cretP4ss --local localhost:80 --admin example.com:8000\n"
239 ) % (sys
.argv
[0], sys
.argv
[0])
240 if len(sys
.argv
) < 2 or (sys
.argv
[1] == '-h' or sys
.argv
[1] == '--help'):
242 parser
= argparse
.ArgumentParser(description
='')
243 parser
.add_argument('--secret', help='shared secret between natserver/client', type=str, default
='', required
=True)
244 parser
.add_argument('--mode', help='work mode: server or client', type=str, default
='server', required
=True)
245 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)
246 parser
.add_argument('--local', help='(client only) ip:port of the local target service', type=str, default
="localhost:80", required
=False)
247 parser
.add_argument('--admin', help='ip:port tuple for admin/upstream/control connection', type=str, default
="0.0.0.0:8081", required
=False)
248 args
= parser
.parse_args()
249 adminip
, adminport
= args
.admin
.split(':')
250 if args
.mode
== 'server':
251 clientip
, clientport
= args
.public
.split(':')
252 srv
= NATSrv(args
.secret
, adminip
, int(adminport
), clientip
, int(clientport
))
256 localip
, localport
= args
.local
.split(':')
257 cl
= NATClient(args
.secret
, adminip
, int(adminport
), localip
, int(localport
))