Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Impacket - Collection of Python classes for working with network protocols. 

2# 

3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved. 

4# 

5# This software is provided under a slightly modified version 

6# of the Apache Software License. See the accompanying LICENSE file 

7# for more information. 

8# 

9# Description: 

10# [MS-TDS] & [MC-SQLR] implementation. 

11# 

12# Author: 

13# Alberto Solino (@agsolino) 

14# 

15# ToDo: 

16# [ ] Add all the tokens left 

17# [ ] parseRow should be rewritten and add support for all the SQL types in a 

18# good way. Right now it just supports a few types. 

19# [ ] printRows is crappy, just an easy way to print the rows. It should be 

20# rewritten to output like a normal SQL client 

21# 

22 

23from __future__ import division 

24from __future__ import print_function 

25import struct 

26import socket 

27import select 

28import random 

29import binascii 

30import math 

31import datetime 

32import string 

33 

34from impacket import ntlm, uuid, LOG 

35from impacket.structure import Structure 

36 

37try: 

38 from OpenSSL import SSL 

39except: 

40 LOG.critical("pyOpenSSL is not installed, can't continue") 

41 raise 

42 

43# We need to have a fake Logger to be compatible with the way Impact  

44# prints information. Outside Impact it's just a print. Inside  

45# we will receive the Impact logger instance to print row information 

46# The rest it processed through the standard impacket logging mech. 

47class DummyPrint: 

48 def logMessage(self,message): 

49 if message == '\n': 

50 print(message) 

51 else: 

52 print(message, end=' ') 

53 

54# MC-SQLR Constants and Structures 

55SQLR_PORT = 1434 

56SQLR_CLNT_BCAST_EX = 0x02 

57SQLR_CLNT_UCAST_EX = 0x03 

58SQLR_CLNT_UCAST_INST= 0x04 

59SQLR_CLNT_UCAST_DAC = 0x0f 

60 

61 

62class SQLR(Structure): 

63 commonHdr = ( 

64 ('OpCode','B'), 

65 ) 

66 

67class SQLR_UCAST_INST(SQLR): 

68 structure = ( 

69 ('Instance',':') 

70 ) 

71 def __init__(self, data = None): 

72 SQLR.__init__(self,data) 

73 if data is not None: 

74 self['OpCode'] = SQLR_CLNT_UCAST_INST 

75 

76class SQLR_UCAST_DAC(SQLR): 

77 structure = ( 

78 ('Protocol', 'B=1'), 

79 ('Instance', ':'), 

80 ) 

81 def __init__(self, data = None): 

82 SQLR.__init__(self,data) 

83 if data is not None: 

84 self['OpCode'] = SQLR_CLNT_UCAST_DAC 

85 

86class SQLR_Response(SQLR): 

87 structure = ( 

88 ('Size','<H'), 

89 ('_Data','_-Data','self["Size"]'), 

90 ('Data',':'), 

91 ) 

92 

93class SQLErrorException(Exception): 

94 pass 

95 

96# TDS Constants and Structures 

97 

98# TYPE constants 

99TDS_SQL_BATCH = 1 

100TDS_PRE_TDS_LOGIN = 2 

101TDS_RPC = 3 

102TDS_TABULAR = 4 

103TDS_ATTENTION = 6 

104TDS_BULK_LOAD_DATA = 7 

105TDS_TRANSACTION = 14 

106TDS_LOGIN7 = 16 

107TDS_SSPI = 17 

108TDS_PRE_LOGIN = 18 

109 

110# Status constants 

111TDS_STATUS_NORMAL = 0 

112TDS_STATUS_EOM = 1 

113TDS_STATUS_RESET_CONNECTION = 8 

114TDS_STATUS_RESET_SKIPTRANS = 16 

115 

116# Encryption 

117TDS_ENCRYPT_OFF = 0 

118TDS_ENCRYPT_ON = 1 

119TDS_ENCRYPT_NOT_SUP = 2 

120TDS_ENCRYPT_REQ = 3 

121 

122# Option 2 Flags 

123TDS_INTEGRATED_SECURITY_ON = 0x80 

124TDS_INIT_LANG_FATAL = 0x01 

125TDS_ODBC_ON = 0x02 

126 

127# Token Types 

128TDS_ALTMETADATA_TOKEN = 0x88 

129TDS_ALTROW_TOKEN = 0xD3 

130TDS_COLMETADATA_TOKEN = 0x81 

131TDS_COLINFO_TOKEN = 0xA5 

132TDS_DONE_TOKEN = 0xFD 

133TDS_DONEPROC_TOKEN = 0xFE 

134TDS_DONEINPROC_TOKEN = 0xFF 

135TDS_ENVCHANGE_TOKEN = 0xE3 

136TDS_ERROR_TOKEN = 0xAA 

137TDS_INFO_TOKEN = 0xAB 

138TDS_LOGINACK_TOKEN = 0xAD 

139TDS_NBCROW_TOKEN = 0xD2 

140TDS_OFFSET_TOKEN = 0x78 

141TDS_ORDER_TOKEN = 0xA9 

142TDS_RETURNSTATUS_TOKEN = 0x79 

143TDS_RETURNVALUE_TOKEN = 0xAC 

144TDS_ROW_TOKEN = 0xD1 

145TDS_SSPI_TOKEN = 0xED 

146TDS_TABNAME_TOKEN = 0xA4 

147 

148# ENVCHANGE Types 

149TDS_ENVCHANGE_DATABASE = 1 

150TDS_ENVCHANGE_LANGUAGE = 2 

151TDS_ENVCHANGE_CHARSET = 3 

152TDS_ENVCHANGE_PACKETSIZE = 4 

153TDS_ENVCHANGE_UNICODE = 5 

154TDS_ENVCHANGE_UNICODE_DS = 6 

155TDS_ENVCHANGE_COLLATION = 7 

156TDS_ENVCHANGE_TRANS_START = 8 

157TDS_ENVCHANGE_TRANS_COMMIT = 9 

158TDS_ENVCHANGE_ROLLBACK = 10 

159TDS_ENVCHANGE_DTC = 11 

160 

161# Column types 

162# FIXED-LEN Data Types 

163TDS_NULL_TYPE = 0x1F 

164TDS_INT1TYPE = 0x30 

165TDS_BITTYPE = 0x32 

166TDS_INT2TYPE = 0x34 

167TDS_INT4TYPE = 0x38 

168TDS_DATETIM4TYPE = 0x3A 

169TDS_FLT4TYPE = 0x3B 

170TDS_MONEYTYPE = 0x3C 

171TDS_DATETIMETYPE = 0x3D 

172TDS_FLT8TYPE = 0x3E 

173TDS_MONEY4TYPE = 0x7A 

174TDS_INT8TYPE = 0x7F 

175 

176# VARIABLE-Len Data Types 

177TDS_GUIDTYPE = 0x24 

178TDS_INTNTYPE = 0x26 

179TDS_DECIMALTYPE = 0x37 

180TDS_NUMERICTYPE = 0x3F 

181TDS_BITNTYPE = 0x68 

182TDS_DECIMALNTYPE = 0x6A 

183TDS_NUMERICNTYPE = 0x6C 

184TDS_FLTNTYPE = 0x6D 

185TDS_MONEYNTYPE = 0x6E 

186TDS_DATETIMNTYPE = 0x6F 

187TDS_DATENTYPE = 0x28 

188TDS_TIMENTYPE = 0x29 

189TDS_DATETIME2NTYPE = 0x2A 

190TDS_DATETIMEOFFSETNTYPE = 0x2B 

191TDS_CHARTYPE = 0x2F 

192TDS_VARCHARTYPE = 0x27 

193TDS_BINARYTYPE = 0x2D 

194TDS_VARBINARYTYPE = 0x25 

195TDS_BIGVARBINTYPE = 0xA5 

196TDS_BIGVARCHRTYPE = 0xA7 

197TDS_BIGBINARYTYPE = 0xAD 

198TDS_BIGCHARTYPE = 0xAF 

199TDS_NVARCHARTYPE = 0xE7 

200TDS_NCHARTYPE = 0xEF 

201TDS_XMLTYPE = 0xF1 

202TDS_UDTTYPE = 0xF0 

203TDS_TEXTTYPE = 0x23 

204TDS_IMAGETYPE = 0x22 

205TDS_NTEXTTYPE = 0x63 

206TDS_SSVARIANTTYPE = 0x62 

207 

208class TDSPacket(Structure): 

