]> code.delx.au - pymsnt/commitdiff
Socks5 sending supported
authorjamesbunton <jamesbunton@55fbd22a-6204-0410-b2f0-b6c764c7e90a>
Mon, 23 Jan 2006 01:07:49 +0000 (01:07 +0000)
committerjamesbunton <jamesbunton@55fbd22a-6204-0410-b2f0-b6c764c7e90a>
Mon, 23 Jan 2006 01:07:49 +0000 (01:07 +0000)
git-svn-id: http://delx.cjb.net/svn/pymsnt/trunk@97 55fbd22a-6204-0410-b2f0-b6c764c7e90a

committer: jamesbunton <jamesbunton@55fbd22a-6204-0410-b2f0-b6c764c7e90a>

src/disco.py
src/ft.py
src/main.py
src/misciq.py
src/tlib/socks5.py

index cf5ac6614aebd4ae54040bb8596ded473ddc85f2..286ce3f8959d242016d92da70bf615d42f92469c 100644 (file)
@@ -36,6 +36,7 @@ XCONFERENCE   = "jabber:x:conference"
 XEVENT        = "jabber:x:event"
 XDELAY        = "jabber:x:delay"
 XAVATAR       = "jabber:x:avatar"
+XDATA         = "jabber:x:data"
 STORAGEAVATAR = "storage:client:avatar"
 XVCARDUPDATE  = "vcard-temp:x:update"
 VCARDTEMP     = "vcard-temp"
index 1dfa6e88b6dc08986d3d1734759947e123a91b8e..f87f55cfcb68b3ef4e955823aa1efe1b152a42cd 100644 (file)
--- a/src/ft.py
+++ b/src/ft.py
@@ -30,7 +30,7 @@ class FTSend:
        
        def reject(self):
                del self.startTransfer
-               self.cancelTransfer
+               self.cancelTransfer()
 
 
 try:
@@ -177,7 +177,7 @@ class FTReceive:
                feature = si.addElement("feature")
                feature.attributes["xmlns"] = disco.FEATURE_NEG
                x = feature.addElement("x")
-               x.attributes["xmlns"] = "jabber:x:data"
+               x.attributes["xmlns"] = disco.XDATA
                x.attributes["type"] = "form"
                field = x.addElement("field")
                field.attributes["type"] = "list-single"
@@ -232,8 +232,62 @@ class FTReceive:
 # SOCKS5
 
 from tlib import socks5
+import struct
+
+class JEP65ConnectionSend(protocol.Protocol):
+# TODO, clean up and move this to tlib.socks5
+       STATE_INITIAL = 1
+       STATE_WAIT_AUTHOK = 2
+       STATE_WAIT_CONNECTOK = 3
+       STATE_READY = 4
+
+       def __init__(self):
+               self.state = self.STATE_INITIAL
+               self.buf = ""
+       
+       def connectionMade(self):
+               self.transport.write(struct.pack("!BBB", 5, 1, 0))
+               self.state = self.STATE_WAIT_AUTHOK
+       
+       def connectionLost(self, reason):
+               if self.state == self.STATE_READY:
+                       self.factory.consumer.close()
+       
+       def _waitAuthOk(self):
+               ver, method = struct.unpack("!BB", self.buf[:2])
+               if ver != 5 or method != 0:
+                       self.transport.loseConnection()
+                       return
+               self.buf = self.buf[2:] # chop
+               
+               # Send CONNECT request
+               length = len(self.factory.hash)
+               self.transport.write(struct.pack("!BBBBB", 5, 1, 0, 3, length))
+               self.transport.write("".join([struct.pack("!B" , ord(x))[0] for x in self.factory.hash]))
+               self.transport.write(struct.pack("!H", 0))
+               self.state = self.STATE_WAIT_CONNECTOK
+       
+       def _waitConnectOk(self):
+               ver, rep, rsv, atyp = struct.unpack("!BBBB", self.buf[:4])
+               if not (ver == 5 and rep == 0):
+                       self.transport.loseConnection()
+                       return
+               
+               self.state = self.STATE_READY
+               self.factory.madeConnection(self.transport.addr[0])
+       
+       def dataReceived(self, buf):
+               if self.state == self.STATE_READY:
+                       self.factory.consumer.write(buf)
+
+               self.buf += buf
+               if self.state == self.STATE_WAIT_AUTHOK:
+                       self._waitAuthOk()
+               elif self.state == self.STATE_WAIT_CONNECTOK:
+                       self._waitConnectOk()
+               
 
-class JEP65Connection(socks5.SOCKSv5):
+class JEP65ConnectionReceive(socks5.SOCKSv5):
        def __init__(self, listener):
                socks5.SOCKSv5.__init__(self)
                self.listener = listener
