From: jamesbunton Date: Mon, 31 Oct 2005 01:12:03 +0000 (+0000) Subject: Reimport and tags (0.10.1) X-Git-Url: https://code.delx.au/pymsnt/commitdiff_plain/7633c3dda6e75f1b19fc62d31f664b949136aacc Reimport and tags (0.10.1) git-svn-id: http://delx.cjb.net/svn/pymsnt/trunk@4 55fbd22a-6204-0410-b2f0-b6c764c7e90a committer: jamesbunton --- diff --git a/src/tlib/msnp11chl.py b/src/tlib/msnp11chl.py new file mode 100644 index 0000000..36964e5 --- /dev/null +++ b/src/tlib/msnp11chl.py @@ -0,0 +1,76 @@ +# Copyright 2005 James Bunton +# Licensed for distribution under the GPL version 2, check COPYING for details + +import md5 +import struct +import string + +MSNP11_PRODUCT_ID = "PROD0090YUAUV{2B" +MSNP11_PRODUCT_KEY = "YMM8C_H7KCQ2S_KL" +MSNP11_MAGIC_NUM = 0x0E79A9C1 + + +def doChallenge(chlData): + md5digest = md5.md5(chlData + MSNP11_PRODUCT_KEY).digest() + + # Make array of md5 string ints + md5Ints = struct.unpack("QQ", md5digest)] + longs = [littleEndify(x, "Q") for x in longs] + longs = [x ^ key for x in longs] + longs = [littleEndify(abs(x), "Q") for x in longs] + + out = "" + for x in longs: + x = hex(x) + x = x[2:len(x)-1] + x = x.zfill(16) + out += x.lower() + + return out + +def littleEndify(num, c="L"): + return struct.unpack(">" + c, struct.pack("<" + c, num))[0] + + +if __name__ == "__main__": + import sys + try: + print doChallenge(sys.argv[1]) + except IndexError: + print "No args passed" + raise + diff --git a/src/tlib/msnp2p.py b/src/tlib/msnp2p.py new file mode 100644 index 0000000..ceaadfd --- /dev/null +++ b/src/tlib/msnp2p.py @@ -0,0 +1,700 @@ +# Copyright 2005 James Bunton +# 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 = '' % (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("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("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: \r\n" % self.to) + s.append("From: \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 + + +