209 structure = ( 

210 ('Type','<B'), 

211 ('Status','<B=1'), 

212 ('Length','>H=8+len(Data)'), 

213 ('SPID','>H=0'), 

214 ('PacketID','<B=0'), 

215 ('Window','<B=0'), 

216 ('Data',':'), 

217 ) 

218 

219class TDS_PRELOGIN(Structure): 

220 structure = ( 

221 ('VersionToken','>B=0'), 

222 ('VersionOffset','>H'), 

223 ('VersionLength','>H=len(self["Version"])'), 

224 ('EncryptionToken','>B=0x1'), 

225 ('EncryptionOffset','>H'), 

226 ('EncryptionLength','>H=1'), 

227 ('InstanceToken','>B=2'), 

228 ('InstanceOffset','>H'), 

229 ('InstanceLength','>H=len(self["Instance"])'), 

230 ('ThreadIDToken','>B=3'), 

231 ('ThreadIDOffset','>H'), 

232 ('ThreadIDLength','>H=4'), 

233 ('EndToken','>B=0xff'), 

234 ('_Version','_-Version','self["VersionLength"]'), 

235 ('Version',':'), 

236 ('Encryption','B'), 

237 ('_Instance','_-Instance','self["InstanceLength"]-1'), 

238 ('Instance',':'), 

239 ('ThreadID',':'), 

240 ) 

241 

242 def getData(self): 

243 self['VersionOffset']=21 

244 self['EncryptionOffset']=self['VersionOffset'] + len(self['Version']) 

245 self['InstanceOffset']=self['EncryptionOffset'] + 1 

246 self['ThreadIDOffset']=self['InstanceOffset'] + len(self['Instance']) 

247 return Structure.getData(self) 

248 

249class TDS_LOGIN(Structure): 

250 structure = ( 

251 ('Length','<L=0'), 

252 ('TDSVersion','>L=0x71'), 

253 ('PacketSize','<L=32764'), 

254 ('ClientProgVer','>L=7'), 

255 ('ClientPID','<L=0'), 

256 ('ConnectionID','<L=0'), 

257 ('OptionFlags1','<B=0xe0'), 

258 ('OptionFlags2','<B'), 

259 ('TypeFlags','<B=0'), 

260 ('OptionFlags3','<B=0'), 

261 ('ClientTimeZone','<L=0'), 

262 ('ClientLCID','<L=0'), 

263 ('HostNameOffset','<H'), 

264 ('HostNameLength','<H=len(self["HostName"])//2'), 

265 ('UserNameOffset','<H=0'), 

266 ('UserNameLength','<H=len(self["UserName"])//2'), 

267 ('PasswordOffset','<H=0'), 

268 ('PasswordLength','<H=len(self["Password"])//2'), 

269 ('AppNameOffset','<H'), 

270 ('AppNameLength','<H=len(self["AppName"])//2'), 

271 ('ServerNameOffset','<H'), 

272 ('ServerNameLength','<H=len(self["ServerName"])//2'), 

273 ('UnusedOffset','<H=0'), 

274 ('UnusedLength','<H=0'), 

275 ('CltIntNameOffset','<H'), 

276 ('CltIntNameLength','<H=len(self["CltIntName"])//2'), 

277 ('LanguageOffset','<H=0'), 

278 ('LanguageLength','<H=0'), 

279 ('DatabaseOffset','<H=0'), 

280 ('DatabaseLength','<H=len(self["Database"])//2'), 

281 ('ClientID','6s=b"\x01\x02\x03\x04\x05\x06"'), 

282 ('SSPIOffset','<H'), 

283 ('SSPILength','<H=len(self["SSPI"])'), 

284 ('AtchDBFileOffset','<H'), 

285 ('AtchDBFileLength','<H=len(self["AtchDBFile"])//2'), 

286 ('HostName',':'), 

287 ('UserName',':'), 

288 ('Password',':'), 

289 ('AppName',':'), 

290 ('ServerName',':'), 

291 ('CltIntName',':'), 

292 ('Database',':'), 

293 ('SSPI',':'), 

294 ('AtchDBFile',':'), 

295 ) 

296 def __init__(self,data=None): 

297 Structure.__init__(self,data) 

298 if data is None: 

299 self['UserName'] = '' 

300 self['Password'] = '' 

301 self['Database'] = '' 

302 self['AtchDBFile'] = '' 

303 

304 def fromString(self, data): 

305 Structure.fromString(self, data) 

306 if self['HostNameLength'] > 0: 

307 self['HostName'] = data[self['HostNameOffset']:][:self['HostNameLength']*2] 

308 

309 if self['UserNameLength'] > 0: 

310 self['UserName'] = data[self['UserNameOffset']:][:self['UserNameLength']*2] 

311 

312 if self['PasswordLength'] > 0: 

313 self['Password'] = data[self['PasswordOffset']:][:self['PasswordLength']*2] 

314 

315 if self['AppNameLength'] > 0: 

316 self['AppName'] = data[self['AppNameOffset']:][:self['AppNameLength']*2] 

317 

318 if self['ServerNameLength'] > 0: 

319 self['ServerName'] = data[self['ServerNameOffset']:][:self['ServerNameLength']*2] 

320 

321 if self['CltIntNameLength'] > 0: 

322 self['CltIntName'] = data[self['CltIntNameOffset']:][:self['CltIntNameLength']*2] 

323 

324 if self['DatabaseLength'] > 0: 

325 self['Database'] = data[self['DatabaseOffset']:][:self['DatabaseLength']*2] 

326 

327 if self['SSPILength'] > 0: 

328 self['SSPI'] = data[self['SSPIOffset']:][:self['SSPILength']*2] 

329 

330 if self['AtchDBFileLength'] > 0: 

331 self['AtchDBFile'] = data[self['AtchDBFileOffset']:][:self['AtchDBFileLength']*2] 

332 

333 def getData(self): 

334 index = 36+50 

335 self['HostNameOffset']= index 

336 

337 index += len(self['HostName']) 

338 

339 if self['UserName'] != '': 

340 self['UserNameOffset'] = index 

341 else: 

342 self['UserNameOffset'] = 0 

343 

344 index += len(self['UserName']) 

345 

346 if self['Password'] != '': 

347 self['PasswordOffset'] = index 

348 else: 

349 self['PasswordOffset'] = 0 

350 

351 index += len(self['Password']) 

352 

353 self['AppNameOffset']= index 

354 self['ServerNameOffset']=self['AppNameOffset'] + len(self['AppName']) 

355 self['CltIntNameOffset']=self['ServerNameOffset'] + len(self['ServerName']) 

356 self['LanguageOffset']=self['CltIntNameOffset'] + len(self['CltIntName']) 

357 self['DatabaseOffset']=self['LanguageOffset'] 

358 self['SSPIOffset']=self['DatabaseOffset'] + len(self['Database']) 

359 self['AtchDBFileOffset']=self['SSPIOffset'] + len(self['SSPI']) 

360 return Structure.getData(self) 

361 

362class TDS_LOGIN_ACK(Structure): 

363 structure = ( 

364 ('TokenType','<B'), 

365 ('Length','<H'), 

366 ('Interface','<B'), 

367 ('TDSVersion','<L'), 

368 ('ProgNameLen','<B'), 

369 ('_ProgNameLen','_-ProgName','self["ProgNameLen"]*2'), 

370 ('ProgName',':'), 

371 ('MajorVer','<B'), 

372 ('MinorVer','<B'), 

373 ('BuildNumHi','<B'), 

374 ('BuildNumLow','<B'), 

375 ) 

376 

377class TDS_RETURNSTATUS(Structure): 

378 structure = ( 

379 ('TokenType','<B'), 

380 ('Value','<L'), 

381 ) 

382 

383class TDS_INFO_ERROR(Structure): 

384 structure = ( 

385 ('TokenType','<B'), 

386 ('Length','<H'), 

387 ('Number','<L'), 

388 ('State','<B'), 

389 ('Class','<B'), 

390 ('MsgTextLen','<H'), 

391 ('_MsgTextLen','_-MsgText','self["MsgTextLen"]*2'), 

392 ('MsgText',':'), 

393 ('ServerNameLen','<B'), 

394 ('_ServerNameLen','_-ServerName','self["ServerNameLen"]*2'), 

395 ('ServerName',':'), 

396 ('ProcNameLen','<B'), 

397 ('_ProcNameLen','_-ProcName','self["ProcNameLen"]*2'), 

398 ('ProcName',':'), 

399 ('LineNumber','<H'), 

400 ) 

