-
Notifications
You must be signed in to change notification settings - Fork 1
/
tncc-script.py
executable file
·336 lines (276 loc) · 10.1 KB
/
tncc-script.py
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import mechanize
import cookielib
import struct
import ssl
import base64
import collections
import zlib
import HTMLParser
import socket
import urlgrabber
import urllib2
ssl._create_default_https_context = ssl._create_unverified_context
# 0013 - Message
def decode_0013(buf):
ret = collections.defaultdict(list)
while (len(buf) >= 12):
length, cmd, out = decode_packet(buf)
buf = buf[length:]
ret[cmd].append(out)
return ret
# 0012 - u32
def decode_0012(buf):
return struct.unpack(">I", buf)
# 0ce4 - encapsulation
def decode_0ce4(buf):
ret = collections.defaultdict(list)
while (len(buf) >= 12):
length, cmd, out = decode_packet(buf)
buf = buf[length:]
ret[cmd].append(out)
return ret
# 0ce5 - string without hex prefixer
def decode_0ce5(buf):
return struct.unpack(str(len(buf)) + "s", buf)
# 0ce7 - string with hex prefixer
def decode_0ce7(buf):
_, s = struct.unpack(">I" + str(len(buf) - 4) + "s", buf)
return s
# 0cf0 - encapsulation
def decode_0cf0(buf):
ret = dict()
cmd, _, out = decode_packet(buf)
ret[cmd] = out
return ret
# 0cf1 - string without hex prefixer
def decode_0cf1(buf):
return struct.unpack(str(len(buf)) + "s", buf)
# 0cf3 - u32
def decode_0cf3(buf):
return struct.unpack(">I", buf)
def decode_packet(buf):
cmd, _1, _2, length, _3 = struct.unpack(">IBBHI", buf[:12])
if (length < 12):
raise Exception("Invalid packet")
data = buf[12:length]
if cmd == 0x0013:
data = decode_0013(data)
elif cmd == 0x0012:
data = decode_0012(data)
elif cmd == 0x0ce4:
data = decode_0ce4(data)
elif cmd == 0x0ce5:
data = decode_0ce5(data)
elif cmd == 0x0ce7:
data = decode_0ce7(data)
elif cmd == 0x0cf0:
data = decode_0cf0(data)
elif cmd == 0x0cf1:
data = decode_0cf1(data)
elif cmd == 0x0cf3:
data = decode_0cf3(data)
else:
data = None
return length, cmd, data
def encode_packet(cmd, align, buf):
if (align > 1 and (len(buf) + 12) % align):
buf += struct.pack(str(align - len(buf) % align) + "x")
return struct.pack(">IBBHI", cmd, 0xc0, 0x00, len(buf) + 12, 0x0000583) + buf
# 0013 - Message
def encode_0013(buf):
return encode_packet(0x0013, 4, buf)
# 0012 - u32
def encode_0012(i):
return encode_packet(0x0012, 1, struct.pack("<I", i))
# 0ce4 - encapsulation
def encode_0ce4(buf):
return encode_packet(0x0ce4, 4, buf)
# 0ce5 - string without hex prefixer
def encode_0ce5(s):
return encode_packet(0x0ce5, 1, struct.pack(str(len(s)) + "s", s))
# 0ce7 - string with hex prefixer
def encode_0ce7(s):
return encode_packet(0x0ce7, 1, struct.pack(">I" + str(len(s)) + "sx",
0x00058316, s))
# 0cf0 - encapsulation
def encode_0cf0(buf):
return encode_packet(0x0cf0, 4, buf)
# 0cf1 - string without hex prefixer
def encode_0cf1(s):
return encode_packet(0x0ce5, 1, struct.pack(str(len(s)) + "s", s))
# 0cf3 - u32
def encode_0cf3(i):
return encode_packet(0x0013, 1, struct.pack("<I", i))
class tncc(object):
def __init__(self, vpn_host):
self.vpn_host = vpn_host
self.path = '/dana-na/'
self.br = mechanize.Browser()
self.cj = cookielib.LWPCookieJar()
self.br.set_cookiejar(self.cj)
# Browser options
self.br.set_handle_equiv(True)
self.br.set_handle_redirect(True)
self.br.set_handle_referer(True)
self.br.set_handle_robots(False)
# Follows refresh 0 but not hangs on refresh > 0
self.br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(),
max_time=1)
# Want debugging messages?
# self.br.set_debug_http(True)
# self.br.set_debug_redirects(True)
# self.br.set_debug_responses(True)
self.user_agent = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008071615 Fedora/3.0.1-1.fc9 Firefox/3.0.1'
self.br.addheaders = [('User-agent', self.user_agent)]
def find_cookie(self, name):
for cookie in self.cj:
if cookie.name == name:
return cookie
return None
def set_cookie(self, name, value):
cookie = cookielib.Cookie(version=0, name=name, value=value,
port=None, port_specified=False, domain=self.vpn_host,
domain_specified=True, domain_initial_dot=False, path=self.path,
path_specified=True, secure=True, expires=None, discard=True,
comment=None, comment_url=None, rest=None, rfc2109=False)
self.cj.set_cookie(cookie)
def parse_response(self):
# Read in key/token fields in HTTP response
response = dict()
last_key = ''
for line in self.r.readlines():
line = line.strip()
# Note that msg is too long and gets wrapped, handle it special
if last_key == 'msg' and len(line):
response['msg'] += line
else:
key = ''
try:
key, val = line.split('=', 1)
response[key] = val
except:
pass
last_key = key
return response
def get_msg_contents(self, msg_value):
# msg has the stuff we want, it's base64 encoded
msg_raw = base64.b64decode(msg_value)
_1, _2, msg_decoded = decode_packet(msg_raw)
# Within msg, there is a field of data
if not len(msg_decoded[0x0ce4]):
return None
compressed = msg_decoded[0x0ce4][0][0x0ce7][0]
# That field has a field that is compressed, decompress it
typ, length, data = compressed.split(':', 2)
if typ == 'COMPRESSED':
data = zlib.decompress(data)
else:
raise Exception("Unknown storage type", typ)
return data
def parse_msg(self, msg_data):
# The decompressed data is HTMLish, decode it. The value="" of each
# tag is the data we want.
objs = []
class ParamHTMLParser(HTMLParser.HTMLParser):
def handle_starttag(self, tag, attrs):
for key, value in attrs:
if key == 'value':
# It's made up of a bunch of key=value pairs separated
# by semicolons
d = dict()
for field in value.split(';'):
field = field.strip()
try:
key, value = field.split('=', 1)
d[key] = value
except:
pass
objs.append(d)
p = ParamHTMLParser()
p.feed(msg_data)
p.close()
return objs
def get_cookie(self, dspreauth=None, dssignin=None):
if dspreauth is None or dssignin is None:
self.r = self.br.open('https://' + self.vpn_host)
else:
try:
self.cj.set_cookie(dspreauth)
except:
self.set_cookie('DSPREAUTH', dspreauth)
try:
self.cj.set_cookie(dssignin)
except:
self.set_cookie('DSSIGNIN', dssignin)
msg_raw = encode_0013(encode_0ce4(encode_0ce7('policy request')) +
encode_0ce5('Accept-Language: en'))
msg = base64.b64encode(msg_raw)
post_data = 'connId=0;msg=' + msg + ';firsttime=1;'
self.r = self.br.open('https://' + self.vpn_host + self.path + 'hc/tnchcupdate.cgi', post_data)
# Parse the data returned into a key/value dict
response = self.parse_response()
# Pull the compressed data block out of msg
data = self.get_msg_contents(response['msg'])
# Pull the data out of the 'value' key in the htmlish stuff returned
objs = self.parse_msg(data) if data else []
for obj in objs:
if 'policy' in obj:
print 'policy', obj['policy']
for key, val in obj.iteritems():
if key != 'policy':
print '\t' + key, val
# Make a set of policies
policies = set()
for entry in objs:
if 'policy' in entry:
policies.add(entry['policy'])
# Everything is OK, this may need updating if OK isn't the right answer
policy_report = ""
for policy in policies:
policy_report += '\npolicy:' + policy + '\nstatus:OK\n'
msg_raw = encode_0013(encode_0ce4(encode_0ce7(policy_report)) +
encode_0ce5('Accept-Language: en'))
msg = base64.b64encode(msg_raw)
post_data = 'connId=1;msg=' + msg + ';firsttime=1;'
self.r = self.br.open('https://' + self.vpn_host + self.path + 'hc/tnchcupdate.cgi', post_data)
# We have a new DSPREAUTH cookie
return self.find_cookie('DSPREAUTH')
class tncc_server(object):
def __init__(self, s, t):
self.sock = s
self.tncc = t
def process_cmd(self):
buf = sock.recv(1024).decode('ascii')
if not len(buf):
sys.exit(0)
cmd, buf = buf.split('\n', 1)
cmd = cmd.strip()
args = dict()
for n in buf.split('\n'):
n = n.strip()
if len(n):
key, val = n.strip().split('=', 1)
args[key] = val
if cmd == 'start':
cookie = self.tncc.get_cookie(args['Cookie'], args['DSSIGNIN'])
resp = '200\n3\n%s\n\n' % cookie.value
sock.send(resp.encode('ascii'))
elif cmd == 'setcookie':
# FIXME: Support for periodic updates
dsid_value = args['Cookie']
if __name__ == "__main__":
vpn_host = sys.argv[1]
t = tncc(vpn_host)
if len(sys.argv) == 4:
dspreauth_value = sys.argv[2]
dssignin_value = sys.argv[3]
print 'TNCC ', dspreauth_value, dssignin_value
print t.get_cookie(dspreauth, dssignin).value
else:
sock = socket.fromfd(0, socket.AF_UNIX, socket.SOCK_SEQPACKET)
server = tncc_server(sock, t)
while True:
server.process_cmd()