@@ -283,7 +337,7 @@ class Proxy65(protocol.Factory):
                self.activeConns = {}
        
        def buildProtocol(self, addr):
-               return JEP65Connection(self)
+               return JEP65ConnectionReceive(self)
        
        def isActive(self, address):
                return address in self.activeConns
@@ -298,14 +352,14 @@ class Proxy65(protocol.Factory):
                        assert address not in self.activeConns
                        self.activeConns[address] = None
                        
-                       if not isinstance(olist[0], JEP65Connection):
+                       if not isinstance(olist[0], (JEP65ConnectionReceive, JEP65ConnectionSend)):
                                legacyftp = olist[0]
                                connection = olist[1]
-                       elif not isinstance(olist[1], JEP65Connection):
+                       elif not isinstance(olist[1], (JEP65ConnectionReceive, JEP65ConnectionSend)):
                                legacyftp = olist[1]
                                connection = olist[0]
                        else:
-                               LogEvent(WARN, '', "No legacyftp")
+                               LogEvent(WARN, '', "No JEP65Connection")
                                return
 
                        legacyftp.accept(connection.transport)
index 76ca79600d5aebc25677971e93ec94a071d8b5b2..1a7803e30e8e3436b7faa81e1d233a6fd7b22b2a 100644 (file)
@@ -60,7 +60,9 @@ class PyTransport(component.Service):
                self.vCardFactory = misciq.VCardFactory(self)
                self.iqAvatarFactor = misciq.IqAvatarFactory(self)
                self.connectUsers = misciq.ConnectUsers(self)
-               if config.ftJabberPort: self.ftSOCKS5 = ft.Proxy65(int(config.ftJabberPort))
+               if config.ftJabberPort:
+                       self.ftSOCKS5Receive = ft.Proxy65(int(config.ftJabberPort))
+                       self.ftSOCKS5Send = misciq.Socks5FileTransfer(self)
                if config.ftOOBPort:
                        self.ftOOBReceive = ft.FileTransferOOBReceive(int(config.ftOOBPort))
                        self.ftOOBSend = misciq.FileTransferOOBSend(self)
index 79a9182410afb6014001c73dd731a7e7a9393124..078208aa0efa7486c0c8a920c166ff6c8ae6f0d3 100644 (file)
@@ -2,7 +2,7 @@
 # Licensed for distribution under the GPL version 2, check COPYING for details
 
 import utils
-from twisted.internet import reactor, task, protocol
+from twisted.internet import reactor, task, protocol, error
 from tlib.xmlw import Element, jid
 from debug import LogEvent, INFO, WARN, ERROR
 import jabw
@@ -49,7 +49,7 @@ class ConnectUsers:
                command.attributes["status"] = "completed"
 
                x = command.addElement("x")
-               x.attributes["xmlns"] = "jabber:x:data"
+               x.attributes["xmlns"] = disco.XDATA
                x.attributes["type"] = "result"
 
                title = x.addElement("title")
@@ -93,7 +93,7 @@ class Statistics:
                command.attributes["status"] = "completed"
 
                x = command.addElement("x")
-               x.attributes["xmlns"] = "jabber:x:data"
+               x.attributes["xmlns"] = disco.XDATA
                x.attributes["type"] = "result"
 
                title = x.addElement("title")
@@ -531,11 +531,147 @@ class Socks5FileTransfer:
        def __init__(self, pytrans):
                self.pytrans = pytrans
                self.pytrans.discovery.addFeature(disco.SI, self.incomingSI, "USER")
+               self.pytrans.discovery.addFeature(disco.FT, lambda: None, "USER")
                self.pytrans.discovery.addFeature(disco.S5B, self.incomingS5B, "USER")
+               self.sessions = {}
        
        def incomingSI(self, el):