401 

402class TDS_ENVCHANGE(Structure): 

403 structure = ( 

404 ('TokenType','<B'), 

405 ('Length','<H=4+len(Data)'), 

406 ('Type','<B'), 

407 ('_Data','_-Data','self["Length"]-1'), 

408 ('Data',':'), 

409 ) 

410 

411class TDS_DONEINPROC(Structure): 

412 structure = ( 

413 ('TokenType','<B'), 

414 ('Status','<H'), 

415 ('CurCmd','<H'), 

416 ('DoneRowCount','<L'), 

417 ) 

418 

419class TDS_ORDER(Structure): 

420 structure = ( 

421 ('TokenType','<B'), 

422 ('Length','<H'), 

423 ('_Data','_-Data','self["Length"]'), 

424 ('Data',':'), 

425 ) 

426 

427 

428class TDS_ENVCHANGE_VARCHAR(Structure): 

429 structure = ( 

430 ('NewValueLen','<B=len(NewValue)'), 

431 ('_NewValue','_-NewValue','self["NewValueLen"]*2'), 

432 ('NewValue',':'), 

433 ('OldValueLen','<B=len(OldValue)'), 

434 ('_OldValue','_-OldValue','self["OldValueLen"]*2'), 

435 ('OldValue',':'), 

436 ) 

437 

438class TDS_ROW(Structure): 

439 structure = ( 

440 ('TokenType','<B'), 

441 ('Data',':'), 

442 ) 

443 

444class TDS_DONE(Structure): 

445 structure = ( 

446 ('TokenType','<B'), 

447 ('Status','<H'), 

448 ('CurCmd','<H'), 

449 ('DoneRowCount','<L'), 

450 ) 

451 

452class TDS_COLMETADATA(Structure): 

453 structure = ( 

454 ('TokenType','<B'), 

455 ('Count','<H'), 

456 ('Data',':'), 

457 ) 

458 

459class MSSQL: 

460 def __init__(self, address, port=1433, rowsPrinter=DummyPrint()): 

461 #self.packetSize = 32764 

462 self.packetSize = 32763 

463 self.server = address 

464 self.port = port 

465 self.socket = 0 

466 self.replies = {} 

467 self.colMeta = [] 

468 self.rows = [] 

469 self.currentDB = '' 

470 self.COL_SEPARATOR = ' ' 

471 self.MAX_COL_LEN = 255 

472 self.lastError = False 

473 self.tlsSocket = None 

474 self.__rowsPrinter = rowsPrinter 

475 

476 def getInstances(self, timeout = 5): 

477 packet = SQLR() 

478 packet['OpCode'] = SQLR_CLNT_UCAST_EX 

479 

480 # Open the connection 

481 af, socktype, proto, canonname, sa = socket.getaddrinfo(self.server, SQLR_PORT, 0, socket.SOCK_DGRAM)[0] 

482 s = socket.socket(af, socktype, proto) 

483 

484 s.sendto(packet.getData(), 0, ( self.server, SQLR_PORT )) 

485 ready, _, _ = select.select([ s.fileno() ], [ ] , [ ], timeout) 

486 if not ready: 

487 return [] 

488 else: 

489 data, _ = s.recvfrom(65536, 0) 

490 

491 s.close() 

492 resp = SQLR_Response(data) 

493 

494 # Now parse the results 

495 entries = resp['Data'].split(b';;') 

496 

497 # We don't want the last one, it's empty 

498 entries.pop() 

499 

500 # the answer to send back 

501 resp = [] 

502 

503 for i, entry in enumerate(entries): 

504 fields = entry.split(b';') 

505 ret = {} 

506 for j, field in enumerate(fields): 

507 if (j & 0x1) == 0: 

508 ret[field.decode('utf-8')] = fields[j+1].decode('utf-8') 

509 resp.append(ret) 

510 

511 return resp 

512 

513 

514 def preLogin(self): 

515 prelogin = TDS_PRELOGIN() 

516 prelogin['Version'] = b"\x08\x00\x01\x55\x00\x00" 

517 #prelogin['Encryption'] = TDS_ENCRYPT_NOT_SUP 

518 prelogin['Encryption'] = TDS_ENCRYPT_OFF 

519 prelogin['ThreadID'] = struct.pack('<L',random.randint(0,65535)) 

520 prelogin['Instance'] = b'MSSQLServer\x00' 

521 

522 self.sendTDS(TDS_PRE_LOGIN, prelogin.getData(), 0) 

523 tds = self.recvTDS() 

524 

525 return TDS_PRELOGIN(tds['Data']) 

526 

527 def encryptPassword(self, password ): 

528 return bytes(bytearray([((x & 0x0f) << 4) + ((x & 0xf0) >> 4) ^ 0xa5 for x in bytearray(password)])) 

529 

530 def connect(self): 

531 af, socktype, proto, canonname, sa = socket.getaddrinfo(self.server, self.port, 0, socket.SOCK_STREAM)[0] 

532 sock = socket.socket(af, socktype, proto) 

533 

534 try: 

535 sock.connect(sa) 

536 except Exception: 

537 #import traceback 

538 #traceback.print_exc() 

539 raise 

540 

541 self.socket = sock 

542 return sock 

543 

544 def disconnect(self): 

545 if self.socket: 

546 return self.socket.close() 

547 

548 def setPacketSize(self, packetSize): 

549 self.packetSize = packetSize 

550 

551 def getPacketSize(self): 

552 return self.packetSize 

553 

554 def socketSendall(self,data): 

555 if self.tlsSocket is None: 

556 return self.socket.sendall(data) 

557 else: 

558 self.tlsSocket.sendall(data) 

559 dd = self.tlsSocket.bio_read(self.packetSize) 

560 return self.socket.sendall(dd) 

561 

562 def sendTDS(self, packetType, data, packetID = 1): 

563 if (len(data)-8) > self.packetSize: 

564 remaining = data[self.packetSize-8:] 

565 tds = TDSPacket() 

566 tds['Type'] = packetType 

567 tds['Status'] = TDS_STATUS_NORMAL 

568 tds['PacketID'] = packetID 

569 tds['Data'] = data[:self.packetSize-8] 

570 self.socketSendall(tds.getData()) 

571 

572 while len(remaining) > (self.packetSize-8): 

573 packetID += 1 

574 tds['PacketID'] = packetID 

575 tds['Data'] = remaining[:self.packetSize-8] 

576 self.socketSendall(tds.getData()) 

577 remaining = remaining[self.packetSize-8:] 

578 data = remaining 

579 packetID+=1 

580 

581 tds = TDSPacket() 

582 tds['Type'] = packetType 

583 tds['Status'] = TDS_STATUS_EOM 

584 tds['PacketID'] = packetID 

585 tds['Data'] = data 

586 self.socketSendall(tds.getData()) 

587 

588 def socketRecv(self, packetSize): 

589 data = self.socket.recv(packetSize) 

590 if self.tlsSocket is not None: 

591 dd = '' 

592 self.tlsSocket.bio_write(data) 

593 while True: 

594 try: 

595 dd += self.tlsSocket.read(packetSize) 

596 except SSL.WantReadError: 

597 data2 = self.socket.recv(packetSize - len(data) ) 

598 self.tlsSocket.bio_write(data2) 

599 pass 

600 else: 

601 data = dd 

602 break 

603 return data 

604 

605 def recvTDS(self, packetSize = None): 

606 # Do reassembly here 

607 if packetSize is None: 

608 packetSize = self.packetSize 

609 packet = TDSPacket(self.socketRecv(packetSize)) 

610 status = packet['Status'] 

611 packetLen = packet['Length']-8 

612 while packetLen > len(packet['Data']): 

613 data = self.socketRecv(packetSize) 

614 packet['Data'] += data 

615 

616 remaining = None 

617 if packetLen < len(packet['Data']): 

618 remaining = packet['Data'][packetLen:] 

619 packet['Data'] = packet['Data'][:packetLen] 

620 

621 #print "REMAINING ",  

622 #if remaining is None:  

623 # print None  

624 #else:  

625 # print len(remaining) 

626 

627 while status != TDS_STATUS_EOM: 

628 if remaining is not None: 

629 tmpPacket = TDSPacket(remaining) 

630 else: 

631 tmpPacket = TDSPacket(self.socketRecv(packetSize)) 

632 

633 packetLen = tmpPacket['Length'] - 8 

634 while packetLen > len(tmpPacket['Data']): 

