def dump( self, indent=0 ): # Produce a nicely formatted dump of the SMB2 header. # # Input: # indent - Number of spaces to indent the formatted output. # # Output: A string, presentng the formatted SMB2 header fields. # # Notes: If the message is a request and the dialect is at least # 0x0300, the ChannelSequence (and a Reserved field) will # replace the Status field (which would otherwise go unused # in a request). This is a protocol modification introduced # with the 3.0 dialect. # ind = ' ' * indent cmdName = self.commandName( self._command ) cmdName = "<unknown>" if( not cmdName ) else cmdName statName = NTStatus( self._status ) statName = "\n" if( statName is None ) else " [%s]\n" % statName.name # Stuff... s = ind + "ProtocolId...: %s\n" % hexstr( self._protocolId[:4] ) s += ind + "StructureSize: 0x{0:04X} ({0:d})\n".format( self._headerSize ) s += ind + "CreditCharge.: 0x{0:04X} ({0:d})\n".format( self._creditCharge ) # Status/Reserved1 if( self.flagReply or self._dialect < SMB2_DIALECT_300 ): s += ind + "Status.......: 0x{0:08X}".format( self._status ) + statName else: s += ind + "ChannelSeq...: 0x{0:04X} ({0:d})\n".format( self._channelSeq ) s += ind + "Reserved1....: 0x{0:04X} ({0:d})\n".format( self._reserved1 ) # More stuff... s += ind + "Command......: 0x{0:02X} ({0:d})".format( self._command ) \ + " [{0:s}]\n".format( self.commandName( self._command ) ) s += ind + "CreditReqResp: 0x{0:04X} ({0:d})\n".format( self.creditReqResp ) s += ind + "Flags........: 0x{0:08X} ({0:d})\n".format( self._flags ) # Flag subfields. s += ind + " Response.....: %s\n" % self.flagReply s += ind + " Async........: %s\n" % self.flagAsync s += ind + " Related Op...: %s\n" % self.flagNext s += ind + " Signed.......: %s\n" % self.flagSigned if( self._dialect >= SMB2_DIALECT_311 ): s += ind + " Priority.....: {0:d}\n".format( self.flagPriority ) s += ind + " DFS Operation: %s\n" % self.flagDFS s += ind + " SMB3.x Replay: %s\n" % self.flagReplay # Yet more stuff... s += ind + "NextCommand..: 0x{0:08X} ({0:d})\n".format( self._nextCommand ) s += ind + "MessageId....: 0x{0:016X} ({0:d})\n".format( self._messageId ) # AsyncId/Reserved2+TreeId if( self.flagAsync ): s += ind + "AsyncId......: 0x{0:016X} ({0:d})\n".format( self._asyncId ) else: s += ind + "Reserved2....: 0x{0:08X} ({0:d})\n".format( self._reserved2 ) s += ind + "TreeId.......: 0x{0:08X} ({0:d})\n".format( self._treeId ) # SessionId and Signature s += ind + "SessionId....: 0x{0:016X} ({0:d})\n".format( self._sessionId ) s += ind + "Signature....: [" tmp = (16 + indent) s += ('\n' + (' ' * tmp)).join( hexstrchop( self._signature, 32 ) ) + "]\n" return( s )
def SessionRequest( CalledName, CallingName ): """Create an NBT Session Service Session Request message. Input: CalledName - The name of the NBT service to which the message is addressed. There may be several named NBT services listening on a given server. CallingName - The name of the NBT service or application that is sending the session request. Errors: ValueError - Thrown if either of the input paramaters does not match the required format. See the Notes. Output: A byte string. The first four bytes are always [0x81, 0, 0, 0x44]. The remaining 68 bytes are the Called and Calling names provided. The total length of the output will always be 72 bytes. Notes: This function is mostly a set of sanity checks. The input parameters must each be L2-encoded NBT names *without NBT scope*. This is an odditiy of the session service. The NBT scope must not be used. The resulting encoded name will always be 34 bytes in length. The first byte will always be 0x20 and the last byte will always be 0x00. Doctest: >>> called = ' EHEPFCEHEPEOFKEPEMEBCACACACACACA\\0' >>> calling = ' EMEJENECFFFCEHEFFCFKCACACACACACA\\0' >>> req = SessionRequest( called, calling ) >>> print "0x%02X" % ParseMsg( req )[0] 0x81 >>> called, calling = map( hexstr, ParseCNames( req[4:] ) ) >>> print "Called.: [%s]\\nCalling: [%s]" % (called, calling) Called.: [ EHEPFCEHEPEOFKEPEMEBCACACACACACA\\x00] Calling: [ EMEJENECFFFCEHEFFCFKCACACACACACA\\x00] """ # Check both names. if( not _L2Okay( CalledName ) ): raise ValueError( "Malformed Called Name: %s." % hexstr( CalledName ) ) if( not _L2Okay( CallingName ) ): raise ValueError( "Malformed Calling Name: %s." % hexstr( CallingName ) ) # Return the composed message. return( "\x81\0\0\x44" + CalledName + CallingName )
def dump( msg=None, indent=0 ): """Parse and pretty print an NBT Session Service message. Input: msg - The (packed) NBT Session Service message. indent - The number of spaces to indent the formatted output. Output: A string containing the formatted NBT message. If the input is empty or None, the empty string is returned. Notes: This function does its own parsing, and does not throw any exceptions if it finds a parsing error. Instead, it just adds the error information to the output. If the message is a Session Message, this function does not dump the payload. That should be handled by the higher level protocol stack. Doctest: >>> print dump( SessionMessage( 1234 ) + "Extra Junk" ) Message Type: 0x00 [Session Message] Flags.......: 0x00 Length......: 1234 >>> called, calling = map( Name, ("CALLED", "CALLING") ) >>> print dump( SessionRequest( called.L2name, calling.L2name ) ) Message Type: 0x81 [Session Request] Flags.......: 0x00 Length......: 68 Called Name.: [ EDEBEMEMEFEECACACACACACACACACACA\\x00] CALLED<20> Calling Name: [ EDEBEMEMEJEOEHCACACACACACACACACA\\x00] CALLING<20> >>> print dump( PositiveResponse() ) Message Type: 0x82 [Positive Session Response] Flags.......: 0x00 Length......: 0 """ def _decodeL2( nom=None ): # Small sub-function to decode L2 NBT names. if( _L2Okay( nom ) ): n = Name() n.setL2name( nom ) return( str( n ) ) return( "<Cannot Decode>" ) # Prepare your spells. ind = ' ' * indent # The correct number of spaces for indentation. inLen = len( msg ) # Length of the input, in bytes. # Check for secret doors and traps. if( not msg ): return( "" ) if( inLen < 4 ): return( ind + "Short input: %s" % hexstr( msg ) ) # Enter the dungeon. mType = ord( msg[0] ) mFlags = (ord( msg[1] ) >> 1) mLen = _formatLong.unpack( msg[:4] )[0] & 0x0001FFFF # Loot the room. s = "{0:s}Message Type: 0x{1:02X} [{2:s}]\n" \ "{0:s}Flags.......: 0x{3:02X}{4:s}\n" \ "{0:s}Length......: {5:d}{6:s}" # Go looking for trouble. fstr = "" if not mFlags else " <Invalid Flags>" validstr = "" if( (mType in _msgLenDict) and (mLen != _msgLenDict[mType]) ): validstr = " <Invalid Length>" s = s.format( ind, mType, MsgTypeStr( mType ), mFlags, fstr, mLen, validstr ) # Deal with the monsters. if( (mType in _msgLenDict) and (inLen < (4 + _msgLenDict[mType])) ): s += "\n%sShort input.: %s" % (ind, hexstr( msg[4:] )) elif( mType == SS_SESSION_REQUEST ): called, calling = (msg[4:38], msg[38:72]) s += "\n%sCalled Name.: [%s]" % (ind, hexstr( called )) s += "\n%s %s" % (ind, _decodeL2( called )) s += "\n%sCalling Name: [%s]" % (ind, hexstr( calling )) s += "\n%s %s" % (ind, _decodeL2( calling )) elif( mType == SS_NEGATIVE_RESPONSE ): eCode = ord(msg[4]) s += "\n%sError Code..: 0x%02X (%s)" % (ind, eCode, ErrCodeStr( eCode )) elif( mType == SS_RETARGET_RESPONSE ): ip, port = _formatIPPort.unpack( msg[4:10] ) s += "\n%sIPv4 Address: %d.%d.%d.%d" % (ind, ip[0], ip[1], ip[2], ip[3]) s += "\n%sPort Number.: %d" % (ind, port) # Face the Gazebo Alone. return( s )