rimossi cloudscraper, simplejson e torrentool, aggiornato sambatools
This commit is contained in:
144
lib/sambatools/python2/nmb/NetBIOS.py
Normal file
144
lib/sambatools/python2/nmb/NetBIOS.py
Normal file
@@ -0,0 +1,144 @@
|
||||
|
||||
import os, logging, random, socket, time, select
|
||||
from base import NBNS, NotConnectedError
|
||||
from nmb_constants import TYPE_CLIENT, TYPE_SERVER, TYPE_WORKSTATION
|
||||
|
||||
class NetBIOS(NBNS):
|
||||
|
||||
log = logging.getLogger('NMB.NetBIOS')
|
||||
|
||||
def __init__(self, broadcast = True, listen_port = 0):
|
||||
"""
|
||||
Instantiate a NetBIOS instance, and creates a IPv4 UDP socket to listen/send NBNS packets.
|
||||
|
||||
:param boolean broadcast: A boolean flag to indicate if we should setup the listening UDP port in broadcast mode
|
||||
:param integer listen_port: Specifies the UDP port number to bind to for listening. If zero, OS will automatically select a free port number.
|
||||
"""
|
||||
self.broadcast = broadcast
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
if self.broadcast:
|
||||
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
if listen_port:
|
||||
self.sock.bind(( '', listen_port ))
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close the underlying and free resources.
|
||||
|
||||
The NetBIOS instance should not be used to perform any operations after this method returns.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
|
||||
def write(self, data, ip, port):
|
||||
assert self.sock, 'Socket is already closed'
|
||||
self.sock.sendto(data, ( ip, port ))
|
||||
|
||||
def queryName(self, name, ip = '', port = 137, timeout = 30):
|
||||
"""
|
||||
Send a query on the network and hopes that if machine matching the *name* will reply with its IP address.
|
||||
|
||||
:param string ip: If the NBNSProtocol instance was instianted with broadcast=True, then this parameter can be an empty string. We will leave it to the OS to determine an appropriate broadcast address.
|
||||
If the NBNSProtocol instance was instianted with broadcast=False, then you should provide a target IP to send the query.
|
||||
:param integer port: The NetBIOS-NS port (IANA standard defines this port to be 137). You should not touch this parameter unless you know what you are doing.
|
||||
:param integer/float timeout: Number of seconds to wait for a reply, after which the method will return None
|
||||
:return: A list of IP addresses in dotted notation (aaa.bbb.ccc.ddd). On timeout, returns None.
|
||||
"""
|
||||
assert self.sock, 'Socket is already closed'
|
||||
|
||||
trn_id = random.randint(1, 0xFFFF)
|
||||
data = self.prepareNameQuery(trn_id, name)
|
||||
if self.broadcast and not ip:
|
||||
ip = '<broadcast>'
|
||||
elif not ip:
|
||||
self.log.warning('queryName: ip parameter is empty. OS might not transmit this query to the network')
|
||||
|
||||
self.write(data, ip, port)
|
||||
|
||||
return self._pollForNetBIOSPacket(trn_id, timeout)
|
||||
|
||||
def queryIPForName(self, ip, port = 137, timeout = 30):
|
||||
"""
|
||||
Send a query to the machine with *ip* and hopes that the machine will reply back with its name.
|
||||
|
||||
The implementation of this function is contributed by Jason Anderson.
|
||||
|
||||
:param string ip: If the NBNSProtocol instance was instianted with broadcast=True, then this parameter can be an empty string. We will leave it to the OS to determine an appropriate broadcast address.
|
||||
If the NBNSProtocol instance was instianted with broadcast=False, then you should provide a target IP to send the query.
|
||||
:param integer port: The NetBIOS-NS port (IANA standard defines this port to be 137). You should not touch this parameter unless you know what you are doing.
|
||||
:param integer/float timeout: Number of seconds to wait for a reply, after which the method will return None
|
||||
:return: A list of string containing the names of the machine at *ip*. On timeout, returns None.
|
||||
"""
|
||||
assert self.sock, 'Socket is already closed'
|
||||
|
||||
trn_id = random.randint(1, 0xFFFF)
|
||||
data = self.prepareNetNameQuery(trn_id, False)
|
||||
self.write(data, ip, port)
|
||||
ret = self._pollForQueryPacket(trn_id, timeout)
|
||||
if ret:
|
||||
return map(lambda s: s[0], filter(lambda s: s[1] == TYPE_SERVER, ret))
|
||||
else:
|
||||
return None
|
||||
|
||||
#
|
||||
# Protected Methods
|
||||
#
|
||||
|
||||
def _pollForNetBIOSPacket(self, wait_trn_id, timeout):
|
||||
end_time = time.time() + timeout
|
||||
while True:
|
||||
try:
|
||||
_timeout = end_time - time.time()
|
||||
if _timeout <= 0:
|
||||
return None
|
||||
|
||||
ready, _, _ = select.select([ self.sock.fileno() ], [ ], [ ], _timeout)
|
||||
if not ready:
|
||||
return None
|
||||
|
||||
data, _ = self.sock.recvfrom(0xFFFF)
|
||||
if len(data) == 0:
|
||||
raise NotConnectedError
|
||||
|
||||
trn_id, ret = self.decodePacket(data)
|
||||
|
||||
if trn_id == wait_trn_id:
|
||||
return ret
|
||||
except select.error, ex:
|
||||
if type(ex) is types.TupleType:
|
||||
if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
|
||||
raise ex
|
||||
else:
|
||||
raise ex
|
||||
|
||||
#
|
||||
# Contributed by Jason Anderson
|
||||
#
|
||||
def _pollForQueryPacket(self, wait_trn_id, timeout):
|
||||
end_time = time.time() + timeout
|
||||
while True:
|
||||
try:
|
||||
_timeout = end_time - time.time()
|
||||
if _timeout <= 0:
|
||||
return None
|
||||
|
||||
ready, _, _ = select.select([ self.sock.fileno() ], [ ], [ ], _timeout)
|
||||
if not ready:
|
||||
return None
|
||||
|
||||
data, _ = self.sock.recvfrom(0xFFFF)
|
||||
if len(data) == 0:
|
||||
raise NotConnectedError
|
||||
|
||||
trn_id, ret = self.decodeIPQueryPacket(data)
|
||||
|
||||
if trn_id == wait_trn_id:
|
||||
return ret
|
||||
except select.error, ex:
|
||||
if type(ex) is types.TupleType:
|
||||
if ex[0] != errno.EINTR and ex[0] != errno.EAGAIN:
|
||||
raise ex
|
||||
else:
|
||||
raise ex
|
||||
136
lib/sambatools/python2/nmb/NetBIOSProtocol.py
Normal file
136
lib/sambatools/python2/nmb/NetBIOSProtocol.py
Normal file
@@ -0,0 +1,136 @@
|
||||
|
||||
import os, logging, random, socket, time
|
||||
from twisted.internet import reactor, defer
|
||||
from twisted.internet.protocol import DatagramProtocol
|
||||
from nmb_constants import TYPE_SERVER
|
||||
from base import NBNS
|
||||
|
||||
IP_QUERY, NAME_QUERY = range(2)
|
||||
|
||||
class NetBIOSTimeout(Exception):
|
||||
"""Raised in NBNSProtocol via Deferred.errback method when queryName method has timeout waiting for reply"""
|
||||
pass
|
||||
|
||||
class NBNSProtocol(DatagramProtocol, NBNS):
|
||||
|
||||
log = logging.getLogger('NMB.NBNSProtocol')
|
||||
|
||||
def __init__(self, broadcast = True, listen_port = 0):
|
||||
"""
|
||||
Instantiate a NBNSProtocol instance.
|
||||
|
||||
This automatically calls reactor.listenUDP method to start listening for incoming packets, so you **must not** call the listenUDP method again.
|
||||
|
||||
:param boolean broadcast: A boolean flag to indicate if we should setup the listening UDP port in broadcast mode
|
||||
:param integer listen_port: Specifies the UDP port number to bind to for listening. If zero, OS will automatically select a free port number.
|
||||
"""
|
||||
self.broadcast = broadcast
|
||||
self.pending_trns = { } # TRN ID -> ( expiry_time, name, Deferred instance )
|
||||
self.transport = reactor.listenUDP(listen_port, self)
|
||||
if self.broadcast:
|
||||
self.transport.getHandle().setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
reactor.callLater(1, self.cleanupPendingTrns)
|
||||
|
||||
def datagramReceived(self, data, from_info):
|
||||
host, port = from_info
|
||||
trn_id, ret = self.decodePacket(data)
|
||||
|
||||
# pending transaction exists for trn_id - handle it and remove from queue
|
||||
if trn_id in self.pending_trns:
|
||||
_, ip, d = self.pending_trns.pop(trn_id)
|
||||
if ip is NAME_QUERY:
|
||||
# decode as query packet
|
||||
trn_id, ret = self.decodeIPQueryPacket(data)
|
||||
d.callback(ret)
|
||||
|
||||
def write(self, data, ip, port):
|
||||
# We don't use the transport.write method directly as it keeps raising DeprecationWarning for ip='<broadcast>'
|
||||
self.transport.getHandle().sendto(data, ( ip, port ))
|
||||
|
||||
def queryName(self, name, ip = '', port = 137, timeout = 30):
|
||||
"""
|
||||
Send a query on the network and hopes that if machine matching the *name* will reply with its IP address.
|
||||
|
||||
:param string ip: If the NBNSProtocol instance was instianted with broadcast=True, then this parameter can be an empty string. We will leave it to the OS to determine an appropriate broadcast address.
|
||||
If the NBNSProtocol instance was instianted with broadcast=False, then you should provide a target IP to send the query.
|
||||
:param integer port: The NetBIOS-NS port (IANA standard defines this port to be 137). You should not touch this parameter unless you know what you are doing.
|
||||
:param integer/float timeout: Number of seconds to wait for a reply, after which the returned Deferred instance will be called with a NetBIOSTimeout exception.
|
||||
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a list of IP addresses in dotted notation (aaa.bbb.ccc.ddd).
|
||||
On timeout, the errback function will be called with a Failure instance wrapping around a NetBIOSTimeout exception
|
||||
"""
|
||||
trn_id = random.randint(1, 0xFFFF)
|
||||
while True:
|
||||
if not self.pending_trns.has_key(trn_id):
|
||||
break
|
||||
else:
|
||||
trn_id = (trn_id + 1) & 0xFFFF
|
||||
|
||||
data = self.prepareNameQuery(trn_id, name)
|
||||
if self.broadcast and not ip:
|
||||
ip = '<broadcast>'
|
||||
elif not ip:
|
||||
self.log.warning('queryName: ip parameter is empty. OS might not transmit this query to the network')
|
||||
|
||||
self.write(data, ip, port)
|
||||
|
||||
d = defer.Deferred()
|
||||
self.pending_trns[trn_id] = ( time.time()+timeout, name, d )
|
||||
return d
|
||||
|
||||
def queryIPForName(self, ip, port = 137, timeout = 30):
|
||||
"""
|
||||
Send a query to the machine with *ip* and hopes that the machine will reply back with its name.
|
||||
|
||||
The implementation of this function is contributed by Jason Anderson.
|
||||
|
||||
:param string ip: If the NBNSProtocol instance was instianted with broadcast=True, then this parameter can be an empty string. We will leave it to the OS to determine an appropriate broadcast address.
|
||||
If the NBNSProtocol instance was instianted with broadcast=False, then you should provide a target IP to send the query.
|
||||
:param integer port: The NetBIOS-NS port (IANA standard defines this port to be 137). You should not touch this parameter unless you know what you are doing.
|
||||
:param integer/float timeout: Number of seconds to wait for a reply, after which the method will return None
|
||||
:return: A *twisted.internet.defer.Deferred* instance. The callback function will be called with a list of names of the machine at *ip*.
|
||||
On timeout, the errback function will be called with a Failure instance wrapping around a NetBIOSTimeout exception
|
||||
"""
|
||||
trn_id = random.randint(1, 0xFFFF)
|
||||
while True:
|
||||
if not self.pending_trns.has_key(trn_id):
|
||||
break
|
||||
else:
|
||||
trn_id = (trn_id + 1) & 0xFFFF
|
||||
|
||||
data = self.prepareNetNameQuery(trn_id)
|
||||
self.write(data, ip, port)
|
||||
|
||||
d = defer.Deferred()
|
||||
d2 = defer.Deferred()
|
||||
d2.addErrback(d.errback)
|
||||
|
||||
def stripCode(ret):
|
||||
if ret is not None: # got valid response. Somehow the callback is also called when there is an error.
|
||||
d.callback(map(lambda s: s[0], filter(lambda s: s[1] == TYPE_SERVER, ret)))
|
||||
|
||||
d2.addCallback(stripCode)
|
||||
self.pending_trns[trn_id] = ( time.time()+timeout, NAME_QUERY, d2 )
|
||||
return d
|
||||
|
||||
def stopProtocol(self):
|
||||
DatagramProtocol.stopProtocol(self)
|
||||
|
||||
def cleanupPendingTrns(self):
|
||||
now = time.time()
|
||||
|
||||
# reply should have been received in the past
|
||||
expired = filter(lambda (trn_id, (expiry_time, name, d)): expiry_time < now, self.pending_trns.iteritems())
|
||||
|
||||
# remove expired items from dict + call errback
|
||||
def expire_item(item):
|
||||
trn_id, (expiry_time, name, d) = item
|
||||
|
||||
del self.pending_trns[trn_id]
|
||||
try:
|
||||
d.errback(NetBIOSTimeout(name))
|
||||
except: pass
|
||||
|
||||
map(expire_item, expired)
|
||||
|
||||
if self.transport:
|
||||
reactor.callLater(1, self.cleanupPendingTrns)
|
||||
0
lib/sambatools/python2/nmb/__init__.py
Normal file
0
lib/sambatools/python2/nmb/__init__.py
Normal file
185
lib/sambatools/python2/nmb/base.py
Normal file
185
lib/sambatools/python2/nmb/base.py
Normal file
@@ -0,0 +1,185 @@
|
||||
|
||||
import struct, logging, random
|
||||
from nmb_constants import *
|
||||
from nmb_structs import *
|
||||
from utils import encode_name
|
||||
|
||||
class NMBSession:
|
||||
|
||||
log = logging.getLogger('NMB.NMBSession')
|
||||
|
||||
def __init__(self, my_name, remote_name, host_type = TYPE_SERVER, is_direct_tcp = False):
|
||||
self.my_name = my_name.upper()
|
||||
self.remote_name = remote_name.upper()
|
||||
self.host_type = host_type
|
||||
self.data_buf = ''
|
||||
|
||||
if is_direct_tcp:
|
||||
self.data_nmb = DirectTCPSessionMessage()
|
||||
self.sendNMBPacket = self._sendNMBPacket_DirectTCP
|
||||
else:
|
||||
self.data_nmb = NMBSessionMessage()
|
||||
self.sendNMBPacket = self._sendNMBPacket_NetBIOS
|
||||
|
||||
#
|
||||
# Overridden Methods
|
||||
#
|
||||
|
||||
def write(self, data):
|
||||
raise NotImplementedError
|
||||
|
||||
def onNMBSessionMessage(self, flags, data):
|
||||
pass
|
||||
|
||||
def onNMBSessionOK(self):
|
||||
pass
|
||||
|
||||
def onNMBSessionFailed(self):
|
||||
pass
|
||||
|
||||
#
|
||||
# Public Methods
|
||||
#
|
||||
|
||||
def feedData(self, data):
|
||||
self.data_buf = self.data_buf + data
|
||||
|
||||
offset = 0
|
||||
while True:
|
||||
length = self.data_nmb.decode(self.data_buf, offset)
|
||||
if length == 0:
|
||||
break
|
||||
elif length > 0:
|
||||
offset += length
|
||||
self._processNMBSessionPacket(self.data_nmb)
|
||||
else:
|
||||
raise NMBError
|
||||
|
||||
if offset > 0:
|
||||
self.data_buf = self.data_buf[offset:]
|
||||
|
||||
def sendNMBMessage(self, data):
|
||||
self.sendNMBPacket(SESSION_MESSAGE, data)
|
||||
|
||||
def requestNMBSession(self):
|
||||
my_name_encoded = encode_name(self.my_name, TYPE_WORKSTATION)
|
||||
remote_name_encoded = encode_name(self.remote_name, self.host_type)
|
||||
self.sendNMBPacket(SESSION_REQUEST, remote_name_encoded + my_name_encoded)
|
||||
|
||||
#
|
||||
# Protected Methods
|
||||
#
|
||||
|
||||
def _processNMBSessionPacket(self, packet):
|
||||
if packet.type == SESSION_MESSAGE:
|
||||
self.onNMBSessionMessage(packet.flags, packet.data)
|
||||
elif packet.type == POSITIVE_SESSION_RESPONSE:
|
||||
self.onNMBSessionOK()
|
||||
elif packet.type == NEGATIVE_SESSION_RESPONSE:
|
||||
self.onNMBSessionFailed()
|
||||
elif packet.type == SESSION_KEEPALIVE:
|
||||
# Discard keepalive packets - [RFC1002]: 5.2.2.1
|
||||
pass
|
||||
else:
|
||||
self.log.warning('Unrecognized NMB session type: 0x%02x', packet.type)
|
||||
|
||||
def _sendNMBPacket_NetBIOS(self, packet_type, data):
|
||||
length = len(data)
|
||||
assert length <= 0x01FFFF
|
||||
flags = 0
|
||||
if length > 0xFFFF:
|
||||
flags |= 0x01
|
||||
length &= 0xFFFF
|
||||
self.write(struct.pack('>BBH', packet_type, flags, length) + data)
|
||||
|
||||
def _sendNMBPacket_DirectTCP(self, packet_type, data):
|
||||
length = len(data)
|
||||
assert length <= 0x00FFFFFF
|
||||
self.write(struct.pack('>I', length) + data)
|
||||
|
||||
|
||||
class NBNS:
|
||||
|
||||
log = logging.getLogger('NMB.NBNS')
|
||||
|
||||
HEADER_STRUCT_FORMAT = '>HHHHHH'
|
||||
HEADER_STRUCT_SIZE = struct.calcsize(HEADER_STRUCT_FORMAT)
|
||||
|
||||
def write(self, data, ip, port):
|
||||
raise NotImplementedError
|
||||
|
||||
def decodePacket(self, data):
|
||||
if len(data) < self.HEADER_STRUCT_SIZE:
|
||||
raise Exception
|
||||
|
||||
trn_id, code, question_count, answer_count, authority_count, additional_count = struct.unpack(self.HEADER_STRUCT_FORMAT, data[:self.HEADER_STRUCT_SIZE])
|
||||
|
||||
is_response = bool((code >> 15) & 0x01)
|
||||
opcode = (code >> 11) & 0x0F
|
||||
flags = (code >> 4) & 0x7F
|
||||
rcode = code & 0x0F
|
||||
|
||||
if opcode == 0x0000 and is_response:
|
||||
name_len = ord(data[self.HEADER_STRUCT_SIZE])
|
||||
offset = self.HEADER_STRUCT_SIZE+2+name_len+8 # constant 2 for the padding bytes before/after the Name and constant 8 for the Type, Class and TTL fields in the Answer section after the Name
|
||||
record_count = (struct.unpack('>H', data[offset:offset+2])[0]) / 6
|
||||
|
||||
offset += 4 # Constant 4 for the Data Length and Flags field
|
||||
ret = [ ]
|
||||
for i in range(0, record_count):
|
||||
ret.append('%d.%d.%d.%d' % struct.unpack('4B', (data[offset:offset + 4])))
|
||||
offset += 6
|
||||
return trn_id, ret
|
||||
else:
|
||||
return trn_id, None
|
||||
|
||||
|
||||
def prepareNameQuery(self, trn_id, name, is_broadcast = True):
|
||||
header = struct.pack(self.HEADER_STRUCT_FORMAT,
|
||||
trn_id, (is_broadcast and 0x0110) or 0x0100, 1, 0, 0, 0)
|
||||
payload = encode_name(name, 0x20) + '\x00\x20\x00\x01'
|
||||
|
||||
return header + payload
|
||||
|
||||
#
|
||||
# Contributed by Jason Anderson
|
||||
#
|
||||
def decodeIPQueryPacket(self, data):
|
||||
if len(data) < self.HEADER_STRUCT_SIZE:
|
||||
raise Exception
|
||||
|
||||
trn_id, code, question_count, answer_count, authority_count, additional_count = struct.unpack(self.HEADER_STRUCT_FORMAT, data[:self.HEADER_STRUCT_SIZE])
|
||||
|
||||
is_response = bool((code >> 15) & 0x01)
|
||||
opcode = (code >> 11) & 0x0F
|
||||
flags = (code >> 4) & 0x7F
|
||||
rcode = code & 0x0F
|
||||
|
||||
try:
|
||||
numnames = struct.unpack('B', data[self.HEADER_STRUCT_SIZE + 44])[0]
|
||||
|
||||
if numnames > 0:
|
||||
ret = [ ]
|
||||
offset = self.HEADER_STRUCT_SIZE + 45
|
||||
|
||||
for i in range(0, numnames):
|
||||
mynme = data[offset:offset + 15]
|
||||
mynme = mynme.strip()
|
||||
ret.append(( mynme, ord(data[offset+15]) ))
|
||||
offset += 18
|
||||
|
||||
return trn_id, ret
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
return trn_id, None
|
||||
|
||||
#
|
||||
# Contributed by Jason Anderson
|
||||
#
|
||||
def prepareNetNameQuery(self, trn_id, is_broadcast = True):
|
||||
header = struct.pack(self.HEADER_STRUCT_FORMAT,
|
||||
trn_id, (is_broadcast and 0x0010) or 0x0000, 1, 0, 0, 0)
|
||||
payload = encode_name('*', 0) + '\x00\x21\x00\x01'
|
||||
|
||||
return header + payload
|
||||
38
lib/sambatools/python2/nmb/nmb_constants.py
Normal file
38
lib/sambatools/python2/nmb/nmb_constants.py
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
# Default port for NetBIOS name service
|
||||
NETBIOS_NS_PORT = 137
|
||||
|
||||
# Default port for NetBIOS session service
|
||||
NETBIOS_SESSION_PORT = 139
|
||||
|
||||
# Owner Node Type Constants
|
||||
NODE_B = 0x00
|
||||
NODE_P = 0x01
|
||||
NODE_M = 0x10
|
||||
NODE_RESERVED = 0x11
|
||||
|
||||
# Name Type Constants
|
||||
TYPE_UNKNOWN = 0x01
|
||||
TYPE_WORKSTATION = 0x00
|
||||
TYPE_CLIENT = 0x03
|
||||
TYPE_SERVER = 0x20
|
||||
TYPE_DOMAIN_MASTER = 0x1B
|
||||
TYPE_MASTER_BROWSER = 0x1D
|
||||
TYPE_BROWSER = 0x1E
|
||||
|
||||
TYPE_NAMES = { TYPE_UNKNOWN: 'Unknown',
|
||||
TYPE_WORKSTATION: 'Workstation',
|
||||
TYPE_CLIENT: 'Client',
|
||||
TYPE_SERVER: 'Server',
|
||||
TYPE_MASTER_BROWSER: 'Master Browser',
|
||||
TYPE_BROWSER: 'Browser Server',
|
||||
TYPE_DOMAIN_MASTER: 'Domain Master'
|
||||
}
|
||||
|
||||
# Values for Session Packet Type field in Session Packets
|
||||
SESSION_MESSAGE = 0x00
|
||||
SESSION_REQUEST = 0x81
|
||||
POSITIVE_SESSION_RESPONSE = 0x82
|
||||
NEGATIVE_SESSION_RESPONSE = 0x83
|
||||
REGTARGET_SESSION_RESPONSE = 0x84
|
||||
SESSION_KEEPALIVE = 0x85
|
||||
69
lib/sambatools/python2/nmb/nmb_structs.py
Normal file
69
lib/sambatools/python2/nmb/nmb_structs.py
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
import struct
|
||||
|
||||
class NMBError(Exception): pass
|
||||
|
||||
|
||||
class NotConnectedError(NMBError):
|
||||
"""
|
||||
Raisd when the underlying NMB connection has been disconnected or not connected yet
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class NMBSessionMessage:
|
||||
|
||||
HEADER_STRUCT_FORMAT = '>BBH'
|
||||
HEADER_STRUCT_SIZE = struct.calcsize(HEADER_STRUCT_FORMAT)
|
||||
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self.type = 0
|
||||
self.flags = 0
|
||||
self.data = ''
|
||||
|
||||
def decode(self, data, offset):
|
||||
data_len = len(data)
|
||||
|
||||
if data_len < offset + self.HEADER_STRUCT_SIZE:
|
||||
# Not enough data for decoding
|
||||
return 0
|
||||
|
||||
self.reset()
|
||||
self.type, self.flags, length = struct.unpack(self.HEADER_STRUCT_FORMAT, data[offset:offset+self.HEADER_STRUCT_SIZE])
|
||||
|
||||
if self.flags & 0x01:
|
||||
length |= 0x010000
|
||||
|
||||
if data_len < offset + self.HEADER_STRUCT_SIZE + length:
|
||||
return 0
|
||||
|
||||
self.data = data[offset+self.HEADER_STRUCT_SIZE:offset+self.HEADER_STRUCT_SIZE+length]
|
||||
return self.HEADER_STRUCT_SIZE + length
|
||||
|
||||
|
||||
class DirectTCPSessionMessage(NMBSessionMessage):
|
||||
|
||||
HEADER_STRUCT_FORMAT = '>I'
|
||||
HEADER_STRUCT_SIZE = struct.calcsize(HEADER_STRUCT_FORMAT)
|
||||
|
||||
def decode(self, data, offset):
|
||||
data_len = len(data)
|
||||
|
||||
if data_len < offset + self.HEADER_STRUCT_SIZE:
|
||||
# Not enough data for decoding
|
||||
return 0
|
||||
|
||||
self.reset()
|
||||
length = struct.unpack(self.HEADER_STRUCT_FORMAT, data[offset:offset+self.HEADER_STRUCT_SIZE])[0]
|
||||
|
||||
if length >> 24 != 0:
|
||||
raise NMBError("Invalid protocol header for Direct TCP session message")
|
||||
|
||||
if data_len < offset + self.HEADER_STRUCT_SIZE + length:
|
||||
return 0
|
||||
|
||||
self.data = data[offset+self.HEADER_STRUCT_SIZE:offset+self.HEADER_STRUCT_SIZE+length]
|
||||
return self.HEADER_STRUCT_SIZE + length
|
||||
50
lib/sambatools/python2/nmb/utils.py
Normal file
50
lib/sambatools/python2/nmb/utils.py
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
import string, re
|
||||
|
||||
|
||||
def encode_name(name, type, scope = None):
|
||||
"""
|
||||
Perform first and second level encoding of name as specified in RFC 1001 (Section 4)
|
||||
"""
|
||||
if name == '*':
|
||||
name = name + '\0' * 15
|
||||
elif len(name) > 15:
|
||||
name = name[:15] + chr(type)
|
||||
else:
|
||||
name = string.ljust(name, 15) + chr(type)
|
||||
|
||||
def _do_first_level_encoding(m):
|
||||
s = ord(m.group(0))
|
||||
return string.uppercase[s >> 4] + string.uppercase[s & 0x0f]
|
||||
|
||||
encoded_name = chr(len(name) * 2) + re.sub('.', _do_first_level_encoding, name)
|
||||
if scope:
|
||||
encoded_scope = ''
|
||||
for s in string.split(scope, '.'):
|
||||
encoded_scope = encoded_scope + chr(len(s)) + s
|
||||
return encoded_name + encoded_scope + '\0'
|
||||
else:
|
||||
return encoded_name + '\0'
|
||||
|
||||
|
||||
def decode_name(name):
|
||||
name_length = ord(name[0])
|
||||
assert name_length == 32
|
||||
|
||||
def _do_first_level_decoding(m):
|
||||
s = m.group(0)
|
||||
return chr(((ord(s[0]) - ord('A')) << 4) | (ord(s[1]) - ord('A')))
|
||||
|
||||
decoded_name = re.sub('..', _do_first_level_decoding, name[1:33])
|
||||
if name[33] == '\0':
|
||||
return 34, decoded_name, ''
|
||||
else:
|
||||
decoded_domain = ''
|
||||
offset = 34
|
||||
while 1:
|
||||
domain_length = ord(name[offset])
|
||||
if domain_length == 0:
|
||||
break
|
||||
decoded_domain = '.' + name[offset:offset + domain_length]
|
||||
offset = offset + domain_length
|
||||
return offset + 1, decoded_name, decoded_domain
|
||||
Reference in New Issue
Block a user