-               pass
+               ID = el.getAttribute("id")
+               def errOut():
+                       self.pytrans.discovery.sendIqError(to=el.getAttribute("from"), fro=el.getAttribute("to"), ID=ID, xmlns=disco.SI, etype="cancel", condition="bad-request")
+
+               toj = jid.intern(el.getAttribute("to"))
+               froj = jid.intern(el.getAttribute("from"))
+               session = self.pytrans.sessions.get(froj.userhost(), None)
+               if not session:
+                       return errOut()
+
+               si = el.si
+               if not (si and si.getAttribute("profile") == disco.FT):
+                       return errOut()
+               file = si.file
+               if not (file and file.defaultUri == disco.FT):
+                       return errOut()
+               try:
+                       sid = si["id"]
+                       filename = file["name"]
+                       filesize = int(file["size"])
+               except KeyError:
+                       return errOut()
+               except ValueError:
+                       return errOut()
+
+               # Check that we can use socks5 bytestreams
+               feature = si.feature
+               if not (feature and feature.defaultUri == disco.FEATURE_NEG):
+                       return errOut()
+               x = feature.x
+               if not (x and x.defaultUri == disco.XDATA):
+                       return errOut()
+               field = x.field
+               if not (field and field.getAttribute("var") == "stream-method"):
+                       return errOut()
+               for option in field.elements():
+                       value = option.value
+                       if not value:
+                               continue
+                       value = value.__str__()
+                       if value == disco.S5B:
+                               break
+               else:
+                       return errOut() # Socks5 bytestreams not supported :(
+
+
+               def startTransfer(consumer):
+                       iq = Element((None, "iq"))
+                       iq["type"] = "result"
+                       iq["to"] = froj.full()
+                       iq["from"] = toj.full()
+                       iq["id"] = ID
+                       si = iq.addElement("si")
+                       si["xmlns"] = disco.SI
+                       feature = si.addElement("feature")
+                       feature["xmlns"] = disco.FEATURE_NEG
+                       x = feature.addElement("x")
+                       x["xmlns"] = disco.XDATA
+                       x["type"] = "submit"
+                       field = x.addElement("field")
+                       field["var"] = "stream-method"
+                       value = field.addElement("value")
+                       value.addContent(disco.S5B)
+                       self.pytrans.send(iq)
+                       self.sessions[(froj.full(), sid)] = consumer
+
+               session.legacycon.sendFile(toj.userhost(), ft.FTSend(startTransfer, errOut, filename, filesize))
        
        def incomingS5B(self, el):
-               pass
+               ID = el.getAttribute("id")
+               def errOut():
+                       self.pytrans.discovery.sendIqError(to=el.getAttribute("from"), fro=el.getAttribute("to"), ID=ID, xmlns=disco.S5B, etype="cancel", condition="item-not-found")
+
+               if el.getAttribute("type") != "set":
+                       return errOut()
+
+               toj = jid.intern(el.getAttribute("to"))
+               froj = jid.intern(el.getAttribute("from"))
+
+               query = el.query
+               if not (query and query.getAttribute("mode") == "tcp"):
+                       return errOut()
+               sid = query.getAttribute("sid")
+               consumer = self.sessions.pop((froj.full(), sid), None)
+               if not consumer:
+                       return errOut()
+               streamhosts = []
+               for streamhost in query.elements():
+                       if streamhost.name == "streamhost":
+                               try:
+                                       JID = streamhost["jid"]
+                                       host = streamhost["host"]
+                                       port = int(streamhost["port"])
+                               except ValueError:
+                                       return errOut()
+                               except KeyError:
+                                       continue
+                               streamhosts.append((JID, host, port))
+
+
+               def gotStreamhost(host):
+                       for streamhost in streamhosts:
+                               if streamhost[1] == host:
+                                       jid = streamhost[0]
+                                       break
+                       else:
+                               LogEvent(WARN)
+                               return errOut()
+
+                       for connector in factory.connectors:
+                               # Stop any other connections
+                               try:
+                                       connector.stopConnecting()
+                               except error.NotConnectingError:
+                                       pass
+
+                       iq = Element((None, "iq"))
+                       iq["type"] = "result"
+                       iq["from"] = toj.full()
+                       iq["to"] = froj.full()
+                       iq["id"] = ID
+                       query = iq.addElement("query")
+                       query["xmlns"] = disco.S5B
+                       streamhost = query.addElement("streamhost-used")
+                       streamhost["jid"] = jid
+                       self.pytrans.send(iq)
+
+
+               # Try the streamhosts
+               factory = protocol.ClientFactory()
+               factory.protocol = ft.JEP65ConnectionSend
+               factory.consumer = consumer
+               factory.hash = utils.socks5Hash(sid, froj.full(), toj.full())
+               factory.madeConnection = gotStreamhost
+               factory.connectors = []
+               for streamhost in streamhosts:
+                       factory.connectors.append(reactor.connectTCP(streamhost[1], streamhost[2], factory))
+
 
index 8639def867044574afcf58625048b801ecaf8076..b55481dd7ff9121a550641d6353cbdb23d66f6c4 100644 (file)
 ##     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  021-1307
 ##     USA
 ##
-##     Copyright (C) 2002-2003 Dave Smith (dizzyd@jabber.org)
-##     Copyright (C) 2005 James Bunton (james@delx.cjb.net)
+##     Copyright (C) 2002-2003 Dave Smith   (dizzyd@jabber.org)
+##     Copyright (C) 2005-2006 James Bunton (james@delx.cjb.net)
 ##
-## $Id: socks5.py,v 1.1.1.1 2003/07/15 04:18:05 dizzyd Exp $
 ##============================================================================
 
 from twisted.internet import protocol, reactor