# $Id: netbios.py 23 2006-11-08 15:45:33Z dugsong $ """Network Basic Input/Output System.""" import struct import dpkt, dns def encode_name(name): """Return the NetBIOS first-level encoded name.""" l = [] for c in struct.pack('16s', name): c = ord(c) l.append(chr((c >> 4) + 0x41)) l.append(chr((c & 0xf) + 0x41)) return ''.join(l) def decode_name(nbname): """Return the NetBIOS first-level decoded nbname.""" if len(nbname) != 32: return nbname l = [] for i in range(0, 32, 2): l.append(chr(((ord(nbname[i]) - 0x41) << 4) | ((ord(nbname[i+1]) - 0x41) & 0xf))) return ''.join(l).split('\x00', 1)[0] # RR types NS_A = 0x01 # IP address NS_NS = 0x02 # Name Server NS_NULL = 0x0A # NULL NS_NB = 0x20 # NetBIOS general Name Service NS_NBSTAT = 0x21 # NetBIOS NODE STATUS # RR classes NS_IN = 1 # NBSTAT name flags NS_NAME_G = 0x8000 # group name (as opposed to unique) NS_NAME_DRG = 0x1000 # deregister NS_NAME_CNF = 0x0800 # conflict NS_NAME_ACT = 0x0400 # active NS_NAME_PRM = 0x0200 # permanent # NBSTAT service names nbstat_svcs = { # (service, unique): list of ordered (name prefix, service name) tuples (0x00, 0):[ ('', 'Domain Name') ], (0x00, 1):[ ('IS~', 'IIS'), ('', 'Workstation Service') ], (0x01, 0):[ ('__MSBROWSE__', 'Master Browser') ], (0x01, 1):[ ('', 'Messenger Service') ], (0x03, 1):[ ('', 'Messenger Service') ], (0x06, 1):[ ('', 'RAS Server Service') ], (0x1B, 1):[ ('', 'Domain Master Browser') ], (0x1C, 0):[ ('INet~Services', 'IIS'), ('', 'Domain Controllers') ], (0x1D, 1):[ ('', 'Master Browser') ], (0x1E, 0):[ ('', 'Browser Service Elections') ], (0x1F, 1):[ ('', 'NetDDE Service') ], (0x20, 1):[ ('Forte_$ND800ZA', 'DCA IrmaLan Gateway Server Service'), ('', 'File Server Service') ], (0x21, 1):[ ('', 'RAS Client Service') ], (0x22, 1):[ ('', 'Microsoft Exchange Interchange(MSMail Connector)') ], (0x23, 1):[ ('', 'Microsoft Exchange Store') ], (0x24, 1):[ ('', 'Microsoft Exchange Directory') ], (0x2B, 1):[ ('', 'Lotus Notes Server Service') ], (0x2F, 0):[ ('IRISMULTICAST', 'Lotus Notes') ], (0x30, 1):[ ('', 'Modem Sharing Server Service') ], (0x31, 1):[ ('', 'Modem Sharing Client Service') ], (0x33, 0):[ ('IRISNAMESERVER', 'Lotus Notes') ], (0x43, 1):[ ('', 'SMS Clients Remote Control') ], (0x44, 1):[ ('', 'SMS Administrators Remote Control Tool') ], (0x45, 1):[ ('', 'SMS Clients Remote Chat') ], (0x46, 1):[ ('', 'SMS Clients Remote Transfer') ], (0x4C, 1):[ ('', 'DEC Pathworks TCPIP service on Windows NT') ], (0x52, 1):[ ('', 'DEC Pathworks TCPIP service on Windows NT') ], (0x87, 1):[ ('', 'Microsoft Exchange MTA') ], (0x6A, 1):[ ('', 'Microsoft Exchange IMC') ], (0xBE, 1):[ ('', 'Network Monitor Agent') ], (0xBF, 1):[ ('', 'Network Monitor Application') ] } def node_to_service_name((name, service, flags)): try: unique = int(flags & NS_NAME_G == 0) for namepfx, svcname in nbstat_svcs[(service, unique)]: if name.startswith(namepfx): return svcname except KeyError: pass return '' class NS(dns.DNS): """NetBIOS Name Service.""" class Q(dns.DNS.Q): pass class RR(dns.DNS.RR): """NetBIOS resource record.""" def unpack_rdata(self, buf, off): if self.type == NS_A: self.ip = self.rdata elif self.type == NS_NBSTAT: num = ord(self.rdata[0]) off = 1 l = [] for i in range(num): name = self.rdata[off:off+15].split(None, 1)[0].split('\x00', 1)[0] service = ord(self.rdata[off+15]) off += 16 flags = struct.unpack('>H', self.rdata[off:off+2])[0] off += 2 l.append((name, service, flags)) self.nodenames = l # XXX - skip stats def pack_name(self, buf, name): return dns.DNS.pack_name(self, buf, encode_name(name)) def unpack_name(self, buf, off): name, off = dns.DNS.unpack_name(self, buf, off) return decode_name(name), off class Session(dpkt.Packet): """NetBIOS Session Service.""" __hdr__ = ( ('type', 'B', 0), ('flags', 'B', 0), ('len', 'H', 0) ) SSN_MESSAGE = 0 SSN_REQUEST = 1 SSN_POSITIVE = 2 SSN_NEGATIVE = 3 SSN_RETARGET = 4 SSN_KEEPALIVE = 5 class Datagram(dpkt.Packet): """NetBIOS Datagram Service.""" __hdr__ = ( ('type', 'B', 0), ('flags', 'B', 0), ('id', 'H', 0), ('src', 'I', 0), ('sport', 'H', 0), ('len', 'H', 0), ('off', 'H', 0) ) DGRAM_UNIQUE = 0x10 DGRAM_GROUP = 0x11 DGRAM_BROADCAST = 0x12 DGRAM_ERROR = 0x13 DGRAM_QUERY = 0x14 DGRAM_POSITIVE = 0x15 DGRAM_NEGATIVE = 0x16