635 data = self.socketRecv(packetSize) 

636 tmpPacket['Data'] += data 

637 

638 remaining = None 

639 if packetLen < len(tmpPacket['Data']): 

640 remaining = tmpPacket['Data'][packetLen:] 

641 tmpPacket['Data'] = tmpPacket['Data'][:packetLen] 

642 

643 status = tmpPacket['Status'] 

644 packet['Data'] += tmpPacket['Data'] 

645 packet['Length'] += tmpPacket['Length'] - 8 

646 

647 #print packet['Length'] 

648 return packet 

649 

650 def kerberosLogin(self, database, username, password='', domain='', hashes=None, aesKey='', kdcHost=None, TGT=None, TGS=None, useCache=True): 

651 

652 if hashes is not None: 

653 lmhash, nthash = hashes.split(':') 

654 lmhash = binascii.a2b_hex(lmhash) 

655 nthash = binascii.a2b_hex(nthash) 

656 else: 

657 lmhash = '' 

658 nthash = '' 

659 

660 resp = self.preLogin() 

661 # Test this! 

662 if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF: 

663 LOG.info("Encryption required, switching to TLS") 

664 

665 # Switching to TLS now 

666 ctx = SSL.Context(SSL.TLSv1_METHOD) 

667 ctx.set_cipher_list('RC4, AES256') 

668 tls = SSL.Connection(ctx,None) 

669 tls.set_connect_state() 

670 while True: 

671 try: 

672 tls.do_handshake() 

673 except SSL.WantReadError: 

674 data = tls.bio_read(4096) 

675 self.sendTDS(TDS_PRE_LOGIN, data,0) 

676 tds = self.recvTDS() 

677 tls.bio_write(tds['Data']) 

678 else: 

679 break 

680 

681 # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement, 

682 # Transport Layer Security(TLS), limit data fragments to 16k in size. 

683 self.packetSize = 16*1024-1 

684 self.tlsSocket = tls 

685 

686 

687 login = TDS_LOGIN() 

688 

689 login['HostName'] = (''.join([random.choice(string.ascii_letters) for _ in range(8)])).encode('utf-16le') 

690 login['AppName'] = (''.join([random.choice(string.ascii_letters) for _ in range(8)])).encode('utf-16le') 

691 login['ServerName'] = self.server.encode('utf-16le') 

692 login['CltIntName'] = login['AppName'] 

693 login['ClientPID'] = random.randint(0,1024) 

694 login['PacketSize'] = self.packetSize 

695 if database is not None: 

696 login['Database'] = database.encode('utf-16le') 

697 login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON 

698 

699 from impacket.spnego import SPNEGO_NegTokenInit, TypesMech 

700 # Importing down here so pyasn1 is not required if kerberos is not used. 

701 from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set 

702 from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS, KerberosError 

703 from impacket.krb5 import constants 

704 from impacket.krb5.types import Principal, KerberosTime, Ticket 

705 from pyasn1.codec.der import decoder, encoder 

706 from pyasn1.type.univ import noValue 

707 from impacket.krb5.ccache import CCache 

708 import os 

709 import datetime 

710 

711 if useCache is True: 

712 try: 

713 ccache = CCache.loadFile(os.getenv('KRB5CCNAME')) 

714 except: 

715 # No cache present 

716 pass 

717 else: 

718 # retrieve domain information from CCache file if needed 

719 if domain == '': 

720 domain = ccache.principal.realm['data'].decode('utf-8') 

721 LOG.debug('Domain retrieved from CCache: %s' % domain) 

722 

723 LOG.debug("Using Kerberos Cache: %s" % os.getenv('KRB5CCNAME')) 

724 principal = 'MSSQLSvc/%s.%s:%d@%s' % (self.server.split('.')[0], domain, self.port, domain.upper()) 

725 creds = ccache.getCredential(principal) 

726 

727 if creds is not None: 

728 TGS = creds.toTGS(principal) 

729 LOG.debug('Using TGS from cache') 

730 else: 

731 # search for the port's instance name instead (instance name based SPN) 

732 LOG.debug('Searching target\'s instances to look for port number %s' % self.port) 

733 instances = self.getInstances() 

734 instanceName = None 

735 for i in instances: 

736 try: 

737 if int(i['tcp']) == self.port: 

738 instanceName = i['InstanceName'] 

739 except: 

740 pass 

741 

742 if instanceName: 

743 principal = 'MSSQLSvc/%s.%s:%s@%s' % (self.server, domain, instanceName, domain.upper()) 

744 creds = ccache.getCredential(principal) 

745 

746 if creds is not None: 

747 TGS = creds.toTGS(principal) 

748 LOG.debug('Using TGS from cache') 

749 else: 

750 # Let's try for the TGT and go from there 

751 principal = 'krbtgt/%s@%s' % (domain.upper(),domain.upper()) 

752 creds = ccache.getCredential(principal) 

753 if creds is not None: 

754 TGT = creds.toTGT() 

755 LOG.debug('Using TGT from cache') 

756 else: 

757 LOG.debug("No valid credentials found in cache. ") 

758 

759 # retrieve user information from CCache file if needed 

760 if username == '' and creds is not None: 

761 username = creds['client'].prettyPrint().split(b'@')[0].decode('utf-8') 

762 LOG.debug('Username retrieved from CCache: %s' % username) 

763 elif username == '' and len(ccache.principal.components) > 0: 

764 username = ccache.principal.components[0]['data'].decode('utf-8') 

765 LOG.debug('Username retrieved from CCache: %s' % username) 

766 

767 # First of all, we need to get a TGT for the user 

768 userName = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value) 

769 while True: 

770 if TGT is None: 

771 if TGS is None: 

772 try: 

773 tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost) 

774 except KerberosError as e: 

775 if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: 

776 # We might face this if the target does not support AES 

777 # So, if that's the case we'll force using RC4 by converting 

778 # the password to lm/nt hashes and hope for the best. If that's already 

779 # done, byebye. 

780 if lmhash == '' and nthash == '' and (aesKey == '' or aesKey is None) and TGT is None and TGS is None: 

781 from impacket.ntlm import compute_lmhash, compute_nthash 

782 LOG.debug('Got KDC_ERR_ETYPE_NOSUPP, fallback to RC4') 

783 lmhash = compute_lmhash(password) 

784 nthash = compute_nthash(password) 

785 continue 

786 else: 

787 raise 

788 else: 

789 raise 

790 else: 

791 tgt = TGT['KDC_REP'] 

792 cipher = TGT['cipher'] 

793 sessionKey = TGT['sessionKey'] 

794 

795 if TGS is None: 

796 # From https://msdn.microsoft.com/en-us/library/ms191153.aspx?f=255&MSPPError=-2147217396 

797 # Beginning with SQL Server 2008, the SPN format is changed in order to support Kerberos authentication 

798 # on TCP/IP, named pipes, and shared memory. The supported SPN formats for named and default instances 

799 # are as follows. 

800 # Named instance 

801 # MSSQLSvc/FQDN:[port | instancename], where: 

802 # MSSQLSvc is the service that is being registered. 

803 # FQDN is the fully qualified domain name of the server. 

804 # port is the TCP port number. 

805 # instancename is the name of the SQL Server instance. 

806 serverName = Principal('MSSQLSvc/%s.%s:%d' % (self.server.split('.')[0], domain, self.port), type=constants.PrincipalNameType.NT_SRV_INST.value) 

807 try: 

808 tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey) 

809 except KerberosError as e: 

810 if e.getErrorCode() == constants.ErrorCodes.KDC_ERR_ETYPE_NOSUPP.value: 

811 # We might face this if the target does not support AES 

812 # So, if that's the case we'll force using RC4 by converting 

813 # the password to lm/nt hashes and hope for the best. If that's already 

814 # done, byebye. 

815 if lmhash == '' and nthash == '' and (aesKey == '' or aesKey is None) and TGT is None and TGS is None: 

816 from impacket.ntlm import compute_lmhash, compute_nthash 

817 LOG.debug('Got KDC_ERR_ETYPE_NOSUPP, fallback to RC4') 

818 lmhash = compute_lmhash(password) 

819 nthash = compute_nthash(password) 

820 else: 

821 raise 

822 else: 

823 raise 

824 else: 

825 break 

826 else: 

827 tgs = TGS['KDC_REP'] 

828 cipher = TGS['cipher'] 

829 sessionKey = TGS['sessionKey'] 

830 break 

831 

