--- /dev/null
+# Copyright 2005 James Bunton <james@delx.cjb.net>
+# Licensed for distribution under the GPL version 2, check COPYING for details
+
+import struct
+import random
+import sha
+
+import utils
+
+MSNP2P_DEBUG = False
+
+class MSNOBJ:
+ def __init__(self):
+ self.creator = ""
+ self.imageData = ""
+ self.size = ""
+ self.type = 0
+ self.location = ""
+ self.friendly = ""
+ self.sha1d = ""
+ self.text = ""
+
+ def setData(self, creator, imageData):
+ self.creator = creator
+ self.imageData = imageData
+ self.size = len(imageData)
+ self.type = 3
+ self.location = "TMP" + str(random.randint(1000,9999))
+ self.friendly = "AAA="
+ self.sha1d = utils.b64enc(sha.sha(imageData).digest())
+ self.makeText()
+
+ def setNull(self):
+ self.creator = ""
+ self.imageData = ""
+ self.size = 0
+ self.type = 0
+ self.location = ""
+ self.friendly = ""
+ self.sha1d = ""
+ self.text = ""
+
+ def makeText(self):
+ h = []
+ h.append("Creator")
+ h.append(self.creator)
+ h.append("Size")
+ h.append(str(self.size))
+ h.append("Type")
+ h.append(str(self.type))
+ h.append("Location")
+ h.append(self.location)
+ h.append("Friendly")
+ h.append(self.friendly)
+ h.append("SHA1D")
+ h.append(self.sha1d)
+ sha1c = utils.b64enc(sha.sha("".join(h)).digest())
+ self.text = '<msnobj Creator="%s" Size="%s" Type="%s" Location="%s" Friendly="%s" SHA1D="%s" SHA1C="%s"/>' % (self.creator, str(self.size), str(self.type), self.location, self.friendly, self.sha1d, sha1c)
+
+ def parse(self, s):
+ e = utils.parseText(s)
+ self.creator = e.getAttribute("Creator")
+ self.size = int(e.getAttribute("Size"))
+ self.type = int(e.getAttribute("Type"))
+ self.location = e.getAttribute("Location")
+ self.friendly = e.getAttribute("Friendly")
+ self.sha1d = e.getAttribute("SHA1D")
+ self.text = s
+
+
+
+class BinaryFields:
+ ACK = 0x02
+ WAIT = 0x04
+ ERR = 0x08
+ DATA = 0x20
+ BYEGOT = 0x40
+ BYESENT = 0x80
+ DATAFT = 0x1000030
+
+ def __init__(self, fields=None):
+ if(not fields):
+ fields = [0] * 10
+ self.fields = fields
+
+ def __getitem__(self, key):
+ return self.fields[key]
+
+ def __setitem__(self, key, value):
+ self.fields[key] = value
+
+ def unpackFields(self, packet):
+ self.fields = struct.unpack("<LLQQLLLLQ", packet[0:48])
+ self.fields += struct.unpack(">L", packet[len(packet)-4:])
+ if MSNP2P_DEBUG:
+ print "Unpacked fields:",
+ for i in self.fields:
+ print hex(i),
+ print
+
+ def packHeaders(self):
+ f = tuple(self.fields)
+ if MSNP2P_DEBUG:
+ print "Packed fields:",
+ for i in self.fields:
+ print hex(i),
+ print
+ return struct.pack("<LLQQLLLLQ", f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8])
+
+ def packFooter(self):
+ return struct.pack(">L", self.fields[9])
+
+
+class MSNSLPMessage:
+ def __init__(self):
+ self.method = ""
+ self.status = ""
+ self.to = ""
+ self.fro = ""
+ self.cseq = 0
+ self.sessionGuid = ""
+ self.sessionID = None
+ self.euf_guid = ""
+ self.data = "\r\n" + chr(0)
+
+ def create(self, method=None, status=None, to=None, fro=None, cseq=0, sessionGuid=None, data=None):
+ self.method = method
+ self.status = status
+ self.to = to
+ self.fro = fro
+ self.cseq = cseq
+ self.sessionGuid = sessionGuid
+ if(data): self.data = data
+
+ def setData(self, sessionID=None, appID=None, guid=None, context=None):
+ s = []
+ if(guid): s.append("EUF-GUID: %s\r\n" % guid)
+ if(sessionID): s.append("SessionID: %s\r\n" % sessionID)
+ if(appID): s.append("AppID: %s\r\n" % appID)
+ if(context): s.append("Context: %s\r\n\r\n" % context)
+ s.append(chr(0))
+
+ self.data = "".join(s)
+
+ def parse(self, s):
+ s = s[48:len(s)-4:]
+ if s.find("MSNSLP/1.0") < 0: return
+
+ lines = s.split("\r\n")
+
+ # Get the MSNSLP method or status
+ msnslp = lines[0].split(" ")
+ if MSNP2P_DEBUG: print "Parsing MSNSLPMessage", s, len(s)
+ if(msnslp[0] in ("INVITE", "BYE")):
+ self.method = msnslp[0].strip()
+ else:
+ self.status = msnslp[1].strip()
+
+ lines.remove(lines[0])
+
+ for line in lines:
+ line = line.split(":")
+ if(len(line) > 1):
+ if(len(line) > 2 and line[0] == "To"):
+ self.to = line[2][:line[2].find('>')]
+ elif(len(line) > 2 and line[0] == "From"):
+ self.fro = line[2][:line[2].find('>')]
+ elif(line[0] == "Call-ID"):
+ self.sessionGuid = line[1].strip()
+ elif(line[0] == "CSeq"):
+ self.cseq = int(line[1].strip())
+ elif(line[0] == "SessionID"):
+ self.sessionID = int(line[1].strip())
+ elif(line[0] == "EUF-GUID"):
+ self.euf_guid = line[1].strip()
+
+ def __str__(self):
+ s = []
+ if(self.method):
+ s.append("%s MSNMSGR:%s MSNSLP/1.0\r\n" % (self.method, self.to))
+ else:
+ s.append("MSNSLP/1.0 %s\r\n" % self.status)
+ s.append("To: <msnmsgr:%s>\r\n" % self.to)
+ s.append("From: <msnmsgr:%s>\r\n" % self.fro)
+ s.append("Via: MSNSLP/1.0/TLP ;branch=%s\r\n" % utils.random_guid())
+ s.append("CSeq: %s \r\n" % str(self.cseq))
+ s.append("Call-ID: %s\r\n" % self.sessionGuid)
+ s.append("Max-Forwards: 0\r\n")
+ if(self.method == "BYE"):
+ s.append("Content-Type: application/x-msnmsgr-sessionclosebody\r\n")
+ else:
+ s.append("Content-Type: application/x-msnmsgr-sessionreqbody\r\n")
+ s.append("Content-Length: %s\r\n\r\n" % len(self.data))
+ s.append(self.data)
+ return "".join(s)
+
+
+class BaseID:
+ def __init__(self, baseID=None, sender=False):
+ if(baseID):
+ self.baseID = baseID
+ else:
+ self.baseID = random.randint(4, 2**30)
+ self.sender = sender
+ if(self.sender):
+ self.pos = -1
+ else:
+ self.pos = 0
+
+ def get(self):
+ if self.pos == -1:
+ return self.baseID
+ else:
+ return self.baseID + self.pos - 3
+
+ def next(self):
+ self.pos += 1
+ if self.pos == 3 and self.sender:
+ self.pos += 1
+ return self.get()
+
+class FileData:
+ def __init__(self, size):
+ self.data = chr(0) * size
+ self.totalSize = size
+ self.gotSize = 0
+
+ def put(self, offset, data):
+ self.data = self.data[0:offset] + data + self.data[offset+len(data):]
+ self.gotSize += len(data)
+
+ def finished(self):
+ return self.gotSize >= self.totalSize
+
+
+class MSNP2P_Avatar:
+ TRANSFER_COUNT = 0
+ ERROR_COUNT = 0
+
+ EUF_GUID = "{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}"
+ ERROR = -1
+
+ def __init__(self):
+ raise "NotImplemented"
+
+ def getImage(self):
+ return False
+
+ def isFinished(self):
+ if self.state >= self.FINISHED:
+ MSNP2P_Avatar.TRANSFER_COUNT += 1
+ return True
+ elif self.state < 0:
+ MSNP2P_Avatar.ERROR_COUNT += 1
+ return True
+ else:
+ return False
+
+ def setError(self, text):
+ if MSNP2P_DEBUG:
+ print "ERROR in avatar transfer: ", self, text, "in state:", self.state
+ self.state = self.ERROR
+
+ def warn(self, text):
+ if MSNP2P_DEBUG:
+ print "Warning in avatar transfer: ", self, text, "in state:", self.state
+
+
+class MSNP2P_Avatar_Send(MSNP2P_Avatar):
+ # Avatar_Send states
+ WAIT_REQUEST = 0
+ SEND_REQACK = 1
+ SEND_200OK = 2
+ WAIT_200OK_ACK = 3
+ SEND_DATAPREP = 4
+ WAIT_DATAPREP_ACK = 5
+ SEND_DATA = 6
+ WAIT_BYE = 7
+ SEND_BYE_ACK = 8
+ FINISHED = 9
+
+ def __init__(self, to, fro, msnobj):
+ self.to = to
+ self.fro = fro
+ self.msnobj = msnobj
+ self.fileOffset = 0 # The amount already sent
+ self.sessionID = 0
+ self.baseID = BaseID(sender=True)
+ self.state = self.WAIT_REQUEST
+ self.lastFields = BinaryFields()
+ self.lastSentFields = BinaryFields()
+
+ def processPacket(self, packet):
+ if MSNP2P_DEBUG:
+ print "processPacket", self.to, self.fro, self.state
+ if(self.state == self.WAIT_REQUEST):
+ message = MSNSLPMessage()
+ message.parse(packet)
+ if(message.method != "INVITE"):
+ self.setError("method" + message.method)
+ return
+ if(message.cseq != 0):
+ self.setError("cseq" + str(message.cseq))
+ return
+ if(message.euf_guid != self.EUF_GUID):
+ self.setError("guid" + message.euf_guid)
+ return
+
+ binaryFields = BinaryFields()
+ binaryFields.unpackFields(packet)
+
+ self.sguid = message.sessionGuid
+ self.sessionID = message.sessionID
+
+ self.lastFields = binaryFields
+ self.state = self.SEND_REQACK
+ return
+
+ if(self.state == self.WAIT_200OK_ACK):
+ binaryFields = BinaryFields()
+ binaryFields.unpackFields(packet)
+
+ if(binaryFields[6] != self.lastSentFields[1]):
+ self.warn("field6," + str(binaryFields[6]) + "," + str(self.lastSentFields[1]))
+ return
+ if(binaryFields[3] != self.lastSentFields[3]):
+ self.setError("field3," + str(binaryFields[3]) + "," + str(self.lastSentFields[3]))
+ return
+ if(binaryFields[8] != self.lastSentFields[3]):
+ self.setError("field8," + str(binaryFields[8]) + "," + str(self.lastSentFields[3]))
+ return
+ if(binaryFields[5] != BinaryFields.ACK):
+ self.setError("field5," + str(binaryFields[5]))
+ return
+
+ self.lastFields = binaryFields
+ self.state = self.SEND_DATAPREP
+ return
+
+ if(self.state == self.WAIT_DATAPREP_ACK):
+ binaryFields = BinaryFields()
+ binaryFields.unpackFields(packet)
+
+ if(binaryFields[0] != self.sessionID):
+ self.warn("field0," + str(binaryFields[0]) + "," + str(self.sessionID))
+ return
+ if(binaryFields[3] != self.lastSentFields[3]):
+ self.setError("field3," + str(binaryFields[3]) + "," + str(self.lastSentFields[3]))
+ return
+ if(binaryFields[8] != self.lastSentFields[3]):
+ self.setError("field8," + str(binaryFields[8]) + "," + str(self.lastSentFields[3]))
+ return
+ if(binaryFields[5] != BinaryFields.ACK):
+ self.setError("field5," + str(binaryFields[5]))
+ return
+ if(binaryFields[6] != self.lastSentFields[1]):
+ self.setError("field6," + str(binaryFields[6]) + "," + str(self.lastSentFields[1]))
+ return
+ if(binaryFields[7] != self.lastSentFields[6]):
+ self.setError("field7," + str(binaryFields[7]) + "," + str(self.lastSentFields[6]))
+ return
+
+ self.lastFields = binaryFields
+ self.baseID.next()
+ self.state = self.SEND_DATA
+ return
+
+ if(self.state == self.WAIT_BYE):
+ binaryFields = BinaryFields()
+ binaryFields.unpackFields(packet)
+
+ # BYE message
+ message = MSNSLPMessage()
+ message.parse(packet)
+ if(message.sessionGuid != self.sguid):
+ self.warn("sessionGuid" + message.sessionGuid)
+ return
+ if(message.method != "BYE"):
+ self.setError("method " + message.method)
+ return
+ if(message.cseq != 0):
+ self.setError("cseq" + str(message.cseq))
+ return
+
+ self.lastFields = binaryFields
+ self.state = self.SEND_BYE_ACK # I don't really care about receiving the data ack
+ return
+
+
+ def getNextPacket(self):
+ if MSNP2P_DEBUG:
+ print "getNextPacket (Avatar_Send)", self.to, self.fro, self.state
+ if(self.state == self.SEND_REQACK):
+ binaryFields = BinaryFields()
+ binaryFields[1] = self.baseID.get()
+ binaryFields[3] = self.lastFields[3]
+ binaryFields[5] = BinaryFields.ACK
+ binaryFields[6] = self.lastFields[1]
+ binaryFields[7] = self.lastFields[6]
+ binaryFields[8] = self.lastFields[3]
+
+ packet = binaryFields.packHeaders() + binaryFields.packFooter()
+
+ self.state = self.SEND_200OK
+ self.lastSentFields = binaryFields
+ return packet
+
+ if(self.state == self.SEND_200OK):
+ msg = MSNSLPMessage()
+ msg.create(status="200 OK", to=self.to, fro=self.fro, cseq=1, sessionGuid=self.sguid)
+ msg.setData(sessionID=self.sessionID)
+ msgStr = str(msg)
+
+ binaryFields = BinaryFields()
+ binaryFields[1] = self.baseID.next()
+ binaryFields[3] = len(msgStr)
+ binaryFields[4] = len(msgStr)
+ binaryFields[6] = random.randint(0, 2**30)
+
+ packet = binaryFields.packHeaders() + msgStr + binaryFields.packFooter()
+
+ self.state = self.WAIT_200OK_ACK
+ self.lastSentFields = binaryFields
+ return packet
+
+ if(self.state == self.SEND_DATAPREP):
+ binaryFields = BinaryFields()
+ binaryFields[0] = self.sessionID
+ binaryFields[1] = self.baseID.next()
+ binaryFields[3] = 4
+ binaryFields[4] = 4
+ binaryFields[6] = random.randint(0, 2**30)
+ binaryFields[9] = 1
+
+ packet = binaryFields.packHeaders() + chr(0) * 4 + binaryFields.packFooter()
+
+ self.state = self.WAIT_DATAPREP_ACK
+ self.lastSentFields = binaryFields
+ return packet
+
+ if(self.state == self.SEND_DATA):
+ binaryFields = BinaryFields()
+ binaryFields[0] = self.sessionID
+ binaryFields[1] = self.baseID.get()
+ binaryFields[2] = self.fileOffset
+ binaryFields[3] = len(self.msnobj.imageData)
+ binaryFields[5] = BinaryFields.DATA
+ binaryFields[6] = random.randint(0, 2**30)
+ binaryFields[9] = 1
+
+ # Work out what part of the file we're sending
+ length = len(self.msnobj.imageData) - self.fileOffset
+ if(length > 1202):
+ # Max amount per message is 1202
+ length = 1202
+ else:
+ # Last packet!
+ self.state = self.WAIT_BYE
+ self.lastSentFields = binaryFields
+ binaryFields[4] = length
+ chunk = self.msnobj.imageData[self.fileOffset : self.fileOffset + length]
+ self.fileOffset += length
+
+ packet = binaryFields.packHeaders() + chunk + binaryFields.packFooter()
+ return packet
+
+ if(self.state == self.SEND_BYE_ACK):
+ binaryFields = BinaryFields()
+ binaryFields[1] = self.baseID.next()
+ binaryFields[3] = self.lastFields[3]
+ binaryFields[5] = BinaryFields.BYEGOT
+ binaryFields[6] = self.lastFields[1]
+ binaryFields[7] = self.lastFields[6]
+ binaryFields[8] = self.lastFields[3]
+
+ packet = binaryFields.packHeaders() + binaryFields.packFooter()
+
+ self.state = self.FINISHED
+ self.lastSentFields = binaryFields
+ return packet
+
+
+
+
+class MSNP2P_Avatar_Receive(MSNP2P_Avatar):
+ # Avatar_Receive states
+ SEND_REQUEST = 0 # Send the request
+ WAIT_REQACK_200OK = 1 # Wait for REQACK and 200OK message
+ # Skip one value so we know both of the above have been done
+ SEND_200OK_ACK = 3 # Received both of the above
+ WAIT_DATAPREP = 4 # Wait for DATAPREP
+ SEND_DATAPREP_ACK = 5 # Send ACK to above
+ WAIT_DATA = 6 # Wait for all data messages and send DATA_ACK
+ SEND_DATA_ACK = 7 # Then send BYE
+ SEND_BYE = 8
+ FINISHED = 9
+
+ def __init__(self, to, fro, msnobj):
+ self.to = to
+ self.fro = fro
+ self.msnobj = msnobj
+ self.sessionID = random.randint(0, 2**30)
+ self.sguid = utils.random_guid()
+ self.baseID = BaseID()
+ self.state = self.SEND_REQUEST
+ self.lastFields = BinaryFields()
+ self.lastSentFields = BinaryFields()
+ self.fileData = None
+
+ def getImage(self):
+ if(self.state >= self.SEND_BYE):
+ return self.fileData.data
+
+ def processPacket(self, packet):
+ if MSNP2P_DEBUG:
+ print "processPacket", self.to, self.fro, self.state
+ if(self.state == self.WAIT_REQACK_200OK or self.state == self.WAIT_REQACK_200OK + 1):
+ binaryFields = BinaryFields()
+ binaryFields.unpackFields(packet)
+
+ if(binaryFields[5] == BinaryFields.ACK):
+ # REQUESTACK!
+ if(binaryFields[6] != self.lastSentFields[1]):
+ self.warn("field6," + str(binaryFields[6]) + "," + str(self.lastSentFields[1]))
+ return
+ if(binaryFields[3] != self.lastSentFields[3]):
+ self.setError("field3," + str(binaryFields[3]) + "," + str(self.lastSentFields[3]))
+ return
+ if(binaryFields[7] != self.lastSentFields[6]):
+ self.setError("field7," + str(binaryFields[7]) + "," + str(self.lastSentFields[6]))
+ return
+ if(binaryFields[8] != self.lastSentFields[3]):
+ self.setError("field8," + str(binaryFields[8]) + "," + str(self.lastSentFields[3]))
+ return
+
+ self.state += 1
+ return
+
+ elif(binaryFields[5] == 0):
+ # 200OK
+ message = MSNSLPMessage()
+ message.parse(packet)
+ if(message.sessionID != self.sessionID):
+ self.warn("sessionID" + str(message.sessionID) + "," + str(self.sessionID))
+ return
+ if(message.status != "200"):
+ self.setError("status" + message.status)
+ return
+ if(message.cseq != 1):
+ self.setError("cseq" + str(message.cseq))
+ return
+ if(message.sessionGuid != self.sguid):
+ self.setError("sessionGuid" + message.sessionGuid + "," + self.sguid)
+ return
+
+ self.lastFields = binaryFields
+ self.state += 1
+ return
+
+ else:
+ self.warn("Packet discarded," + str(binaryFields[5]))
+
+ if(self.state == self.WAIT_DATAPREP):
+ binaryFields = BinaryFields()
+ binaryFields.unpackFields(packet)
+
+ if(binaryFields[0] != self.sessionID):
+ self.warn("field0," + str(binaryFields[0]) + "," + str(self.sessionID))
+ return
+ if(binaryFields[3] != 4):
+ self.setError("field3," + str(binaryFields[3]))
+ return
+ if(binaryFields[4] != 4):
+ self.setError("field4," + str(binaryFields[4]))
+ return
+ if(binaryFields[9] != 1):
+ self.warn("field9," + str(binaryFields[9]))
+ # return
+
+ self.lastFields = binaryFields
+ self.state = self.SEND_DATAPREP_ACK
+ return
+
+ if(self.state == self.WAIT_DATA):
+ binaryFields = BinaryFields()
+ binaryFields.unpackFields(packet)
+ if(binaryFields[0] != self.sessionID):
+ self.warn("field0," + str(binaryFields[0]) + "," + str(self.sessionID))
+ return
+ if(binaryFields[5] != BinaryFields.DATA):
+ self.setError("field5," + str(binaryFields[5]))
+ return
+ if(binaryFields[9] != 1):
+ self.warn("field9," + str(binaryFields[9]))
+ # return
+ offset = binaryFields[2]
+ total = binaryFields[3]
+ length = binaryFields[4]
+ if(not self.fileData):
+ self.fileData = FileData(binaryFields[3])
+
+ data = packet[48:len(packet)-4]
+ self.fileData.put(offset, packet[48:len(packet)-4])
+
+ if self.fileData.finished():
+ self.state = self.SEND_DATA_ACK
+
+ self.lastFields = binaryFields
+
+ return
+
+ def getNextPacket(self):
+ if MSNP2P_DEBUG:
+ print "getNextPacket (Avatar_Receive)", self.to, self.fro, self.state
+ if(self.state == self.SEND_REQUEST):
+ msg = MSNSLPMessage()
+ msg.create(method="INVITE", to=self.to, fro=self.fro, cseq=0, sessionGuid=self.sguid)
+ msg.setData(sessionID=self.sessionID, appID="1", guid=self.EUF_GUID, context=utils.b64enc(self.msnobj.text + chr(0)))
+ msgStr = str(msg)
+
+ binaryFields = BinaryFields()
+ binaryFields[1] = self.baseID.get()
+ binaryFields[6] = random.randint(0, 2**30)
+ binaryFields[3] = len(msgStr)
+ binaryFields[4] = len(msgStr)
+
+ packet = binaryFields.packHeaders() + msgStr + binaryFields.packFooter()
+
+ self.state = self.WAIT_REQACK_200OK
+ self.lastSentFields = binaryFields
+ return packet
+
+ if(self.state == self.SEND_200OK_ACK):
+ binaryFields = BinaryFields()
+ binaryFields[1] = self.baseID.next()
+ binaryFields[3] = self.lastFields[3]
+ binaryFields[8] = self.lastFields[3]
+ binaryFields[5] = BinaryFields.ACK
+ binaryFields[6] = self.lastFields[1]
+ binaryFields[7] = self.lastFields[6]
+
+ packet = binaryFields.packHeaders() + binaryFields.packFooter()
+
+ self.state = self.WAIT_DATAPREP
+ self.lastSentFields = binaryFields
+ return packet
+
+ if(self.state == self.SEND_DATAPREP_ACK):
+ binaryFields = BinaryFields()
+ binaryFields[0] = self.sessionID
+ binaryFields[1] = self.baseID.next()
+ binaryFields[3] = 4
+ binaryFields[8] = 4
+ binaryFields[5] = BinaryFields.ACK
+ binaryFields[6] = self.lastFields[1]
+ binaryFields[7] = self.lastFields[6]
+
+ packet = binaryFields.packHeaders() + binaryFields.packFooter()
+
+ self.state = self.WAIT_DATA
+ self.lastSentFields = binaryFields
+ return packet
+
+ if(self.state == self.SEND_DATA_ACK):
+ # FIXME Check hash!
+ binaryFields = BinaryFields()
+ binaryFields[0] = self.sessionID
+ binaryFields[1] = self.baseID.next()
+ binaryFields[3] = self.lastFields[3]
+ binaryFields[8] = self.lastFields[3]
+ binaryFields[5] = BinaryFields.ACK
+ binaryFields[6] = self.lastFields[1]
+ binaryFields[7] = self.lastFields[6]
+
+ packet = binaryFields.packHeaders() + binaryFields.packFooter()
+
+ self.state = self.SEND_BYE
+ self.lastSentFields = binaryFields
+ return packet
+
+ if(self.state == self.SEND_BYE):
+ msg = MSNSLPMessage()
+ msg.create(method="BYE", to=self.to, fro=self.fro, cseq=0, sessionGuid=self.sguid)
+ msgStr = str(msg)
+
+ binaryFields = BinaryFields()
+ binaryFields[1] = self.baseID.next()
+ binaryFields[3] = len(msgStr)
+ binaryFields[4] = len(msgStr)
+ binaryFields[5] = BinaryFields.BYESENT
+ binaryFields[6] = random.randint(0, 2**30)
+
+ packet = binaryFields.packHeaders() + msgStr + binaryFields.packFooter()
+
+ self.state = self.FINISHED
+ self.lastSentFields = binaryFields
+ return packet
+
+
+