Commits

Ivan Vučica committed c1f109a

First release

  • Participants

Comments (0)

Files changed (4)

+XMPPTunTap
+==========
+
+Copyright (c) 2012, Ivan Vucica, http://ivan.vucica.net/
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following
+   disclaimer in the documentation and/or other materials
+   provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
+About
+-----
+
+This is a small XMPP bot that makes use of TUN/TAP virtual network capability
+of the operating system to add a virtual network card, and then sends all
+network traffic over XMPP to the target party via message stanzas.
+
+This was primarily a proof-of-concept hack to allow communication with a
+certain emulator running in a browser. (Network traffic is sent from the
+emulator into browser-based XMPP client, and the client then sends it to
+the XMPP server which can forward the traffic onwards). If it were to be
+done properly, at the very least it'd have to use the InBand Bytestreams
+as defined in XEP-0047.
+
+If it were to be done even better, it'd use Jingle to negotiate the channel
+with some of the candidates being in-band, and some actually determined
+via ICE and the data being transmitted peer-to-peer.
+
+However, that was not the goal; the goal was to allow exchange of traffic
+with the sandboxed emulator, and to do so in the easiest way possible.
+
+Requirements
+------------
+This uses xmpppy library.
+
+More specifically, xmpppy-0.5.0rc1 was used. Unpack `xmpppy-0.5.0rc1.tar.gz`
+and place the `xmpp/` subfolder directly into xmpptuntap's folder.
+
+Todo
+----
+
+* Add Linux support. Currently, a static TAP device is opened, in the way
+TUN/TAP support works under Mac OS X.
+
+* Switch to Jingle negotiation. Even if true p2p support is not added,
+it'd still be nice to make things compatible with someone's theoretical
+future client. (XEP-0166)
+
+* Switch to in-band bytestreams. This would tolerate incoming data
+coming out of order. (XEP-0047, XEP-0261)
+
+
+

config.py.example

+config = {
+	'jid' : 'johnny@exmaple.org',
+	'password' : '123'
+}

example-launcher.sh