832 # Let's build a NegTokenInit with a Kerberos REQ_AP 

833 

834 blob = SPNEGO_NegTokenInit() 

835 

836 # Kerberos 

837 blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']] 

838 

839 # Let's extract the ticket from the TGS 

840 tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0] 

841 ticket = Ticket() 

842 ticket.from_asn1(tgs['ticket']) 

843 

844 # Now let's build the AP_REQ 

845 apReq = AP_REQ() 

846 apReq['pvno'] = 5 

847 apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value) 

848 

849 opts = list() 

850 apReq['ap-options'] = constants.encodeFlags(opts) 

851 seq_set(apReq,'ticket', ticket.to_asn1) 

852 

853 authenticator = Authenticator() 

854 authenticator['authenticator-vno'] = 5 

855 authenticator['crealm'] = domain 

856 seq_set(authenticator, 'cname', userName.components_to_asn1) 

857 now = datetime.datetime.utcnow() 

858 

859 authenticator['cusec'] = now.microsecond 

860 authenticator['ctime'] = KerberosTime.to_asn1(now) 

861 

862 encodedAuthenticator = encoder.encode(authenticator) 

863 

864 # Key Usage 11 

865 # AP-REQ Authenticator (includes application authenticator 

866 # subkey), encrypted with the application session key 

867 # (Section 5.5.1) 

868 encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None) 

869 

870 apReq['authenticator'] = noValue 

871 apReq['authenticator']['etype'] = cipher.enctype 

872 apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator 

873 

874 blob['MechToken'] = encoder.encode(apReq) 

875 

876 login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON 

877 

878 login['SSPI'] = blob.getData() 

879 login['Length'] = len(login.getData()) 

880 

881 # Send the NTLMSSP Negotiate or SQL Auth Packet 

882 self.sendTDS(TDS_LOGIN7, login.getData()) 

883 

884 # According to the specs, if encryption is not required, we must encrypt just 

885 # the first Login packet :-o 

886 if resp['Encryption'] == TDS_ENCRYPT_OFF: 

887 self.tlsSocket = None 

888 

889 tds = self.recvTDS() 

890 

891 self.replies = self.parseReply(tds['Data']) 

892 

893 if TDS_LOGINACK_TOKEN in self.replies: 

894 return True 

895 else: 

896 return False 

897 

898 def login(self, database, username, password='', domain='', hashes = None, useWindowsAuth = False): 

899 

900 if hashes is not None: 

901 lmhash, nthash = hashes.split(':') 

902 lmhash = binascii.a2b_hex(lmhash) 

903 nthash = binascii.a2b_hex(nthash) 

904 else: 

905 lmhash = '' 

906 nthash = '' 

907 

908 resp = self.preLogin() 

909 # Test this! 

910 if resp['Encryption'] == TDS_ENCRYPT_REQ or resp['Encryption'] == TDS_ENCRYPT_OFF: 

911 LOG.info("Encryption required, switching to TLS") 

912 

913 # Switching to TLS now 

914 ctx = SSL.Context(SSL.TLSv1_METHOD) 

915 ctx.set_cipher_list('RC4, AES256') 

916 tls = SSL.Connection(ctx,None) 

917 tls.set_connect_state() 

918 while True: 

919 try: 

920 tls.do_handshake() 

921 except SSL.WantReadError: 

922 data = tls.bio_read(4096) 

923 self.sendTDS(TDS_PRE_LOGIN, data,0) 

924 tds = self.recvTDS() 

925 tls.bio_write(tds['Data']) 

926 else: 

927 break 

928 

929 # SSL and TLS limitation: Secure Socket Layer (SSL) and its replacement,  

930 # Transport Layer Security(TLS), limit data fragments to 16k in size. 

931 self.packetSize = 16*1024-1 

932 self.tlsSocket = tls 

933 

934 

935 login = TDS_LOGIN() 

936 

937 login['HostName'] = (''.join([random.choice(string.ascii_letters) for i in range(8)])).encode('utf-16le') 

938 login['AppName'] = (''.join([random.choice(string.ascii_letters) for i in range(8)])).encode('utf-16le') 

939 login['ServerName'] = self.server.encode('utf-16le') 

940 login['CltIntName'] = login['AppName'] 

941 login['ClientPID'] = random.randint(0,1024) 

942 login['PacketSize'] = self.packetSize 

943 if database is not None: 

944 login['Database'] = database.encode('utf-16le') 

945 login['OptionFlags2'] = TDS_INIT_LANG_FATAL | TDS_ODBC_ON 

946 

947 if useWindowsAuth is True: 

948 login['OptionFlags2'] |= TDS_INTEGRATED_SECURITY_ON 

949 # NTLMSSP Negotiate 

950 auth = ntlm.getNTLMSSPType1('','') 

951 login['SSPI'] = auth.getData() 

952 else: 

953 login['UserName'] = username.encode('utf-16le') 

954 login['Password'] = self.encryptPassword(password.encode('utf-16le')) 

955 login['SSPI'] = '' 

956 

957 

958 login['Length'] = len(login.getData()) 

959 

960 # Send the NTLMSSP Negotiate or SQL Auth Packet 

961 self.sendTDS(TDS_LOGIN7, login.getData()) 

962 

963 # According to the specs, if encryption is not required, we must encrypt just  

964 # the first Login packet :-o  

965 if resp['Encryption'] == TDS_ENCRYPT_OFF: 

966 self.tlsSocket = None 

967 

968 tds = self.recvTDS() 

969 

970 

971 if useWindowsAuth is True: 

972 serverChallenge = tds['Data'][3:] 

973 

974 # Generate the NTLM ChallengeResponse AUTH  

975 type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, serverChallenge, username, password, domain, lmhash, nthash) 

976 

977 self.sendTDS(TDS_SSPI, type3.getData()) 

978 tds = self.recvTDS() 

979 

980 self.replies = self.parseReply(tds['Data']) 

981 

982 if TDS_LOGINACK_TOKEN in self.replies: 

983 return True 

984 else: 

985 return False 

986 

987 

988 def processColMeta(self): 

989 for col in self.colMeta: 

990 if col['Type'] in [TDS_NVARCHARTYPE, TDS_NCHARTYPE, TDS_NTEXTTYPE]: 

991 col['Length'] = col['TypeData']//2 

992 fmt = '%%-%ds' 

993 elif col['Type'] in [TDS_GUIDTYPE]: 

994 col['Length'] = 36 

995 fmt = '%%%ds' 

996 elif col['Type'] in [TDS_DECIMALNTYPE,TDS_NUMERICNTYPE]: 

997 col['Length'] = ord(col['TypeData'][0:1]) 

998 fmt = '%%%ds' 

999 elif col['Type'] in [TDS_DATETIMNTYPE]: 

1000 col['Length'] = 19 

1001 fmt = '%%-%ds' 

1002 elif col['Type'] in [TDS_INT4TYPE, TDS_INTNTYPE]: 

1003 col['Length'] = 11 

1004 fmt = '%%%ds' 

1005 elif col['Type'] in [TDS_FLTNTYPE, TDS_MONEYNTYPE]: 

1006 col['Length'] = 25 

1007 fmt = '%%%ds' 

1008 elif col['Type'] in [TDS_BITNTYPE, TDS_BIGCHARTYPE]: 

1009 col['Length'] = col['TypeData'] 

1010 fmt = '%%%ds' 

1011 elif col['Type'] in [TDS_BIGBINARYTYPE, TDS_BIGVARBINTYPE]: 

1012 col['Length'] = col['TypeData'] * 2 

1013 fmt = '%%%ds' 

1014 elif col['Type'] in [TDS_TEXTTYPE, TDS_BIGVARCHRTYPE]: 

1015 col['Length'] = col['TypeData'] 

1016 fmt = '%%-%ds' 

1017 else: 

1018 col['Length'] = 10 

1019 fmt = '%%%ds' 

1020 

1021 if len(col['Name']) > col['Length']: 

1022 col['Length'] = len(col['Name']) 

1023 elif col['Length'] > self.MAX_COL_LEN: 

1024 col['Length'] = self.MAX_COL_LEN 

1025 

1026 col['Format'] = fmt % col['Length'] 

1027 

1028 

1029 def printColumnsHeader(self): 

1030 if len(self.colMeta) == 0: 

1031 return 

1032 for col in self.colMeta: 

1033 self.__rowsPrinter.logMessage(col['Format'] % col['Name'] + self.COL_SEPARATOR) 

1034 self.__rowsPrinter.logMessage('\n') 

1035 for col in self.colMeta: 

1036 self.__rowsPrinter.logMessage('-'*col['Length'] + self.COL_SEPARATOR) 

1037 self.__rowsPrinter.logMessage('\n') 

1038 

1039 

1040 def printRows(self): 

1041 if self.lastError is True: 

1042 return 

1043 self.processColMeta() 

1044 self.printColumnsHeader() 

1045 for row in self.rows: 

1046 for col in self.colMeta: 

1047 self.__rowsPrinter.logMessage(col['Format'] % row[col['Name']] + self.COL_SEPARATOR) 

1048 self.__rowsPrinter.logMessage('\n') 

1049 

1050 def printReplies(self): 

1051 for keys in list(self.replies.keys()): 

1052 for i, key in enumerate(self.replies[keys]): 

1053 if key['TokenType'] == TDS_ERROR_TOKEN: 

1054 error = "ERROR(%s): Line %d: %s" % (key['ServerName'].decode('utf-16le'), key['LineNumber'], key['MsgText'].decode('utf-16le')) 

1055 self.lastError = SQLErrorException("ERROR: Line %d: %s" % (key['LineNumber'], key['MsgText'].decode('utf-16le'))) 

1056 LOG.error(error) 

1057 

1058 elif key['TokenType'] == TDS_INFO_TOKEN: 

1059 LOG.info("INFO(%s): Line %d: %s" % (key['ServerName'].decode('utf-16le'), key['LineNumber'], key['MsgText'].decode('utf-16le'))) 

1060 

1061 elif key['TokenType'] == TDS_LOGINACK_TOKEN: 

1062 LOG.info("ACK: Result: %s - %s (%d%d %d%d) " % (key['Interface'], key['ProgName'].decode('utf-16le'), key['MajorVer'], key['MinorVer'], key['BuildNumHi'], key['BuildNumLow'])) 

1063 

1064 elif key['TokenType'] == TDS_ENVCHANGE_TOKEN: 

1065 if key['Type'] in (TDS_ENVCHANGE_DATABASE, TDS_ENVCHANGE_LANGUAGE, TDS_ENVCHANGE_CHARSET, TDS_ENVCHANGE_PACKETSIZE): 

1066 record = TDS_ENVCHANGE_VARCHAR(key['Data']) 

1067 if record['OldValue'] == '': 

1068 record['OldValue'] = 'None'.encode('utf-16le') 

1069 elif record['NewValue'] == '': 

1070 record['NewValue'] = 'None'.encode('utf-16le') 

1071 if key['Type'] == TDS_ENVCHANGE_DATABASE: 

1072 _type = 'DATABASE' 

1073 elif key['Type'] == TDS_ENVCHANGE_LANGUAGE: 

1074 _type = 'LANGUAGE' 

1075 elif key['Type'] == TDS_ENVCHANGE_CHARSET: 

1076 _type = 'CHARSET' 

1077 elif key['Type'] == TDS_ENVCHANGE_PACKETSIZE: 

1078 _type = 'PACKETSIZE' 

1079 else: 

1080 _type = "%d" % key['Type'] 

1081 LOG.info("ENVCHANGE(%s): Old Value: %s, New Value: %s" % (_type,record['OldValue'].decode('utf-16le'), record['NewValue'].decode('utf-16le'))) 

1082 

1083 def parseRow(self,token,tuplemode=False): 

1084 # TODO: This REALLY needs to be improved. Right now we don't support correctly all the data types 

1085 # help would be appreciated ;)  

1086 if len(token) == 1: 

1087 return 0 

1088 

1089 row = [] if tuplemode else {} 

1090 

1091 origDataLen = len(token['Data']) 

1092 data = token['Data'] 

1093 for col in self.colMeta: 

1094 _type = col['Type'] 

1095 if (_type == TDS_NVARCHARTYPE) |\ 

1096 (_type == TDS_NCHARTYPE): 

1097 #print "NVAR 0x%x" % _type 

1098 charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] 

1099 data = data[struct.calcsize('<H'):] 

1100 if charLen != 0xFFFF: 

1101 value = data[:charLen].decode('utf-16le') 

1102 data = data[charLen:] 

1103 else: 

1104 value = 'NULL' 

1105 

1106 elif _type == TDS_BIGVARCHRTYPE: 

1107 charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] 

1108 data = data[struct.calcsize('<H'):] 

1109 if charLen != 0xFFFF: 

1110 value = data[:charLen] 

1111 data = data[charLen:] 

1112 else: 

1113 value = 'NULL' 

1114 

1115 elif _type == TDS_GUIDTYPE: 

1116 uuidLen = ord(data[0:1]) 

1117 data = data[1:] 

1118 if uuidLen > 0: 

1119 uu = data[:uuidLen] 

1120 value = uuid.bin_to_string(uu) 

1121 data = data[uuidLen:] 

1122 else: 

1123 value = 'NULL' 

1124 

1125 elif (_type == TDS_NTEXTTYPE) |\ 

1126 (_type == TDS_IMAGETYPE) : 

1127 # Skip the pointer data 

1128 charLen = ord(data[0:1]) 

1129 if charLen == 0: 

1130 value = 'NULL' 

1131 data = data[1:] 

1132 else: 

1133 data = data[1+charLen+8:] 

1134 charLen = struct.unpack('<L',data[:struct.calcsize('<L')])[0] 

1135 data = data[struct.calcsize('<L'):] 

1136 if charLen != 0xFFFF: 

1137 if _type == TDS_NTEXTTYPE: 

1138 value = data[:charLen].decode('utf-16le') 

1139 else: 

1140 value = binascii.b2a_hex(data[:charLen]) 

1141 data = data[charLen:] 

1142 else: 

1143 value = 'NULL' 

1144 

1145 elif _type == TDS_TEXTTYPE: 

1146 # Skip the pointer data 

1147 charLen = ord(data[0:1]) 

1148 if charLen == 0: 

1149 value = 'NULL' 

1150 data = data[1:] 

1151 else: 

1152 data = data[1+charLen+8:] 

1153 charLen = struct.unpack('<L',data[:struct.calcsize('<L')])[0] 

1154 data = data[struct.calcsize('<L'):] 

1155 if charLen != 0xFFFF: 

1156 value = data[:charLen] 

1157 data = data[charLen:] 

1158 else: 

1159 value = 'NULL' 

1160 

1161 elif (_type == TDS_BIGVARBINTYPE) |\ 

1162 (_type == TDS_BIGBINARYTYPE): 

1163 charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] 

1164 data = data[struct.calcsize('<H'):] 

1165 if charLen != 0xFFFF: 

1166 value = binascii.b2a_hex(data[:charLen]) 

1167 data = data[charLen:] 

1168 else: 

1169 value = 'NULL' 

1170 

1171 elif (_type == TDS_DATETIM4TYPE) |\ 

1172 (_type == TDS_DATETIMNTYPE) |\ 

1173 (_type == TDS_DATETIMETYPE): 

1174 value = '' 

1175 if _type == TDS_DATETIMNTYPE: 

1176 # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and 

1177 # datetime SQL data _types respectively. 

1178 if ord(data[0:1]) == 4: 

1179 _type = TDS_DATETIM4TYPE 

1180 elif ord(data[0:1]) == 8: 

1181 _type = TDS_DATETIMETYPE 

1182 else: 

1183 value = 'NULL' 

1184 data = data[1:] 

1185 if _type == TDS_DATETIMETYPE: 

1186 # datetime is represented in the following sequence: 

1187 # * One 4-byte signed integer that represents the number of days since January 1, 1900. Negative 

1188 # numbers are allowed to represents dates since January 1, 1753. 

1189 # * One 4-byte unsigned integer that represents the number of one three-hundredths of a second 

1190 # (300 counts per second) elapsed since 12 AM that day. 

1191 dateValue = struct.unpack('<l',data[:4])[0] 

1192 data = data[4:] 

1193 if dateValue < 0: 

1194 baseDate = datetime.date(1753,1,1) 

1195 else: 

1196 baseDate = datetime.date(1900,1,1) 

1197 timeValue = struct.unpack('<L',data[:4])[0] 

1198 data = data[4:] 

1199 elif _type == TDS_DATETIM4TYPE: 

1200 # Small datetime 

1201 # 2.2.5.5.1.8 

1202 # Date/Times 

1203 # smalldatetime is represented in the following sequence: 

1204 # * One 2-byte unsigned integer that represents the number of days since January 1, 1900. 