+#!/bin/bash
+
+# Specify tap device, ip address and jid on command line.
+sudo ./xmpptuntap.py tap1 10.0.2.1 johnny2@example.com 
+
+#!/usr/bin/env python
+from config import *
+import xmpp, xmpp.protocol
+import os, select
+import sys
+
+import base64
+
+targetForNetTraffic = None
+
+discoInfo = {
+                           'ids': [{
+                                   'category': 'application',
+                                   'type': 'bot',
+                                   'name': 'XMPP TUN/TAP Bot'
+                                   }],
+                           'features': [xmpp.protocol.NS_DISCO_INFO, "http://xmpptuntap.vucica.net/protocol/"],
+            }
+
+
+def onIqDisco(conn, request):
+        rep=request.buildReply('result')
+#if node: rep.setQuerynode(node)
+        q=rep.getTag('query')
+        if request.getQueryNS()==xmpp.protocol.NS_DISCO_INFO:
+            #if type(handler)==dict: dt=handler['info']
+            #else: dt=handler(conn,request,'info')
+            #if dt==None:
+            #    conn.send(Error(request,ERR_ITEM_NOT_FOUND))
+            #    raise NodeProcessed
+
+            dt = discoInfo
+
+            # handler must return dictionary:
+            # {'ids':[{},{},{},{}], 'features':[fe,at,ur,es], 'xdata':DataForm}
+            for id in dt['ids']: q.addChild('identity',id)
+            for feature in dt['features']: q.addChild('feature',{'var':feature})
+            if dt.has_key('xdata'): q.addChild(node=dt['xdata'])
+
+            conn.send(rep)
+            raise xmpp.protocol.NodeProcessed
+	
+def onMessage(client, message_in):
+	global targetForNetTraffic
+	text = message_in.getBody()
+	user = message_in.getFrom()
+
+	for aNode in message_in.getPayload():
+		if aNode.getNamespace() == "http://xmpptuntap.vucica.net/protocol/":
+			if aNode.getName() == "invitation" and user != targetForNetTraffic:
+				targetForNetTraffic = user
+				invitation = xmpp.Node("invitation")
+				invitation.setNamespace("http://xmpptuntap.vucica.net/protocol/")
+				
+				message = xmpp.Message(user, payload = [invitation])
+				message.setType("headline")
+				client.send(message)
+				return
+			if aNode.getName() == "data":
+
+				incomingData = base64.decodestring(aNode.getCDATA())
+			
+				try:
+					os.write(tuntapFD, incomingData)
+				except Exception, e:
+					print "problem"
+					print e
+				return	
+
+
+
+jid = xmpp.protocol.JID(config['jid'])
+if len(sys.argv) >= 4:
+	jid = xmpp.protocol.JID(sys.argv[3])
+client = xmpp.Client(jid.getDomain()) #, debug=[])
+client.connect()
+client.auth(jid.getNode(), config['password'], "xmpptuntap")
+
+"""
+disco = xmpp.browser.Browser()
+disco.PlugIn(client)
+disco.setDiscoHandler({
+                           'info': {
+                           'ids': [{
+                                   'category': 'applicationt',
+                                   'type': 'bot',
+                                   'name': 'XMPP TUN/TAP Bot'
+                                   }],
+                           'features': [xmpp.protocol.NS_DISCO_INFO, "http://xmpptuntap.vucica.net/protocol/"],
+                           },
+			   'items':[]
+                           })
+
+commands = xmpp.commands.Commands(disco)
+commands.PlugIn(client)
+
+command_test = TestCommand()
+command_test.plugin(commands)
+command_ls = LsCommand()
+command_ls.plugin(commands)
+"""
+
+#Replacing the preceding with two steps: requesting roster and sending 
+#custom presence
+# client.sendInitPresence()
+xmpp.roster.Roster().PlugIn(client) # request roster
+caps = xmpp.Node("c", attrs={"node": "http://xmpptuntap.vucica.net/desktop/caps", "ver": "1.0", "ext": ""}, payload = [ ]); # payload can contain more nodes
+caps.setNamespace(xmpp.NS_CAPS)
+presencePayload	= [caps]
+client.send(xmpp.Presence(payload=presencePayload));
+	
+client.RegisterHandler('message', onMessage);
+client.RegisterHandler("iq", onIqDisco, typ="get", ns=xmpp.protocol.NS_DISCO_INFO);
+
+tuntapDevice = "tap2"
+if len(sys.argv) >= 2:
+	tuntapDevice = sys.argv[1]
+ipAddress = "10.0.2.1"
+if len(sys.argv) >= 3:
+	ipAddress = sys.argv[2]
+tuntapFD = os.open("/dev/" + tuntapDevice, os.O_RDWR)
+os.system("ifconfig " + tuntapDevice + " inet " + ipAddress);
+
+socketlist = {
+	client.Connection._sock:'xmpp',
+	sys.stdin:'stdio',
+	tuntapFD: 'tuntap'
+}
+print tuntapFD
+while 1:
+	try:
+#	client.Process(1)
+
+		(i , o, e) = select.select(socketlist.keys(),[],[],1)
+		for each in i:
+			if socketlist[each] == 'xmpp':
+				client.Process(1)
+			elif socketlist[each] == 'stdio':
+				msg = sys.stdin.readline().rstrip('\r\n')
+#				print "Not handling stdin " + msg
+				print "Inviting " + msg
+				
+				targetForNetTraffic = msg
+				invitation = xmpp.Node("invitation")
+				invitation.setNamespace("http://xmpptuntap.vucica.net/protocol/")
+				
+				message = xmpp.Message(msg, payload = [invitation])
+				message.setType("headline")
+				client.send(message)
+
+#				bot.stdio_message(msg)
+			elif socketlist[each] == 'tuntap':
+				dataNode = xmpp.Node("data")
+				dataNode.setNamespace("http://xmpptuntap.vucica.net/protocol/")
+				
+				# TODO explore if we can get exact number of bytes to read using something like:
+				#fcntl.ioctl(each, fcntl.FIONREAD...)
+				try:
+					output = os.read(tuntapFD, 8192)
+					dataNode.setData(base64.encodestring(output))
+
+					message = xmpp.Message(targetForNetTraffic, payload = [dataNode])
+					message.setType("headline")
+					client.send(message)
+				except (OSError, IOError), ioe:
+					if ioe.args[0] in (errno.EAGAIN, errno.EINTR):
+						continue
+					else:
+					 	print "Connection lost"
+						exit
+
+				
+			else:
+				raise Exception("Unknown socket type: %s" % repr(socketlist[each]))
+
+
+	except KeyboardInterrupt:
+		# finish whatever needed
+		break
+