1205 # * One 2-byte unsigned integer that represents the number of minutes elapsed since 12 AM that 

1206 # day. 

1207 dateValue = struct.unpack('<H',data[:struct.calcsize('<H')])[0] 

1208 data = data[struct.calcsize('<H'):] 

1209 timeValue = struct.unpack('<H',data[:struct.calcsize('<H')])[0] 

1210 data = data[struct.calcsize('<H'):] 

1211 baseDate = datetime.date(1900,1,1) 

1212 if value != 'NULL': 

1213 dateValue = datetime.date.fromordinal(baseDate.toordinal() + dateValue) 

1214 hours, mod = divmod(timeValue//300, 60*60) 

1215 minutes, second = divmod(mod, 60) 

1216 value = datetime.datetime(dateValue.year, dateValue.month, dateValue.day, hours, minutes, second) 

1217 

1218 elif (_type == TDS_INT4TYPE) |\ 

1219 (_type == TDS_MONEY4TYPE) |\ 

1220 (_type == TDS_FLT4TYPE): 

1221 #print "INT4" 

1222 value = struct.unpack('<l',data[:struct.calcsize('<l')])[0] 

1223 data = data[struct.calcsize('<l'):] 

1224 

1225 elif _type == TDS_FLTNTYPE: 

1226 valueSize = ord(data[:1]) 

1227 if valueSize == 4: 

1228 fmt = '<f' 

1229 elif valueSize == 8: 

1230 fmt = '<d' 

1231 

1232 data = data[1:] 

1233 

1234 if valueSize > 0: 

1235 value = struct.unpack(fmt,data[:valueSize])[0] 

1236 data = data[valueSize:] 

1237 else: 

1238 value = 'NULL' 

1239 

1240 elif _type == TDS_MONEYNTYPE: 

1241 valueSize = ord(data[:1]) 

1242 if valueSize == 4: 

1243 fmt = '<l' 

1244 elif valueSize == 8: 

1245 fmt = '<q' 

1246 

1247 data = data[1:] 

1248 

1249 if valueSize > 0: 

1250 value = struct.unpack(fmt,data[:valueSize])[0] 

1251 if valueSize == 4: 

1252 value = float(value) // math.pow(10,4) 

1253 else: 

1254 value = float(value >> 32) // math.pow(10,4) 

1255 data = data[valueSize:] 

1256 else: 

1257 value = 'NULL' 

1258 

1259 

1260 elif _type == TDS_BIGCHARTYPE: 

1261 #print "BIGC" 

1262 charLen = struct.unpack('<H',data[:struct.calcsize('<H')])[0] 

1263 data = data[struct.calcsize('<H'):] 

1264 value = data[:charLen] 

1265 data = data[charLen:] 

1266 

1267 elif (_type == TDS_INT8TYPE) |\ 

1268 (_type == TDS_FLT8TYPE) |\ 

1269 (_type == TDS_MONEYTYPE): 

1270 #print "DATETIME" 

1271 value = struct.unpack('<q',data[:struct.calcsize('<q')])[0] 

1272 data = data[struct.calcsize('<q'):] 

1273 

1274 

1275 elif _type == TDS_INT2TYPE: 

1276 #print "INT2TYPE" 

1277 value = struct.unpack('<H',(data[:2]))[0] 

1278 data = data[2:] 

1279 

1280 elif _type == TDS_DATENTYPE: 

1281 # date is represented as one 3-byte unsigned integer that represents the number of days since 

1282 # January 1, year 1. 

1283 valueSize = ord(data[:1]) 

1284 data = data[1:] 

1285 if valueSize > 0: 

1286 dateBytes = data[:valueSize] 

1287 dateValue = struct.unpack('<L','\x00'+dateBytes)[0] 

1288 value = datetime.date.fromtimestamp(dateValue) 

1289 data = data[valueSize:] 

1290 else: 

1291 value = 'NULL' 

1292 

1293 elif (_type == TDS_BITTYPE) |\ 

1294 (_type == TDS_INT1TYPE): 

1295 #print "BITTYPE" 

1296 value = ord(data[:1]) 

1297 data = data[1:] 

1298 

1299 elif (_type == TDS_NUMERICNTYPE) |\ 

1300 (_type == TDS_DECIMALNTYPE): 

1301 valueLen = ord(data[:1]) 

1302 data = data[1:] 

1303 value = data[:valueLen] 

1304 data = data[valueLen:] 

1305 precision = ord(col['TypeData'][1:2]) 

1306 scale = ord(col['TypeData'][2:3]) 

1307 if valueLen > 0: 

1308 isPositiveSign = ord(value[0:1]) 

1309 if (valueLen-1) == 2: 

1310 fmt = '<H' 

1311 elif (valueLen-1) == 4: 

1312 fmt = '<L' 

1313 elif (valueLen-1) == 8: 

1314 fmt = '<Q' 

1315 else: 

1316 # Still don't know how to handle higher values 

1317 value = "TODO: Interpret TDS_NUMERICNTYPE correctly" 

1318 number = struct.unpack(fmt, value[1:])[0] 

1319 number //= math.pow(precision, scale) 

1320 if isPositiveSign == 0: 

1321 number *= -1 

1322 value = number 

1323 else: 

1324 value = 'NULL' 

1325 

1326 elif _type == TDS_BITNTYPE: 

1327 #print "BITNTYPE" 

1328 valueSize = ord(data[:1]) 

1329 data = data[1:] 

1330 if valueSize > 0: 

1331 if valueSize == 1: 

1332 value = ord(data[:valueSize]) 

1333 else: 

1334 value = data[:valueSize] 

1335 else: 

1336 value = 'NULL' 

1337 data = data[valueSize:] 

1338 

1339 elif _type == TDS_INTNTYPE: 

1340 valueSize = ord(data[:1]) 

1341 if valueSize == 1: 

1342 fmt = '<B' 

1343 elif valueSize == 2: 

1344 fmt = '<h' 

1345 elif valueSize == 4: 

1346 fmt = '<l' 

1347 elif valueSize == 8: 

1348 fmt = '<q' 

1349 else: 

1350 fmt = '' 

1351 

1352 data = data[1:] 

1353 

1354 if valueSize > 0: 

1355 value = struct.unpack(fmt,data[:valueSize])[0] 

1356 data = data[valueSize:] 

1357 else: 

1358 value = 'NULL' 

1359 elif _type == TDS_SSVARIANTTYPE: 

1360 raise Exception("ParseRow: SQL Variant type not yet supported :(") 

1361 else: 

1362 raise Exception("ParseROW: Unsupported data type: 0%x" % _type) 

1363 

1364 if tuplemode: 

1365 row.append(value) 

1366 else: 

1367 row[col['Name']] = value 

1368 

1369 

1370 self.rows.append(row) 

1371 

1372 return origDataLen - len(data) 

1373 

1374 def parseColMetaData(self, token): 

1375 # TODO Add support for more data types! 

1376 count = token['Count'] 

1377 if count == 0xFFFF: 

1378 return 0 

1379 

1380 self.colMeta = [] 

1381 origDataLen = len(token['Data']) 

1382 data = token['Data'] 

1383 for i in range(count): 

1384 column = {} 

1385 userType = struct.unpack('<H',data[:struct.calcsize('<H')])[0] 

1386 data = data[struct.calcsize('<H'):] 

1387 flags = struct.unpack('<H',data[:struct.calcsize('<H')])[0] 

1388 data = data[struct.calcsize('<H'):] 

1389 colType = struct.unpack('<B',data[:struct.calcsize('<B')])[0] 

1390 data = data[struct.calcsize('<B'):] 

1391 if (colType == TDS_BITTYPE) |\ 

1392 (colType == TDS_INT1TYPE) |\ 

1393 (colType == TDS_INT2TYPE) |\ 

1394 (colType == TDS_INT8TYPE) |\ 

1395 (colType == TDS_DATETIMETYPE) |\ 

1396 (colType == TDS_DATETIM4TYPE) |\ 

1397 (colType == TDS_FLT4TYPE) |\ 

1398 (colType == TDS_FLT8TYPE) |\ 

1399 (colType == TDS_MONEYTYPE) |\ 

1400 (colType == TDS_MONEY4TYPE) |\ 

1401 (colType == TDS_DATENTYPE) |\ 

1402 (colType == TDS_INT4TYPE): 

1403 typeData = '' 

1404 elif (colType == TDS_INTNTYPE) |\ 

1405 (colType == TDS_TIMENTYPE) |\ 

1406 (colType == TDS_DATETIME2NTYPE) |\ 

1407 (colType == TDS_DATETIMEOFFSETNTYPE) |\ 

1408 (colType == TDS_FLTNTYPE) |\ 

1409 (colType == TDS_MONEYNTYPE) |\ 

1410 (colType == TDS_GUIDTYPE) |\ 

1411 (colType == TDS_BITNTYPE): 

1412 typeData = ord(data[0:1]) 

1413 data = data[1:] 

1414 

1415 elif colType == TDS_DATETIMNTYPE: 

1416 # For DATETIMNTYPE, the only valid lengths are 0x04 and 0x08, which map to smalldatetime and 

1417 # datetime SQL data types respectively. 

1418 typeData = ord(data[0:1]) 

1419 data = data[1:] 

1420 

1421 elif (colType == TDS_BIGVARBINTYPE) |\ 

1422 (colType == TDS_BIGBINARYTYPE) |\ 

1423 (colType == TDS_NCHARTYPE) |\ 

1424 (colType == TDS_NVARCHARTYPE) |\ 

1425 (colType == TDS_BIGVARCHRTYPE) |\ 

1426 (colType == TDS_BIGCHARTYPE): 

1427 typeData = struct.unpack('<H',data[:2])[0] 

1428 data = data[2:] 

1429 elif (colType == TDS_DECIMALNTYPE) |\ 

1430 (colType == TDS_NUMERICNTYPE) |\ 

1431 (colType == TDS_DECIMALTYPE): 

1432 typeData = data[:3] 

1433 data = data[3:] 

1434 elif (colType == TDS_IMAGETYPE) |\ 

1435 (colType == TDS_TEXTTYPE) |\ 

1436 (colType == TDS_XMLTYPE) |\ 

1437 (colType == TDS_SSVARIANTTYPE) |\ 

1438 (colType == TDS_NTEXTTYPE): 

1439 typeData = struct.unpack('<L',data[:4])[0] 

1440 data = data[4:] 

1441 else: 

1442 raise Exception("Unsupported data type: 0x%x" % colType) 

1443 

1444 # Collation exceptions: 

1445 if (colType == TDS_NTEXTTYPE) |\ 

1446 (colType == TDS_BIGCHARTYPE) |\ 

1447 (colType == TDS_BIGVARCHRTYPE) |\ 

1448 (colType == TDS_NCHARTYPE) |\ 

1449 (colType == TDS_NVARCHARTYPE) |\ 

1450 (colType == TDS_TEXTTYPE): 

1451 # Skip collation 

1452 data = data[5:] 

1453 

1454 # PartTableName exceptions: 

1455 if (colType == TDS_IMAGETYPE) |\ 

1456 (colType == TDS_TEXTTYPE) |\ 

1457 (colType == TDS_NTEXTTYPE): 

1458 # This types have Table Elements, we just discard them for now. 

1459 # ToDo parse this correctly! 

1460 # Get the Length 

1461 dataLen = struct.unpack('<H',data[:2])[0] 

1462 data = data[2:] 

1463 # skip the text 

1464 data = data[dataLen*2:] 

1465 

1466 colNameLength = struct.unpack('<B',data[:struct.calcsize('<B')])[0] 

1467 data = data[struct.calcsize('<B'):] 

1468 colName = data[:colNameLength*2].decode('utf-16le') 

1469 data = data[colNameLength*2:] 

1470 column['Name'] = colName 

1471 column['Type'] = colType 

1472 column['TypeData'] = typeData 

1473 column['Flags'] = flags 

1474 self.colMeta.append(column) 

1475 

1476 return origDataLen - len(data) 

1477 

1478 def parseReply(self, tokens,tuplemode=False): 

1479 if len(tokens) == 0: 

1480 return False 

1481 

1482 replies = {} 

1483 while len(tokens) > 0: 

1484 tokenID = struct.unpack('B',tokens[0:1])[0] 

1485 if tokenID == TDS_ERROR_TOKEN: 

1486 token = TDS_INFO_ERROR(tokens) 

1487 elif tokenID == TDS_RETURNSTATUS_TOKEN: 

1488 token = TDS_RETURNSTATUS(tokens) 

1489 elif tokenID == TDS_INFO_TOKEN: 

1490 token = TDS_INFO_ERROR(tokens) 

1491 elif tokenID == TDS_LOGINACK_TOKEN: 

1492 token = TDS_LOGIN_ACK(tokens) 

1493 elif tokenID == TDS_ENVCHANGE_TOKEN: 

1494 token = TDS_ENVCHANGE(tokens) 

1495 if token['Type'] is TDS_ENVCHANGE_PACKETSIZE: 

1496 record = TDS_ENVCHANGE_VARCHAR(token['Data']) 

1497 self.packetSize = int( record['NewValue'].decode('utf-16le') ) 

1498 elif token['Type'] is TDS_ENVCHANGE_DATABASE: 

1499 record = TDS_ENVCHANGE_VARCHAR(token['Data']) 

1500 self.currentDB = record['NewValue'].decode('utf-16le') 

1501 

1502 elif (tokenID == TDS_DONEINPROC_TOKEN) |\ 

1503 (tokenID == TDS_DONEPROC_TOKEN): 

1504 token = TDS_DONEINPROC(tokens) 

1505 elif tokenID == TDS_ORDER_TOKEN: 

1506 token = TDS_ORDER(tokens) 

1507 elif tokenID == TDS_ROW_TOKEN: 

1508 #print "ROW" 

1509 token = TDS_ROW(tokens) 

1510 tokenLen = self.parseRow(token,tuplemode) 

1511 token['Data'] = token['Data'][:tokenLen] 

1512 elif tokenID == TDS_COLMETADATA_TOKEN: 

1513 #print "COLMETA" 

1514 token = TDS_COLMETADATA(tokens) 

1515 tokenLen = self.parseColMetaData(token) 

1516 token['Data'] = token['Data'][:tokenLen] 

1517 elif tokenID == TDS_DONE_TOKEN: 

1518 token = TDS_DONE(tokens) 

1519 else: 

1520 LOG.error("Unknown Token %x" % tokenID) 

1521 return replies 

1522 

1523 if (tokenID in replies) is not True: 

1524 replies[tokenID] = list() 

1525 

1526 replies[tokenID].append(token) 

1527 tokens = tokens[len(token):] 

1528 #print "TYPE 0x%x, LEN: %d" %(tokenID, len(token)) 

1529 #print repr(tokens[:10]) 

1530 

1531 return replies 

1532 

1533 def batch(self, cmd,tuplemode=False,wait=True): 

1534 # First of all we clear the rows, colMeta and lastError 

1535 self.rows = [] 

1536 self.colMeta = [] 

1537 self.lastError = False 

1538 self.sendTDS(TDS_SQL_BATCH, (cmd+'\r\n').encode('utf-16le')) 

1539 if wait: 

1540 tds = self.recvTDS() 

1541 self.replies = self.parseReply(tds['Data'],tuplemode) 

1542 return self.rows 

1543 else: 

1544 return True 

1545 

1546 

1547 def batchStatement(self, cmd,tuplemode=False): 

1548 # First of all we clear the rows, colMeta and lastError 

1549 self.rows = [] 

1550 self.colMeta = [] 

1551 self.lastError = False 

1552 self.sendTDS(TDS_SQL_BATCH, (cmd+'\r\n').encode('utf-16le')) 

1553 #self.recvTDS()  

1554 

1555 

1556 # Handy alias 

1557 sql_query = batch 

1558 

1559 def changeDB(self, db): 

1560 if db != self.currentDB: 

1561 chdb = 'use %s' % db 

1562 self.batch(chdb) 

1563 self.printReplies() 

1564 

1565 def RunSQLQuery(self,db,sql_query,tuplemode=False,wait=True,**kwArgs): 

1566 db = db or 'master' 

1567 self.changeDB(db) 

1568 self.printReplies() 

1569 ret = self.batch(sql_query,tuplemode,wait) 

1570 if wait: 

1571 self.printReplies() 

1572 if self.lastError: 

1573 raise self.lastError 

1574 if self.lastError: 

1575 raise self.lastError 

1576 return ret 

1577 

1578 def RunSQLStatement(self,db,sql_query,wait=True,**kwArgs): 

1579 self.RunSQLQuery(db,sql_query,wait=wait) 

1580 if self.lastError: 

1581 raise self.lastError 

1582 return True