diff --git a/apps/sam/code.leo b/apps/sam/code.leo new file mode 100644 index 0000000000000000000000000000000000000000..52cd3703f650031d685bb6ca560d6c4162e2b373 --- /dev/null +++ b/apps/sam/code.leo @@ -0,0 +1,3430 @@ +<?xml version="1.0" encoding="UTF-8"?> +<leo_file> +<leo_header file_format="2" tnodes="0" max_tnode_index="216" clone_windows="0"/> +<globals body_outline_ratio="0.37624999999999997"> + <global_window_position top="155" left="108" height="585" width="890"/> + <global_log_window_position top="0" left="0" height="0" width="0"/> +</globals> +<preferences> +</preferences> +<find_panel_settings> + <find_string></find_string> + <change_string></change_string> +</find_panel_settings> +<vnodes> +<v t="davidmcnab.041004143447" a="E"><vh>I2P SAM Server and Client</vh> +<v t="davidmcnab.041004144338" tnodeList="davidmcnab.041004144338,davidmcnab.041004144338.1,davidmcnab.041004144338.2,davidmcnab.041004144338.4,davidmcnab.041004144338.5,davidmcnab.041004144338.6,davidmcnab.041004144338.8,davidmcnab.041004144338.9,davidmcnab.041004144338.10,davidmcnab.041004144338.11,davidmcnab.041004144338.12,davidmcnab.041004144338.13,davidmcnab.041004144338.14,davidmcnab.041004144338.15,davidmcnab.041004144338.17,davidmcnab.041004144338.18,davidmcnab.041004144338.19,davidmcnab.041004144338.20,davidmcnab.041004144338.21,davidmcnab.041004144338.22,davidmcnab.041004144338.23,davidmcnab.041004144338.24,davidmcnab.041004144338.26,davidmcnab.041004144338.27,davidmcnab.041004144338.29,davidmcnab.041004144338.30,davidmcnab.041004144338.31,davidmcnab.041004144338.32,davidmcnab.041004144338.33,davidmcnab.041004144338.34,davidmcnab.041004144338.35,davidmcnab.041004144338.36,davidmcnab.041004144338.37,davidmcnab.041004144338.38,davidmcnab.041004144338.39,davidmcnab.041004144338.40,davidmcnab.041004144338.41,davidmcnab.041004144338.42,davidmcnab.041004144338.43,davidmcnab.041004144338.44,davidmcnab.041004144338.45,davidmcnab.041004144338.46,davidmcnab.041004144338.47,davidmcnab.041004144338.49,davidmcnab.041004144338.50,davidmcnab.041004144338.51,davidmcnab.041004144338.52,davidmcnab.041004144338.53,davidmcnab.041004144338.54,davidmcnab.041004144338.55,davidmcnab.041004144338.56,davidmcnab.041004144338.57,davidmcnab.041004144338.58,davidmcnab.041004144338.59,davidmcnab.041004144338.60,davidmcnab.041004144338.62,davidmcnab.041004144338.63,davidmcnab.041004144338.64,davidmcnab.041004144338.65,davidmcnab.041004144338.66,davidmcnab.041004144338.67,davidmcnab.041004144338.68,davidmcnab.041004144338.69,davidmcnab.041004144338.70,davidmcnab.041004144338.71,davidmcnab.041004144338.72,davidmcnab.041004144338.73,davidmcnab.041004144338.74,davidmcnab.041004144338.75,davidmcnab.041004144338.76,davidmcnab.041004144338.77,davidmcnab.041004144338.78,davidmcnab.041004144338.79,davidmcnab.041004144338.80,davidmcnab.041004144338.81,davidmcnab.041004144338.82,davidmcnab.041004144338.83,davidmcnab.041004144338.84,davidmcnab.041004144338.85,davidmcnab.041004144338.86,davidmcnab.041004144338.87,davidmcnab.041004144338.88,davidmcnab.041004144338.89,davidmcnab.041004144338.90,davidmcnab.041004144338.92,davidmcnab.041004144338.93,davidmcnab.041004144338.94,davidmcnab.041004144338.95,davidmcnab.041004144338.96,davidmcnab.041004144338.97,davidmcnab.041004144338.98,davidmcnab.041004144338.99,davidmcnab.041004144338.100,davidmcnab.041004144338.101,davidmcnab.041004144338.102,davidmcnab.041004144338.103,davidmcnab.041004144338.105,davidmcnab.041004144338.106,davidmcnab.041004144338.107,davidmcnab.041004144338.108,davidmcnab.041004144338.109"><vh>@file jython/src/i2psam.py</vh> +<v t="davidmcnab.041004144338.1"><vh>imports</vh></v> +<v t="davidmcnab.041004144338.2"><vh>globals</vh></v> +<v t="davidmcnab.041004144338.3" a="E"><vh>I2CP Interface Classes</vh> +<v t="davidmcnab.041004144338.4"><vh>class JavaWrapper</vh></v> +<v t="davidmcnab.041004144338.5" a="E"><vh>class I2PDestination</vh> +<v t="davidmcnab.041004144338.6"><vh>__init__</vh></v> +<v t="davidmcnab.041004144338.7" a="E"><vh>Exporting Methods</vh> +<v t="davidmcnab.041004144338.8"><vh>toBin</vh></v> +<v t="davidmcnab.041004144338.9"><vh>toBinFile</vh></v> +<v t="davidmcnab.041004144338.10"><vh>toBinPrivate</vh></v> +<v t="davidmcnab.041004144338.11"><vh>toBinFilePrivate</vh></v> +<v t="davidmcnab.041004144338.12"><vh>toBase64</vh></v> +<v t="davidmcnab.041004144338.13"><vh>toBase64Private</vh></v> +<v t="davidmcnab.041004144338.14"><vh>toBase64File</vh></v> +<v t="davidmcnab.041004144338.15"><vh>toBase64FilePrivate</vh></v> +</v> +<v t="davidmcnab.041004144338.16" a="E"><vh>Importing Methods</vh> +<v t="davidmcnab.041004144338.17"><vh>fromBin</vh></v> +<v t="davidmcnab.041004144338.18"><vh>fromBinFile</vh></v> +<v t="davidmcnab.041004144338.19"><vh>fromBinPrivate</vh></v> +<v t="davidmcnab.041004144338.20"><vh>fromBinFilePrivate</vh></v> +<v t="davidmcnab.041004144338.21"><vh>fromBase64</vh></v> +<v t="davidmcnab.041004144338.22"><vh>fromBase64File</vh></v> +<v t="davidmcnab.041004144338.23"><vh>fromBase64Private</vh></v> +<v t="davidmcnab.041004144338.24"><vh>fromBase64PrivateFile</vh></v> +</v> +<v t="davidmcnab.041004144338.25" a="E"><vh>Signature Methods</vh> +<v t="davidmcnab.041004144338.26"><vh>sign</vh></v> +<v t="davidmcnab.041004144338.27"><vh>verify</vh></v> +</v> +<v t="davidmcnab.041004144338.28" a="E"><vh>Sanity Methods</vh> +<v t="davidmcnab.041004144338.29"><vh>hasPrivate</vh></v> +</v> +</v> +<v t="davidmcnab.041004144338.30" a="E"><vh>class I2PClient</vh> +<v t="davidmcnab.041004144338.31" a="E"><vh>__init__</vh></v> +<v t="davidmcnab.041004144338.32"><vh>createDestination</vh></v> +<v t="davidmcnab.041004144338.33"><vh>createSession</vh></v> +</v> +<v t="davidmcnab.041004144338.34" a="E"><vh>class I2PSession</vh> +<v t="davidmcnab.041004144338.35"><vh>attributes</vh></v> +<v t="davidmcnab.041004144338.36"><vh>__init__</vh></v> +<v t="davidmcnab.041004144338.37"><vh>sendMessage</vh></v> +<v t="davidmcnab.041004144338.38"><vh>numMessages</vh></v> +<v t="davidmcnab.041004144338.39"><vh>getMessage</vh></v> +<v t="davidmcnab.041004144338.40"><vh>setSessionListener</vh></v> +<v t="davidmcnab.041004144338.41"><vh>destroySession</vh></v> +<v t="davidmcnab.041004144338.42" a="E"><vh>CALLBACKS</vh> +<v t="davidmcnab.041004144338.43"><vh>on_message</vh></v> +<v t="davidmcnab.041004144338.44"><vh>on_abuse</vh></v> +<v t="davidmcnab.041004144338.45"><vh>on_disconnected</vh></v> +<v t="davidmcnab.041004144338.46"><vh>on_error</vh></v> +</v> +</v> +<v t="davidmcnab.041004144338.47"><vh>class I2PSessionListener</vh></v> +</v> +<v t="davidmcnab.041004144338.48" a="E"><vh>Streaming Interface Classes</vh> +<v t="davidmcnab.041004144338.49" a="E"><vh>class I2PSocket</vh> +<v t="davidmcnab.041004144338.50"><vh>attributes</vh></v> +<v t="davidmcnab.041004144338.51"><vh>__init__</vh></v> +<v t="davidmcnab.041004144338.52"><vh>bind</vh></v> +<v t="davidmcnab.041004144338.53"><vh>listen</vh></v> +<v t="davidmcnab.041004144338.54"><vh>accept</vh></v> +<v t="davidmcnab.041004144338.55"><vh>connect</vh></v> +<v t="davidmcnab.041004144338.56"><vh>recv</vh></v> +<v t="davidmcnab.041004144338.57"><vh>send</vh></v> +<v t="davidmcnab.041004144338.58"><vh>available</vh></v> +<v t="davidmcnab.041004144338.59"><vh>close</vh></v> +<v t="davidmcnab.041004144338.60"><vh>_createSockmgr</vh></v> +</v> +</v> +<v t="davidmcnab.041004144338.61" a="E"><vh>I2P SAM Server</vh> +<v t="davidmcnab.041004144338.62" a="E"><vh>class I2PSamServer</vh> +<v t="davidmcnab.041004144338.63"><vh>attributes</vh></v> +<v t="davidmcnab.041004144338.64"><vh>__init__</vh></v> +<v t="davidmcnab.041004144338.65"><vh>run</vh></v> +<v t="davidmcnab.041004144338.66"><vh>finish_request</vh></v> +<v t="davidmcnab.041004144338.67"><vh>samAllocId</vh></v> +</v> +<v t="davidmcnab.041004144338.68" a="E"><vh>class I2PSamClientHandler</vh> +<v t="davidmcnab.041004144338.69"><vh>handle</vh></v> +<v t="davidmcnab.041004144338.70"><vh>on_genkeys</vh></v> +<v t="davidmcnab.041004144338.71"><vh>on_createsession</vh></v> +<v t="davidmcnab.041004144338.72"><vh>on_destroysession</vh></v> +<v t="davidmcnab.041004144338.73"><vh>on_send</vh></v> +<v t="davidmcnab.041004144338.74"><vh>on_receive</vh></v> +<v t="davidmcnab.041004144338.75"><vh>on_HELLO</vh></v> +<v t="davidmcnab.041004144338.76"><vh>on_SESSION</vh></v> +<v t="davidmcnab.041004144338.77"><vh>on_SESSION_CREATE</vh></v> +<v t="davidmcnab.041004144338.78"><vh>on_STREAM</vh></v> +<v t="davidmcnab.041004144338.79"><vh>on_DATAGRAM</vh></v> +<v t="davidmcnab.041004144338.80"><vh>on_RAW</vh></v> +<v t="davidmcnab.041004144338.81"><vh>on_NAMING</vh></v> +<v t="davidmcnab.041004144338.82"><vh>on_DEST</vh></v> +<v t="davidmcnab.041004144338.83"><vh>on_message</vh></v> +<v t="davidmcnab.041004144338.84"><vh>threadSocketListener</vh></v> +<v t="davidmcnab.041004144338.85"><vh>samParse</vh></v> +<v t="davidmcnab.041004144338.86"><vh>samSend</vh></v> +<v t="davidmcnab.041004144338.87"><vh>samCreateArgsList</vh></v> +<v t="davidmcnab.041004144338.88"><vh>_sendbytes</vh></v> +<v t="davidmcnab.041004144338.89"><vh>_recvbytes</vh></v> +</v> +</v> +<v t="davidmcnab.041004144338.90"><vh>Exceptions</vh></v> +<v t="davidmcnab.041004144338.91" a="E"><vh>Functions</vh> +<v t="davidmcnab.041004144338.92"><vh>shahash</vh></v> +<v t="davidmcnab.041004144338.93"><vh>base64enc</vh></v> +<v t="davidmcnab.041004144338.94"><vh>base64dec</vh></v> +<v t="davidmcnab.041004144338.95"><vh>str2bytearray</vh></v> +<v t="davidmcnab.041004144338.96"><vh>bytearray2str</vh></v> +<v t="davidmcnab.041004144338.97"><vh>byteoutstream2str</vh></v> +<v t="davidmcnab.041004144338.98"><vh>dict2props</vh></v> +<v t="davidmcnab.041004144338.99"><vh>takeKey</vh></v> +<v t="davidmcnab.041004144338.100"><vh>log</vh></v> +<v t="davidmcnab.041004144338.101"><vh>logException</vh></v> +<v t="davidmcnab.041004144338.102"><vh>usage</vh></v> +<v t="davidmcnab.041004144338.103"><vh>main</vh></v> +</v> +<v t="davidmcnab.041004144338.104" a="E"><vh>Tests</vh> +<v t="davidmcnab.041004144338.105" tnodeList="davidmcnab.041004144338.105"><vh>testdests</vh></v> +<v t="davidmcnab.041004144338.106"><vh>testsigs</vh></v> +<v t="davidmcnab.041004144338.107"><vh>testsession</vh></v> +<v t="davidmcnab.041004144338.108"><vh>testsocket</vh></v> +</v> +<v t="davidmcnab.041004144338.109"><vh>MAINLINE</vh></v> +</v> +<v t="davidmcnab.041004144551" a="EV" tnodeList="davidmcnab.041004144551,davidmcnab.041004144551.1,davidmcnab.041004144551.2,davidmcnab.041004144551.3,davidmcnab.041004144551.4,davidmcnab.041004144551.5,davidmcnab.041004144551.6,davidmcnab.041004144551.7,davidmcnab.041004144551.8,davidmcnab.041004144551.9,davidmcnab.041004144551.10,davidmcnab.041004144551.12,davidmcnab.041004144551.13,davidmcnab.041004144551.14,davidmcnab.041004144551.15,davidmcnab.041004144551.16,davidmcnab.041004144551.17,davidmcnab.041004144551.18,davidmcnab.041004144551.19,davidmcnab.041004144551.20,davidmcnab.041004144551.21,davidmcnab.041004144551.22,davidmcnab.041004144551.23,davidmcnab.041004144551.24,davidmcnab.041004144551.26,davidmcnab.041004144551.27,davidmcnab.041004144551.28,davidmcnab.041004144551.29,davidmcnab.041004144551.30,davidmcnab.041004144551.31,davidmcnab.041004144551.32,davidmcnab.041004144551.33,davidmcnab.041004144551.35,davidmcnab.041004144551.36,davidmcnab.041004144551.37,davidmcnab.041004144551.38,davidmcnab.041004144551.39,davidmcnab.041004144551.40,davidmcnab.041004144551.41,davidmcnab.041004144551.42,davidmcnab.041004144551.43,davidmcnab.041004144551.45,davidmcnab.041004144551.46,davidmcnab.041004144551.47,davidmcnab.041004144551.48,davidmcnab.041004144551.49,davidmcnab.041004144551.50,davidmcnab.041004144551.51,davidmcnab.041004144551.52"><vh>@file python/src/i2psamclient.py</vh> +<v t="davidmcnab.041004144551.1"><vh>imports</vh></v> +<v t="davidmcnab.041004144551.2"><vh>globals</vh></v> +<v t="davidmcnab.041004144551.3"><vh>exceptions</vh></v> +<v t="davidmcnab.041004144551.4" a="E"><vh>class I2PSamClient</vh> +<v t="davidmcnab.041004144551.5"><vh>attributes</vh></v> +<v t="davidmcnab.041004144551.6"><vh>__init__</vh></v> +<v t="davidmcnab.041004144551.7"><vh>createSession</vh></v> +<v t="davidmcnab.041004144551.8"><vh>destroySession</vh></v> +<v t="davidmcnab.041004144551.9"><vh>send</vh></v> +<v t="davidmcnab.041004144551.10"><vh>receive</vh></v> +<v t="davidmcnab.041004144551.11" a="E"><vh>SAM methods</vh> +<v t="davidmcnab.041004144551.12"><vh>samHello</vh></v> +<v t="davidmcnab.041004144551.13"><vh>samSessionCreate</vh></v> +<v t="davidmcnab.041004144551.14"><vh>samDestGenerate</vh></v> +<v t="davidmcnab.041004144551.15"><vh>samRawSend</vh></v> +<v t="davidmcnab.041004144551.16"><vh>samRawCheck</vh></v> +<v t="davidmcnab.041004144551.17"><vh>samRawReceive</vh></v> +<v t="davidmcnab.041004144551.18"><vh>samDatagramSend</vh></v> +<v t="davidmcnab.041004144551.19"><vh>samDatagramCheck</vh></v> +<v t="davidmcnab.041004144551.20"><vh>samDatagramReceive</vh></v> +<v t="davidmcnab.041004144551.21"><vh>samNamingLookup</vh></v> +<v t="davidmcnab.041004144551.22"><vh>samParse</vh></v> +<v t="davidmcnab.041004144551.23"><vh>samSend</vh></v> +<v t="davidmcnab.041004144551.24"><vh>samCreateArgsList</vh></v> +</v> +<v t="davidmcnab.041004144551.25" a="E"><vh>Receiver Side</vh> +<v t="davidmcnab.041004144551.26"><vh>threadRx</vh></v> +<v t="davidmcnab.041004144551.27"><vh>on_HELLO</vh></v> +<v t="davidmcnab.041004144551.28"><vh>on_SESSION</vh></v> +<v t="davidmcnab.041004144551.29"><vh>on_STREAM</vh></v> +<v t="davidmcnab.041004144551.30"><vh>on_DATAGRAM</vh></v> +<v t="davidmcnab.041004144551.31"><vh>on_RAW</vh></v> +<v t="davidmcnab.041004144551.32"><vh>on_NAMING</vh></v> +<v t="davidmcnab.041004144551.33"><vh>on_DEST</vh></v> +</v> +<v t="davidmcnab.041004144551.34" a="E"><vh>Utility Methods</vh> +<v t="davidmcnab.041004144551.35"><vh>_recvline</vh></v> +<v t="davidmcnab.041004144551.36"><vh>_recvbytes</vh></v> +<v t="davidmcnab.041004144551.37"><vh>_sendbytes</vh></v> +<v t="davidmcnab.041004144551.38"><vh>_sendline</vh></v> +</v> +</v> +<v t="davidmcnab.041004144551.39" a="E"><vh>class I2PRemoteSession</vh> +<v t="davidmcnab.041004144551.40"><vh>__init__</vh></v> +<v t="davidmcnab.041004144551.41"><vh>send</vh></v> +<v t="davidmcnab.041004144551.42"><vh>recv</vh></v> +<v t="davidmcnab.041004144551.43"><vh>destroy</vh></v> +</v> +<v t="davidmcnab.041004144551.44" a="E"><vh>Functions</vh> +<v t="davidmcnab.041004144551.45"><vh>log</vh></v> +<v t="davidmcnab.041004144551.46"><vh>logException</vh></v> +<v t="davidmcnab.041004144551.47"><vh>demoNAMING</vh></v> +<v t="davidmcnab.041004144551.48"><vh>demoRAW</vh></v> +<v t="davidmcnab.041004144551.49"><vh>demoDATAGRAM</vh></v> +<v t="davidmcnab.041004144551.50"><vh>demoSTREAM</vh></v> +<v t="davidmcnab.041004144551.51"><vh>demo</vh></v> +</v> +<v t="davidmcnab.041004144551.52"><vh>MAINLINE</vh></v> +</v> +</v> +</vnodes> +<tnodes> +<t tx="davidmcnab.041004143447"></t> +<t tx="davidmcnab.041004144338">@first #!/usr/bin/env jython +r""" +Implements I2P SAM Server. (refer U{http://drupal.i2p.net/node/view/144}) + +Also contains useful classes for jython programs, +which wrap the I2P java classes into more python-compatible +paradigms. + +If you run this module (or the i2psam.jar file created from it) +without arguments, it'll run an I2P SAM server bridge, listening +on port 7656. + +The file i2psamclient.py contains python client classes and a +demo program. + +Latest vers of this file is available from U{http://www.freenet.org.nz/i2p/i2psam.py} +Latest epydoc-generated doco at U{http://www.freenet.org.nz/i2p/i2pjyDoc} + +The i2psam.jar file is built from this module with the following +command (requires jython and java 1.4.x+ to be installed):: + + CLASSPATH=/path/to/i2p.jar:/path/to/mstreaming.jar \ + jythonc -jar i2psam.jar --all -A net.invisiblenet i2psam.py + +""" + +@others + + +</t> +<t tx="davidmcnab.041004144338.1"># python imports +import sys, os, time, Queue, thread, threading, StringIO, traceback, getopt +from SocketServer import ThreadingTCPServer, StreamRequestHandler + +# java imports +import java + +# i2p-specific imports +import net.i2p +import net.i2p.client # to shut up epydoc + +# shut up java with a few more imports +import net.i2p.client.streaming +import net.i2p.crypto +import net.i2p.data +import net.i2p.client.I2PClient +import net.i2p.client.I2PClientFactory +import net.i2p.client.naming +#import net.i2p.client.I2PSessionListener + +# handy shorthand refs +i2p = net.i2p +jI2PClient = i2p.client.I2PClient + +# import my own helper hack module +#import I2PHelper + +</t> +<t tx="davidmcnab.041004144338.2">clientFactory = i2p.client.I2PClientFactory + +#i2phelper = I2PHelper() + +PROP_RELIABILITY_BEST_EFFORT = i2p.client.I2PClient.PROP_RELIABILITY_BEST_EFFORT +PROP_RELIABILITY_GUARANTEED = i2p.client.I2PClient.PROP_RELIABILITY_GUARANTEED + +version = "0.1.0" + +# host/port that our socketserver listens on +i2psamhost = "127.0.0.1" +i2psamport = 7656 + +# host/port that I2P's I2CP listens on +i2cpHost = "127.0.0.1" +i2cpPort = 7654 + +#print "i2cpPort=%s" % repr(i2cpPort) + +# ------------------------------------------ +# logging settings + +# 1=v.quiet, 2=normal, 3=verbose, 4=debug, 5=painful +verbosity = 5 + +# change to a filename to log there instead +logfile = sys.stdout + +# when set to 1, and when logfile != sys.stdout, log msgs are written +# both to logfile and console stdout +log2console = 1 + +# don't touch this! +loglock = threading.Lock() + + +</t> +<t tx="davidmcnab.041004144338.3"></t> +<t tx="davidmcnab.041004144338.4">class JavaWrapper: + """ + Wraps a java object as attribute '_item', and forwards + __getattr__ to it. + + All the classes here derive from this + """ + def __init__(self, item): + self._item = item + + def __getattr__(self, attr): + return getattr(self._item, attr) + + +</t> +<t tx="davidmcnab.041004144338.5">class I2PDestination(JavaWrapper): + """ + Wraps java I2P destination objects, with a big difference - these + objects store the private parts. + """ + @others + +</t> +<t tx="davidmcnab.041004144338.6">def __init__(self, **kw): + """ + Versatile constructor + + Keywords (choose only one option): + - (none) - create a whole new dest + - dest, private - wrap an existing I2P java dest with private stream + (private is a byte array) + - bin - reconstitute a public-only dest from a binary string + - binfile - reconstitute public-only from a binary file + - binprivate - reconsistitute private dest from binary string + - binfileprivate - reconsistitute private dest from binary file pathname + - base64 - reconstitute public-only from base64 string + - base64file - reconstitute public-only from file containing base64 + - base64private - reconstitute private from string containing base64 + - base64fileprivate - reconstitute private from file containing base64 + + also: + - client - a java net.i2p.client.I2PClient object + (avoids need for temporary client object when creating new dests) + """ + dest = i2p.data.Destination() + JavaWrapper.__init__(self, dest) + self._private = None + + if kw.has_key('dest'): + self._item = kw['dest'] + if kw.has_key('private'): + self._private = kw['private'] + + elif kw.has_key('bin'): + self.fromBin(kw['bin']) + + elif kw.has_key('binfile'): + self.fromBinFilePrivate(kw['binfile']) + + elif kw.has_key('binprivate'): + self.fromBinPrivate(kw['binprivate']) + + elif kw.has_key('binfileprivate'): + self.fromBinFilePrivate(kw['binfileprivate']) + + elif kw.has_key('base64'): + self.fromBase64(kw['base64']) + + elif kw.has_key('base64file'): + self.fromBase64File(kw['base64file']) + + elif kw.has_key('base64private'): + self.fromBase64Private(kw['base64private']) + + elif kw.has_key('base64fileprivate'): + self.fromBase64FilePrivate(kw['base64fileprivate']) + + else: + # create a whole new one, with a temporary client object (if needed) + if kw.has_key('client'): + client = kw['client'] + else: + client = clientFactory.createClient() + bytestream = java.io.ByteArrayOutputStream() + self._item = client.createDestination(bytestream) + self._private = bytestream.toByteArray() + +</t> +<t tx="davidmcnab.041004144338.7"></t> +<t tx="davidmcnab.041004144338.8">def toBin(self): + """ + Returns a binary string of dest + """ + return bytearray2str(self.toByteArray()) + +</t> +<t tx="davidmcnab.041004144338.9">def toBinFile(self, path): + """ + Writes out public binary to a file + """ + f = open(path, "wb") + f.write(self.toBin()) + f.flush() + f.close() + +</t> +<t tx="davidmcnab.041004144338.10">def toBinPrivate(self): + """ + Returns the private key string as binary + """ + if self._private == None: + raise NoPrivateKey + return bytearray2str(self._private) + +</t> +<t tx="davidmcnab.041004144338.11">def toBinFilePrivate(self, path): + """ + Writes out a binary file with the dest info + """ + f = open(path, "wb") + f.write(self.toBinPrivate()) + f.flush() + f.close() + +</t> +<t tx="davidmcnab.041004144338.12">def toBase64(self): + """ + Returns base64 string of public part + """ + return self._item.toBase64() + +</t> +<t tx="davidmcnab.041004144338.13">def toBase64Private(self): + """ + Exports dest as base64, including private stuff + """ + if self._private == None: + raise NoPrivateKey + return i2p.data.Base64.encode(self._private) + +</t> +<t tx="davidmcnab.041004144338.14">def toBase64File(self, path): + """ + Exports dest to file as base64 + """ + f = open(path, "wb") + f.write(self.toBase64()) + f.flush() + f.close() + +</t> +<t tx="davidmcnab.041004144338.15">def toBase64FilePrivate(self, path): + """ + Writes out a base64 file with the private dest info + """ + f = open(path, "wb") + f.write(self.toBase64Private()) + f.flush() + f.close() + +</t> +<t tx="davidmcnab.041004144338.16"></t> +<t tx="davidmcnab.041004144338.17">def fromBin(self, bin): + """ + Loads this dest from a binary string + """ + self._item.fromByteArray(str2bytearray(bin)) + self._private = None + +</t> +<t tx="davidmcnab.041004144338.18">def fromBinFile(self, path): + """ + Loads public part from file containing binary + """ + f = open(path, "rb") + self.fromBin(f.read()) + f.close() + +</t> +<t tx="davidmcnab.041004144338.19">def fromBinPrivate(self, s): + """ + Loads this dest object from a base64 private key string + """ + bytes = str2bytearray(s) + self._private = bytes + stream = java.io.ByteArrayInputStream(bytes) + self._item.readBytes(stream) + +</t> +<t tx="davidmcnab.041004144338.20">def fromBinFilePrivate(self, path): + """ + Loads this dest object, given the pathname of a file containing + a binary destkey + """ + self.fromBinPrivate(open(path, "rb").read()) + +</t> +<t tx="davidmcnab.041004144338.21">def fromBase64(self, b64): + """ + Loads this dest from a base64 string + """ + self._item.fromBase64(b64) + self._private = None + +</t> +<t tx="davidmcnab.041004144338.22">def fromBase64File(self, path): + """ + Loads public part from file containing base64 + """ + f = open(path, "rb") + self.fromBase64(f.read()) + f.close() + +</t> +<t tx="davidmcnab.041004144338.23">def fromBase64Private(self, s): + """ + Loads this dest object from a base64 private key string + """ + bytes = i2p.data.Base64.decode(s) + self._private = bytes + stream = java.io.ByteArrayInputStream(bytes) + self._item.readBytes(stream) + +</t> +<t tx="davidmcnab.041004144338.24">def fromBase64FilePrivate(self, path): + """ + Loads this dest from a base64 file containing private key + """ + self.fromBase64Private(open(path, "rb").read()) + +</t> +<t tx="davidmcnab.041004144338.25"></t> +<t tx="davidmcnab.041004144338.26">def sign(self, s): + """ + Signs a string using this dest's priv key + """ + # get byte stream + bytes = str2bytearray(s) + + # stream up our private bytes + stream = java.io.ByteArrayInputStream(self._private) + + # temporary dest object + d = i2p.data.Destination() + + # suck the public part off the stream + d.readBytes(stream) + + # temporary private key object + privkey = i2p.data.PrivateKey() + privkey.readBytes(stream) + + # now we should just have the signing key portion left in the stream + signingkey = i2p.data.SigningPrivateKey() + signingkey.readBytes(stream) + + # create DSA engine + dsa = i2p.crypto.DSAEngine() + + sig = dsa.sign(bytes, signingkey) + + rawsig = bytearray2str(sig.getData()) + + return rawsig + +</t> +<t tx="davidmcnab.041004144338.27">def verify(self, s, sig): + """ + Verifies a string against this dest, to test if it was actually + signed by whoever has the dest privkey + """ + # get byte stream from data + databytes = str2bytearray(s) + + # get signature stream from sig + sigstream = java.io.ByteArrayInputStream(str2bytearray(sig)) + + # make a signature object + signature = i2p.data.Signature() + signature.readBytes(sigstream) + + # get signature verify key + pubkey = self.getSigningPublicKey() + + #log(4, "databytes=%s, pubkey=%s" % (repr(databytes), repr(pubkey))) + + # now get a verification + dsa = i2p.crypto.DSAEngine() + result = dsa.verifySignature(signature, databytes, pubkey) + + return result + + + +</t> +<t tx="davidmcnab.041004144338.28"></t> +<t tx="davidmcnab.041004144338.29">def hasPrivate(self): + """ + Returns True if this dest has private parts, False if not + """ + + if self._private: + return 1 + else: + return 0 +</t> +<t tx="davidmcnab.041004144338.30">class I2PClient(JavaWrapper): + """ + jython-comfortable wrapper for java I2P client class + """ + @others + +</t> +<t tx="davidmcnab.041004144338.31">def __init__(self, **kw): + """ + I2PClient constructor + + No args or keywords as yet + """ + client = clientFactory.createClient() + JavaWrapper.__init__(self, client) + +</t> +<t tx="davidmcnab.041004144338.32">def createDestination(self, **kw): + """ + Creates a destination, either a new one, or from a bin or base64 file + + Keywords: + - see L{I2PDestination} constructor + """ + return I2PDestination(**kw) + +</t> +<t tx="davidmcnab.041004144338.33">def createSession(self, dest, sessionClass=None, **kw): + """ + Create a session + + Arguments: + - dest - an L{I2PDestination} object which MUST contain a private portion + - sessionClass - if given, this should be a subclass + of I2PSession. This allows you to implement your own handlers. + + Keywords: + - session options (refer javadocs) + """ + if sessionClass is None: + sessionClass = I2PSession + + if not dest.hasPrivate(): + raise NoPrivateKey("Dest object has no private key") + + #print kw + #session = self._item.createSession(destStream, dict2props(kw)) + session = sessionClass(client=self, dest=dest, **kw) + return session + #return sessionClass(session=session) + +</t> +<t tx="davidmcnab.041004144338.34">class I2PSession(JavaWrapper): + """ + Wraps an I2P client session + + You can subclass this, overriding the on_* handler callbacks, + and pass it as an argument to I2PClient.createSession + + In the default 'on_message' callback, message retrieval is + synchronous - inbound messages get written to an internal queue, + which you can checked with numMessages() and retrieved from via + getMessage(). You may override on_message() if you + want to handle incoming messages asynchronously yourself. + + Note - as far as I can tell, this class should be thread-safe. + """ + @others +</t> +<t tx="davidmcnab.041004144338.35">host = i2cpHost +port = i2cpPort +</t> +<t tx="davidmcnab.041004144338.36">def __init__(self, **kw): + """ + I2PSession constructor + + Keywords: + - either: + - session - a java i2p session object + - or: + - client - an L{I2PClient} object + - dest - an L{I2PDestination} object + Also: + - listener - an L{I2PSessionListener} object. + + Router-level options: + - reliability - one of 'guaranteed' and 'besteffort' (default 'besteffort') + - host - host on which router is running + - port - port on which router is listening + """ + # + # grab options destined for java class + # + options = {} + + reliability = takeKey(kw, 'reliability', 'besteffort') + if reliability == 'guaranteed': + reliability = jI2PClient.PROP_RELIABILITY_GUARANTEED + else: + reliability = jI2PClient.PROP_RELIABILITY_BEST_EFFORT + options[jI2PClient.PROP_RELIABILITY] = reliability + + host = takeKey(kw, 'host', self.host) + options[jI2PClient.PROP_TCP_HOST] = host + + port = takeKey(kw, 'port', self.port) + options[jI2PClient.PROP_TCP_PORT] = str(port) + + if kw.has_key('reliability'): + reliability = kw['reliability'] + + if kw.has_key('listener'): + listener = kw['listener'] + del kw['listener'] + else: + listener = I2PSessionListener() + + #print options + + # + # other keywords handled locally + # + if kw.has_key('session'): + session = kw['session'] + del kw['session'] + JavaWrapper.__init__(self, session) + elif kw.has_key('client') and kw.has_key('dest'): + client = kw['client'] + dest = kw['dest'] + del kw['client'] + del kw['dest'] + destStream = java.io.ByteArrayInputStream(dest._private) + session = self._item = client._item.createSession(destStream, dict2props(options)) + #client.createSession(dest, dict2props(options)) + else: + raise Exception("implementation incomplete") + + # set up a listener + self.setSessionListener(listener) + + # set up a queue for inbound msgs + self.qInbound = Queue.Queue() + self.lockInbound = threading.Lock() + self.nInboundMessages = 0 + + self.lockOutbound = threading.Lock() + + + +</t> +<t tx="davidmcnab.041004144338.37">def sendMessage(self, dest, payload): + """ + Sends a message to another dest + + Arguments: + - dest - an L{I2PDestination} object + - payload - a string to send + """ + dest = dest._item + payload = str2bytearray(payload) + self.lockOutbound.acquire() + try: + res = self._item.sendMessage(dest, payload) + except: + self.lockOutbound.release() + raise + self.lockOutbound.release() + return res +</t> +<t tx="davidmcnab.041004144338.38">def numMessages(self): + """ + Returns the number of unretrieved inbound messages + """ + self.lockInbound.acquire() + n = self.nInboundMessages + self.lockInbound.release() + return n +</t> +<t tx="davidmcnab.041004144338.39">def getMessage(self, blocking=1): + """ + Returns the next available inbound message. + + If blocking is set to 1 (default), blocks + till another message comes in. + + If blocking is set to 0, returns None if there + are no available messages. + """ + if blocking: + msg = self.qInbound.get() + #print "getMessage: acquiring lock" + self.lockInbound.acquire() + #print "getMessage: got lock" + self.nInboundMessages -= 1 + else: + #print "getMessage: acquiring lock" + self.lockInbound.acquire() + #print "getMessage: got lock" + if self.nInboundMessages > 0: + msg = self.qInbound.get() + self.nInboundMessages -= 1 + else: + msg = None + self.lockInbound.release() + #print "getMessage: released lock" + return msg + +</t> +<t tx="davidmcnab.041004144338.40">def setSessionListener(self, listener): + """ + Designates an L{I2PSessionListener} object to listen to this session + """ + self.listener = listener + listener.addSession(self) + self._item.setSessionListener(listener) + + +</t> +<t tx="davidmcnab.041004144338.41">def destroySession(self): + """ + Destroys an existing session + + Note that due to a jython quirk, calls to destroySession might + trigger a TypeError relating to arg mismatch - we ignore such + errors here because by the time the exception happens, the + session has already been successfully closed + """ + try: + self._item.destroySession() + except TypeError: + pass + +</t> +<t tx="davidmcnab.041004144338.42"># +# handler methods which you should override +# + +@others +</t> +<t tx="davidmcnab.041004144338.43">def on_message(self, msg): + """ + Callback for when a message arrives. + + Appends the message to the inbound queue, which you can check + with the numMessages() method, and read with getMessage() + + You should override this if you want to handle inbound messages + asynchronously. + + Arguments: + - msg - a string that was sent by peer + """ + #print "on_message: msg=%s" % msg + self.lockInbound.acquire() + #print "on_message: got lock" + self.qInbound.put(msg) + self.nInboundMessages += 1 + self.lockInbound.release() + #print "on_message: released lock" + +</t> +<t tx="davidmcnab.041004144338.44">def on_abuse(self, severity): + """ + Callback indicating abuse is happening + + Arguments: + - severity - an int of abuse level, 1-100 + """ + print "on_abuse: severity=%s" % severity + +</t> +<t tx="davidmcnab.041004144338.45">def on_disconnected(self): + """ + Callback indicating remote peer disconnected + """ + print "on_disconnected" + +</t> +<t tx="davidmcnab.041004144338.46">def on_error(self, message, error): + """ + Callback indicating an error occurred + """ + print "on_error: message=%s error=%s" % (message, error) + +</t> +<t tx="davidmcnab.041004144338.47">class I2PSessionListener(i2p.client.I2PSessionListener): + """ + Wraps a java i2p.client.I2PSessionListener object + """ + def __init__(self, *sessions): + self.sessions = list(sessions) + + def addSession(self, session): + """ + Adds an L{I2PSession} object to the list of sessions to listen on + + Note - you must also invoke the session's setSessionListener() method + (see I2PSession.setSessionListener) + """ + if session not in self.sessions: + self.sessions.append(session) + + def delSession(self, session): + """ + Stop listening to a given session + """ + if session in self.sessions: + del self.sessions.index[session] + + def messageAvailable(self, session, msgId, size): + """ + Callback from java:: + public void messageAvailable( + I2PSession session, + int msgId, + long size) + """ + #print "listener - messageAvailable" + + # try to find session in our sessions table + sessions = filter(lambda s, session=session: s._item == session, self.sessions) + if sessions: + #print "compare to self.session->%s" % (session == self.session._item) + + # found a matching session - retrieve it + session = sessions[0] + + # retrieve message and pass to callback + msg = session.receiveMessage(msgId) + msgStr = bytearray2str(msg) + session.on_message(msgStr) + else: + print "messageAvailable: unknown session=%s msgId=%s size=%s" % (session, msgId, size) + + def reportAbuse(self, session, severity): + """ + Callback from java:: + public void reportAbuse( + I2PSession session, + int severity) + """ + if self.session: + self.session.on_abuse(severity) + else: + print "reportAbuse: unknown session=%s severity=%s" % (session, severity) + + def disconnected(self, session): + """ + Callback from java:: + public void disconnected(I2PSession session) + """ + if self.session: + self.session.on_disconnected() + else: + print "disconnected: unknown session=%s" % session + + def errorOccurred(session, message, error): + """ + Callback from java:: + public void errorOccurred( + I2PSession session, + java.lang.String message, + java.lang.Throwable error) + """ + if self.session: + self.session.on_error(message, error) + else: + print "errorOccurred: message=%s error=%s" % (message, error) + +</t> +<t tx="davidmcnab.041004144338.48"></t> +<t tx="davidmcnab.041004144338.49">class I2PSocket: + """ + Wraps I2P streaming API into a form resembling python sockets + """ + @others +</t> +<t tx="davidmcnab.041004144338.50">host = i2cpHost +port = i2cpPort + +</t> +<t tx="davidmcnab.041004144338.51">def __init__(self, dest=None, **kw): + """ + Create an I2P streaming socket + + Arguments: + - dest - a private destination to associate with this socket + + Keywords: + - host - hostname on which i2cp is listening (default self.host) + - port - port on which i2cp listens (default self.port) + + Internally used keywords (used for wrapping an accept()ed connection): + - dest + - remdest + - sock + - instream + - outstream + """ + # set up null attribs + self.sockmgr = None + self.instream = None + self.outstream = None + self.sock = None + self._connected = 0 + self._blocking = 1 + + # save dest (or lack thereof) + self.dest = dest + + if kw.has_key('sock') \ + and kw.has_key('dest') \ + and kw.has_key('remdest') \ + and kw.has_key('instream') \ + and kw.has_key('outstream'): + # wrapping an accept()'ed connection + self.sock = kw['sock'] + self.dest = kw['dest'] + self.remdest = kw['remdest'] + self.instream = kw['instream'] + self.outstream = kw['outstream'] + else: + # process keywords + self.host = kw.get('host', self.host) + self.port = int(kw.get('port', self.port)) + + # we need a factory, don't we? + self.sockmgrFact = i2p.client.streaming.I2PSocketManagerFactory() +</t> +<t tx="davidmcnab.041004144338.52">def bind(self, dest=None): + """ + 'binds' the socket to a dest + + dest is an I2PDestination object, which you may specify in the constructor + instead of here. However, we give you the option of specifying here for + some semantic compatibility with python sockets. + """ + if dest is not None: + self.dest = dest + elif not self.dest: + # create new dest, client should interrogate it at some time + self.dest = Destination() +</t> +<t tx="davidmcnab.041004144338.53">def listen(self, *args, **kw): + """ + Sets up the object to receive connections + """ + # sanity checks + if self.sockmgr: + raise I2PSocketError(".sockmgr already present - have you already called listen?") + if not self.dest: + raise I2PSocketError("socket is not bound to a destination") + + # create the socket manager + self._createSockmgr() + </t> +<t tx="davidmcnab.041004144338.54">def accept(self): + """ + Waits for incoming connections, and returns a new I2PSocket object + with the connection + """ + # sanity check + if not self.sockmgr: + raise I2PSocketError(".listen() has not been called on this socket") + + # accept a conn and get its streams + sock = self.sockmgr.getServerSocket().accept() + instream = sock.getInputStream() + outstream = sock.getOutputStream() + remdest = I2PDestination(dest=sock.getPeerDestination()) + + # wrap it and return it + sockobj = I2PSocket(dest=self.dest, + remdest=remdest, + sock=sock, + instream=instream, + outstream=outstream) + self._connected = 1 + return sockobj + +</t> +<t tx="davidmcnab.041004144338.55">def connect(self, remdest): + """ + Connects to a remote destination + """ + # sanity check + if self.sockmgr: + raise I2PSocketError(".sockmgr already present - have you already called listen/connect?") + + # create whole new dest if none was provided to constructor + if self.dest is None: + self.dest = I2PDestination() + + # create the socket manager + self._createSockmgr() + + # do the connect + #print "remdest._item = %s" % repr(remdest._item) + + opts = net.i2p.client.streaming.I2PSocketOptions() + try: + self.sock = self.sockmgr.connect(remdest._item, opts) + self.remdest = remdest + except: + logException(2, "apparent exception, continuing...") + self.instream = self.sock.getInputStream() + self.outstream = self.sock.getOutputStream() + self._connected = 1 +</t> +<t tx="davidmcnab.041004144338.56">def recv(self, nbytes): + """ + Reads nbytes of data from socket + """ + # sanity check + if not self.instream: + raise I2PSocketError("Socket is not connected") + + # for want of better methods, read bytewise + chars = [] + while nbytes > 0: + byte = self.instream.read() + if byte < 0: + break # got all we're gonna get + char = chr(byte) + chars.append(char) + #print "read: got a byte %s (%s)" % (byte, repr(char)) + nbytes -= 1 + + # got it all + buf = "".join(chars) + #print "recv: buf=%s" % repr(buf) + return buf + + +</t> +<t tx="davidmcnab.041004144338.57">def send(self, buf): + """ + Sends buf thru socket + """ + # sanity check + if not self.outstream: + raise I2PSocketError("Socket is not connected") + + # and write it out + #print "send: writing '%s' to outstream..." % repr(buf) + outstream = self.outstream + for c in buf: + outstream.write(ord(c)) + + # flush just in case + #print "send: flushing..." + self.outstream.flush() + + #print "send: done" +</t> +<t tx="davidmcnab.041004144338.58">def available(self): + """ + Returns the number of bytes available for recv() + """ + return self.sock.available() + +</t> +<t tx="davidmcnab.041004144338.59">def close(self): + """ + Closes the socket + """ + # sanity check + #if not self._connected: + # raise I2PSocketError("Socket is not connected") + + # shut up everything + try: + self.instream.close() + except: + pass + try: + self.outstream.close() + except: + pass + try: + self.sock.close() + except: + pass +</t> +<t tx="davidmcnab.041004144338.60">def _createSockmgr(self): + + #options = {jI2PClient.PROP_TCP_HOST: self.host, + # jI2PClient.PROP_TCP_PORT: self.port} + options = {} + props = dict2props(options) + + # get a java stream thing from dest + stream = java.io.ByteArrayInputStream(self.dest._private) + + # create socket manager thing + self.sockmgr = self.sockmgrFact.createManager(stream, self.host, self.port, props) +</t> +<t tx="davidmcnab.041004144338.61"></t> +<t tx="davidmcnab.041004144338.62">class I2PSamServer(ThreadingTCPServer): + """ + A server which makes I2CP available via a socket + """ + @others +</t> +<t tx="davidmcnab.041004144338.63">host = i2psamhost +port = i2psamport + +i2cphost = i2cpHost +i2cpport = i2cpPort + +version = version + + +</t> +<t tx="davidmcnab.041004144338.64">def __init__(self, i2pclient=None, **kw): + """ + Create the client listener object + + Arguments: + - i2pclient - an I2PClient object - optional - if not + given, one will be created + + Keywords: + - host - host to listen on for client conns (default self.host ('127.0.0.1') + - port - port to listen on for client conns (default self.port (7656) + - i2cphost - host to talk to i2cp on (default self.i2cphost ('127.0.0.1')) + - i2cpport - port to talk to i2cp on (default self.i2cphost ('127.0.0.1')) + """ + + # create an I2PClient object if none given + if i2pclient is None: + i2pclient = I2PClient() + self.i2pclient = i2pclient + + # get optional host/port for client and i2cp + self.host = kw.get('host', self.host) + self.port = int(kw.get('port', self.port)) + self.i2cphost = kw.get('i2cphost', self.i2cphost) + self.i2cpport = int(kw.get('i2cpport', self.i2cpport)) + + # create record of current sessions, and a lock for it + self.sessions = {} + self.sessionsLock = threading.Lock() + self.streams = {} + self.streamsLock = threading.Lock() + self.samNextId = 1 + self.samNextIdLock = threading.Lock() + + # and create the server + try: + ThreadingTCPServer.__init__( + self, + (self.host, self.port), + I2PSamClientHandler) + except: + log(4, "crashed with host=%s, port=%s" % (self.host, self.port)) + raise + +</t> +<t tx="davidmcnab.041004144338.65">def run(self): + """ + Run the SAM server. + + when connections come in, they are automatically + accepted, and an L{I2PClientHandler} object created, + and its L{handle} method invoked. + """ + log(4, "Listening for client requests on %s:%s" % (self.host, self.port)) + self.serve_forever() + + +</t> +<t tx="davidmcnab.041004144338.66">def finish_request(self, request, client_address): + """Finish one request by instantiating RequestHandlerClass.""" + try: + self.RequestHandlerClass(request, client_address, self) + except: + pass + log(3, "Client session terminated") +</t> +<t tx="davidmcnab.041004144338.67">def samAllocId(self): + """ + Allocates a new unique id as required by SAM protocol + """ + self.samNextIdLock.acquire() + id = self.samNextId + self.samNextId += 1 + self.samNextIdLock.release() + return id +</t> +<t tx="davidmcnab.041004144338.68">class I2PSamClientHandler(StreamRequestHandler): + r""" + Manages a single socket connection from a client. + + When a client connects to the SAM server, the I2PSamServer + object creates an instance of this class, and invokes its + handle method. See L{handle}. + + Note that if a client terminates its connection to the server, the server + will destroy all current connections initiated by that client + + Size values are decimal + Connection is persistent + """ + @others</t> +<t tx="davidmcnab.041004144338.69">def handle(self): + """ + Reads command/data messages from SAM Client, executes these, + and sends back responses. + + Plants callback hooks into I2PSession objects, so that when + data arrives via I2P, it can be immediately sent to the client. + """ + self.localsessions = {} + self.globalsessions = self.server.sessions + + self.localstreams = {} # keyed by sam stream id + self.globalstreams = self.server.streams + + self.samSessionIsOpen = 0 + self.samSessionStyle = '' + + # need a local sending lock + self.sendLock = threading.Lock() + + log(5, "Got req from %s" % repr(self.client_address)) + + try: + self.namingService = i2p.client.naming.HostsTxtNamingService() + except: + logException(2, "Failed to create naming service object") + + try: + while 1: + # get req + req = self.rfile.readline().strip() + flds = [s.strip() for s in req.split(" ")] + cmd = flds[0] + if cmd in ['HELLO', 'SESSION', 'STREAM', 'DATAGRAM', 'RAW', 'NAMING', 'DEST']: + topic, subtopic, args = self.samParse(flds) + method = getattr(self, "on_"+cmd, None) + method(topic, subtopic, args) + else: + method = getattr(self, "on_"+cmd, None) + if method: + method(flds) + else: + # bad shit + self.wfile.write("error unknown command '%s'\n" % cmd) + + except IOError: + log(3, "Client connection terminated") + except ValueError: + pass + except: + logException(4, "Client req handler crashed") + self.wfile.write("error\n") + + # clean up sessions + for dest in self.localsessions.keys(): + if dest in self.globalsessions.keys(): + log(4, "forgetting global dest %s" % dest[:30]) + del self.globalsessions[dest] + + self.finish() + #thread.exit() + +</t> +<t tx="davidmcnab.041004144338.70">def on_genkeys(self, flds): + + log(4, "entered") + + server = self.server + client = server.i2pclient + globalsessions = server.sessions + sessionsLock = server.sessionsLock + + read = self.rfile.read + readline = self.rfile.readline + write = self.wfile.write + flush = self.wfile.flush + + # genkeys + try: + dest = I2PDestination() + priv = dest.toBase64Private() + pub = dest.toBase64() + write("ok %s %s\n" % (pub, priv)) + except: + write("error exception\n") +</t> +<t tx="davidmcnab.041004144338.71">def on_createsession(self, flds): + + log(4, "entered") + + server = self.server + client = server.i2pclient + globalsessions = server.sessions + sessionsLock = server.sessionsLock + + read = self.rfile.read + readline = self.rfile.readline + write = self.wfile.write + flush = self.wfile.flush + + sessionsLock.acquire() + + try: + b64priv = flds[1] + + # spit if someone else already has this dest + if b64priv in globalsessions.keys(): + write("error dest in use\n") + elif b64priv in self.localsessions.keys(): + # duh, already open locally, treat as ok + write("ok\n") + else: + # whole new session - set it up + dest = I2PDestination(base64private=b64priv) + log(4, "Creating session on dest '%s'" % b64priv[:40]) + session = client.createSession(dest) + log(4, "Connecting session on dest '%s'" % b64priv[:40]) + session.connect() + log(4, "Session on dest '%s' now live" % b64priv[:40]) + + # and remember it + self.localsessions[b64priv] = session + globalsessions[b64priv] = session + + # and tell the client the good news + write("ok\n") + except: + logException(4, "createsession fail") + write("error exception\n") + + sessionsLock.release() +</t> +<t tx="davidmcnab.041004144338.72">def on_destroysession(self, flds): + + log(4, "entered") + + server = self.server + client = server.i2pclient + globalsessions = server.sessions + sessionsLock = server.sessionsLock + + read = self.rfile.read + readline = self.rfile.readline + write = self.wfile.write + flush = self.wfile.flush + + sessionsLock.acquire() + + try: + b64priv = flds[1] + + # spit if session not known + if not globalsessions.has_key(b64priv): + # no such session presently exists anywhere + write("error nosuchsession\n") + elif not self.localsessions.has_key(b64priv): + # session exists, but another client owns it + write("error notyoursession\n") + else: + # session exists and we own it + session = self.localsessions[b64priv] + del self.localsessions[b64priv] + del globalsessions[b64priv] + try: + session.destroySession() + write("ok\n") + except: + raise + except: + logException(4, "destroy session failed") + write("error exception\n") + + sessionsLock.release() + + log(4, "done") + +</t> +<t tx="davidmcnab.041004144338.73">def on_send(self, flds): + + #log(4, "entered: %s" % repr(flds)) + log(4, "entered") + + server = self.server + client = server.i2pclient + globalsessions = server.sessions + sessionsLock = server.sessionsLock + + read = self.rfile.read + readline = self.rfile.readline + write = self.wfile.write + flush = self.wfile.flush + + sessionsLock.acquire() + + session = None + try: + size = int(flds[1]) + b64priv = flds[2] + b64peer = flds[3] + msg = self._recvbytes(size) + + # spit if session not known + if not globalsessions.has_key(b64priv): + # no such session presently exists anywhere + log(4, "no such session") + write("error nosuchsession\n") + elif not self.localsessions.has_key(b64priv): + # session exists, but another client owns it + write("error notyoursession\n") + else: + session = self.localsessions[b64priv] + except: + logException(2, "Send exception") + write("error exception on send command\n") + + sessionsLock.release() + + if not session: + return + + # now get/instantiate the remote dest + try: + peerDest = I2PDestination(base64=b64peer) + except: + peerDest = None + logException(2, "Send: bad remote dest") + write("error bad remote dest\n") + if not peerDest: + return + + # and do the send + try: + res = session.sendMessage(peerDest, msg) + except: + logException(2, "Send: failed") + write("error exception on send\n") + res = None + + if res is None: + return + + # report result + if res: + write("ok\n") + else: + write("error send failed\n") + + log(4, "done") + +</t> +<t tx="davidmcnab.041004144338.74">def on_receive(self, flds): + + log(4, "entered") + + server = self.server + client = server.i2pclient + globalsessions = server.sessions + sessionsLock = server.sessionsLock + + read = self.rfile.read + readline = self.rfile.readline + write = self.wfile.write + flush = self.wfile.flush + + sessionsLock.acquire() + + session = None + try: + b64priv = flds[1] + + # spit if session not known + if not globalsessions.has_key(b64priv): + # no such session presently exists anywhere + write("error nosuchsession\n") + elif not self.localsessions.has_key(b64priv): + # session exists, but another client owns it + write("error notyoursession\n") + else: + session = self.localsessions[b64priv] + except: + logException(4, "receive command error") + write("error exception on receive command\n") + sessionsLock.release() + + if not session: + log(4, "no session matching privdest %s" % b64priv[:30]) + return + + # does this session have any received data? + if session.numMessages() > 0: + msg = session.getMessage() + write("ok %s\n%s" % (len(msg), msg)) + else: + write("ok 0\n") + + log(4, "done") + + return + +</t> +<t tx="davidmcnab.041004144338.75">def on_HELLO(self, topic, subtopic, args): + """ + Responds to client PING + """ + log(4, "entered") + self.samSend("HELLO", "PONG") + log(4, "responded to HELLO") + +</t> +<t tx="davidmcnab.041004144338.76">def on_SESSION(self, topic, subtopic, args): + + log(4, "entered") + + server = self.server + client = server.i2pclient + globalsessions = server.sessions + localsessions = self.localsessions + sessionsLock = server.sessionsLock + + read = self.rfile.read + readline = self.rfile.readline + write = self.wfile.write + flush = self.wfile.flush + + if subtopic == 'CREATE': + + if self.samSessionIsOpen: + self.samSend("SESSION", "STATUS", + RESULT="I2P_ERROR", + MESSAGE="Session_already_created", + ) + return + + # get/validate STYLE arg + style = self.samSessionStyle = args.get('STYLE', None) + if style is None: + self.samSend("SESSION", "STATUS", + RESULT="I2P_ERROR", + MESSAGE="Missing_STYLE_argument", + ) + return + elif style not in ['STREAM', 'DATAGRAM', 'RAW']: + self.samSend("SESSION", "STATUS", + RESULT="I2P_ERROR", + MESSAGE="Invalid_STYLE_argument_'%s'" % style, + ) + return + + # get/validate DESTINATION arg + dest = args.get('DESTINATION', None) + if dest == 'TRANSIENT': + # create new temporary dest + dest = self.samDest = I2PDestination() + destb64 = dest.toBase64Private() + else: + # make sure dest isn't globally or locally known + if dest in globalsessions.keys() or dest in localsessions.keys(): + self.samSend("SESSION", "STATUS", + RESULT="DUPLICATED_DEST", + MESSAGE="Destination_'%s...'_already_in_use" % dest[:20], + ) + return + + # try to reconstitute dest from given base64 + try: + destb64 = dest + dest = I2PDestination(base64private=dest) + except: + self.samSend("SESSION", "STATUS", + RESULT="INVALID_KEY", + MESSAGE="Bad_destination_base64_string_'%s...'" % destb64[:20], + ) + return + + # got valid dest now + self.dest = dest + self.samDestPub = dest.toBase64() + + if style in ['RAW', 'DATAGRAM']: + + if style == 'DATAGRAM': + # we need to know how big binary pub dests and sigs + self.samDestPubBin = dest.toBin() + self.samDestPubBinLen = len(self.samDestPubBin) + self.samSigLen = len(self.dest.sign("nothing")) + + log(4, "binary pub dests are %s bytes, sigs are %s bytes" % ( + self.samDestPubBinLen, self.samSigLen)) + + i2cpHost = args.get('I2CP.HOST', server.i2cphost) + i2cpPort = int(args.get('I2CP.PORT', server.i2cpport)) + + # both these styles require an I2PSession object + session = client.createSession(dest, host=i2cpHost, port=i2cpPort) + + # plug in our inbound message handler + session.on_message = self.on_message + + log(4, "Connecting session on dest '%s'" % destb64[:40]) + try: + session.connect() + except net.i2p.client.I2PSessionException: + self.samSend("SESSION", "STATUS", + RESULT="I2P_ERROR", + MESSAGE="Failed_to_connect_to_i2cp_port", + ) + logException(3, "Failed to connect I2PSession") + return + + log(4, "Session on dest '%s' now live" % destb64[:40]) + + # and remember it + localsessions[destb64] = session + globalsessions[destb64] = session + self.samSession = session + + else: # STREAM + # no need to create session object, because we're using streaming api + + # but we do need to mark it as being in use + localsessions[destb64] = globalsessions[destb64] = None + + # make a local socket + sock = self.samSock = I2PSocket(dest) + + # and we also need to fire up a socket listener + thread.start_new_thread(self.threadSocketListener, (sock, dest)) + + # finally, we can reply with the good news + self.samSend("SESSION", "STATUS", + RESULT="OK", + ) + + else: # subtopic != CREATE + self.samSend("SESSION", "STATUS", + RESULT="I2P_ERROR", + MESSAGE="Invalid_command_'SESSION_%s'" % subtopic, + ) + return + +</t> +<t tx="davidmcnab.041004144338.77">def on_SESSION_CREATE(self, topic, subtopic, args): + + log(4, "entered") + + server = self.server + client = server.i2pclient + globalsessions = server.sessions + localsessions = self.localsessions + sessionsLock = server.sessionsLock + + read = self.rfile.read + readline = self.rfile.readline + write = self.wfile.write + flush = self.wfile.flush + +</t> +<t tx="davidmcnab.041004144338.78">def on_STREAM(self, topic, subtopic, args): + + log(4, "entered") + + server = self.server + client = server.i2pclient + globalsessions = server.sessions + sessionsLock = server.sessionsLock + + read = self.rfile.read + readline = self.rfile.readline + write = self.wfile.write + flush = self.wfile.flush + + if subtopic == 'CONNECT': + # who are we connecting to again? + remdest = I2PDestionation(b64=args['DESTINATION']) + id = args['ID'] + + try: + self.samSock.connect(remdest) + self.samSend("STREAM", "STATUS", + RESULT='OK', + ID=id, + ) + except: + self.samSend("STREAM", "STATUS", + RESULT='I2P_ERROR', + MESSAGE='exception on connect', + ) + +</t> +<t tx="davidmcnab.041004144338.79">def on_DATAGRAM(self, topic, subtopic, args): + r""" + DATAGRAM SEND + DESTINATION=$base64key + SIZE=$numBytes\n[$numBytes of data] + + All datagram messages have a signature/hash header, formatted as: + - sender's binary public dest + - S(H(sender_bin_pubdest + recipient_bin_pubdest + msg)) + """ + log(4, "entered") + + # at this stage of things, we don't know how to handle anything except SEND + if subtopic != 'SEND': + log(3, "Got illegal subtopic '%s' in DATAGRAM command" % subtopic) + return + + # get the details + peerdestb64 = args['DESTINATION'] + peerdest = I2PDestination(base64=peerdestb64) + peerdestBin = base64dec(peerdestb64) + data = args['DATA'] + + # make up the header + log(4, "samDestPubBin (%s) %s" % (type(self.samDestPubBin), repr(self.samDestPubBin))) + log(4, "peerdestBin (%s) %s" % (type(peerdestBin), repr(peerdestBin))) + log(4, "data (%s) %s" % (type(data), repr(data))) + + hashed = shahash(self.samDestPubBin + peerdestBin + data) + log(4, "hashed=%s" % repr(hashed)) + + sig = self.dest.sign(hashed) + log(4, "sig=%s" % repr(sig)) + hdr = self.samDestPubBin + sig + + # send the thing + self.samSession.sendMessage(peerdest, hdr + data) + +</t> +<t tx="davidmcnab.041004144338.80">def on_RAW(self, topic, subtopic, args): + r""" + RAW SEND + DESTINATION=$base64key + SIZE=$numBytes\n[$numBytes of data] + """ + log(4, "entered") + + # at this stage of things, we don't know how to handle anything except SEND + if subtopic != 'SEND': + return + + # get the details + peerdest = I2PDestination(base64=args['DESTINATION']) + msg = args['DATA'] + + # send the thing + self.samSession.sendMessage(peerdest, msg) +</t> +<t tx="davidmcnab.041004144338.81">def on_NAMING(self, topic, subtopic, args): + + log(4, "entered: %s %s %s" % (repr(topic), repr(subtopic), repr(args))) + + # at this stage of things, we don't know how to handle anything except LOOKUP + if subtopic != 'LOOKUP': + return + + # get the details + host = args['NAME'] + + log(4, "looking up host %s" % host) + + # try to lookup + jdest = self.namingService.lookup(host) + + if not jdest: + log(4, "host %s not found" % host) + self.samSend("NAMING", "REPLY", + RESULT="KEY_NOT_FOUND", + NAME=host, + ) + return + + try: + b64 = I2PDestination(dest=jdest).toBase64() + self.samSend("NAMING", "REPLY", + RESULT="OK", + NAME=host, + VALUE=b64, + ) + log(4, "host %s found and valid key returned" % host) + return + except: + log(4, "host %s found but key invalid" % host) + self.samSend("NAMING", "REPLY", + RESULT="INVALID_KEY", + NAME=host, + ) + +</t> +<t tx="davidmcnab.041004144338.82">def on_DEST(self, topic, subtopic, args): + + log(4, "Generating dest") + + dest = I2PDestination() + priv = dest.toBase64Private() + pub = dest.toBase64() + + log(4, "Sending dest to client") + + self.samSend("DEST", "REPLY", PUB=pub, PRIV=priv) + + log(4, "done") +</t> +<t tx="davidmcnab.041004144338.83">def on_message(self, msg): + """ + This callback gets plugged into the I2PSession object, + so we can asychronously notify our client when stuff arrives + """ + if self.samSessionStyle == 'RAW': + self.samSend("RAW", "RECEIVE", msg) + + elif self.samSessionStyle == 'DATAGRAM': + # ain't so simple, we gotta rip and validate the header + remdestBin = msg[:self.samDestPubBinLen] + log(4, "remdestBin=%s" % repr(remdestBin)) + + sig = msg[self.samDestPubBinLen:self.samDestPubBinLen+self.samSigLen] + log(4, "sig=%s" % repr(sig)) + + data = msg[self.samDestPubBinLen+self.samSigLen:] + log(4, "data=%s" % repr(data)) + + # now try to verify + hashed = shahash(remdestBin + self.samDestPubBin + data) + log(4, "hashed=%s" % repr(hashed)) + + remdest = I2PDestination(bin=remdestBin) + if remdest.verify(hashed, sig): + # fine - very good, pass it on + log(4, "sig from peer is valid") + self.samSend("DATAGRAM", "RECEIVE", data, + DESTINATION=remdest.toBase64(), + ) + else: + log(4, "DATAGRAM sig from peer is invalid") +</t> +<t tx="davidmcnab.041004144338.84">def threadSocketListener(self, sock, dest): + """ + Listens for incoming socket connections, and + notifies the client accordingly + """ + destb64 = dest.toBase64() + + log(4, "Listening for connections to %s..." % destb64[:40]) + while 1: + newsock = sock.accept() + + # need an id, negative + id = - self.server.samAllocId() + + # register it in local and global streams + self.localstreams[id] = self.globalstreams[id] = newsock + + # who is connected to us? + remdest = newsock.remdest + remdest_b64 = remdest.toBase64() + + # and notify the client + self.samSend("STREAM", "CONNECTED", + DESTINATION=remdest_b64, + ID=id) +</t> +<t tx="davidmcnab.041004144338.85">def samParse(self, flds): + """ + carves up a SAM command, returns it as a 3-tuple: + - cmd - command string + - subcmd - subcommand string + - dargs - dict of args + """ + cmd = flds[0] + subcmd = flds[1] + args = flds[2:] + + dargs = {} + for arg in args: + try: + name, val = arg.split("=", 1) + except: + logException(3, "failed to process %s" % repr(arg)) + raise + dargs[name] = val + + # read and add data if any + if dargs.has_key('SIZE'): + size = dargs['SIZE'] = int(dargs['SIZE']) + dargs['DATA'] = self._recvbytes(size) + + #log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v[:40])) for k,v in dargs.items()])) + log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v)) for k,v in dargs.items()])) + + return cmd, subcmd, dargs + + + +</t> +<t tx="davidmcnab.041004144338.86">def samSend(self, topic, subtopic, data=None, **kw): + """ + Sends a SAM message (reply?) back to client + + Arguments: + - topic - the first word in the reply, eg 'STREAM' + - subtopic - the second word of the reply, eg 'CONNECTED' + - data - a string of raw data to send back (optional) + Keywords: + - extra 'name=value' items to pass back. + + Notes: + 1. SIZE is not required. If sending back data, it will + be sized and a SIZE arg inserted automatically. + 2. a dict of values can be passed to the 'args' keyword, in lieu + of direct keywords. This allows for cases where arg names would + cause python syntax clashes, eg 'tunnels.depthInbound' + """ + items = [topic, subtopic] + + # stick in SIZE if needed + if data is not None: + kw['SIZE'] = str(len(data)) + else: + data = '' # for later + + self.samCreateArgsList(kw, items) + + # and whack it together + buf = " ".join(items) + '\n' + data + + # and ship it + self.sendLock.acquire() + try: + self._sendbytes(buf) + except: + self.sendLock.release() + raise + self.sendLock.release() + +</t> +<t tx="davidmcnab.041004144338.87">def samCreateArgsList(self, kw1, lst): + for k,v in kw1.items(): + if k == 'args': + self.samCreateArgsList(v, lst) + else: + lst.append("=".join([str(k), str(v)])) +</t> +<t tx="davidmcnab.041004144338.88">def _sendbytes(self, raw): + + self.wfile.write(raw) + self.wfile.flush() +</t> +<t tx="davidmcnab.041004144338.89">def _recvbytes(self, count): + """ + Does a guaranteed read of n bytes + """ + read = self.rfile.read + + chunks = [] + needed = count + while needed > 0: + chunk = read(needed) + chunklen = len(chunk) + needed -= chunklen + chunks.append(chunk) + raw = "".join(chunks) + + # done + return raw + +</t> +<t tx="davidmcnab.041004144338.90">class NoPrivateKey(Exception): + """Destination object has no private key""" + +class I2PSocketError(Exception): + """Error working with I2PSocket objects""" +</t> +<t tx="davidmcnab.041004144338.91"></t> +<t tx="davidmcnab.041004144338.92">def shahash(s): + """ + Calculates SHA Hash of a string, as a string, using + I2P hashing facility + """ + h = net.i2p.crypto.SHA256Generator().calculateHash(s) + h = bytearray2str(h.getData()) + return h +</t> +<t tx="davidmcnab.041004144338.93">def base64enc(s): + return net.i2p.data.Base64.encode(s) +</t> +<t tx="davidmcnab.041004144338.94">def base64dec(s): + return bytearray2str(net.i2p.data.Base64.decode(s)) + +</t> +<t tx="davidmcnab.041004144338.95">def str2bytearray(s): + """ + Convenience - converts python string to java-friendly byte array + """ + a = [] + for c in s: + n = ord(c) + if n >= 128: + n = n - 256 + a.append(n) + return a + +</t> +<t tx="davidmcnab.041004144338.96">def bytearray2str(a): + """ + Convenience - converts java-friendly byte array to python string + """ + chars = [] + for n in a: + if n < 0: + n += 256 + chars.append(chr(n)) + return "".join(chars) + +</t> +<t tx="davidmcnab.041004144338.97">def byteoutstream2str(bs): + """ + Convenience - converts java-friendly byteoutputstream to python string + """ + chars = [] + while 1: + c = bs.read() + if c >= 0: + chars.append(chr(c)) + else: + break + return "".join(chars) + +</t> +<t tx="davidmcnab.041004144338.98">def dict2props(d): + """ + Converts a python dict d into a java.util.Properties object + """ + props = java.util.Properties() + for k,v in d.items(): + props[k] = str(v) + return props + + +</t> +<t tx="davidmcnab.041004144338.99">def takeKey(somedict, keyname, default=None): + """ + Utility function to destructively read a key from a given dict. + Same as the dict's 'takeKey' method, except that the key (if found) + sill be deleted from the dictionary. + """ + if somedict.has_key(keyname): + val = somedict[keyname] + del somedict[keyname] + else: + val = default + return val +</t> +<t tx="davidmcnab.041004144338.100">def log(level, msg, nPrev=0): + + # ignore messages that are too trivial for chosen verbosity + if level > verbosity: + return + + loglock.acquire() + try: + # rip the stack + caller = traceback.extract_stack()[-(2+nPrev)] + path, line, func = caller[:3] + path = os.path.split(path)[1] + full = "%s:%s:%s():\n* %s" % ( + path, + line, + func, + msg.replace("\n", "\n + ")) + now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + msg = "%s %s\n" % (now, full) + + if logfile == sys.stdout: + print msg + else: + file(logfile, "a").write(msg+"\n") + except: + s = StringIO.StringIO() + traceback.print_exc(file=s) + print s.getvalue() + print "Logger crashed" + loglock.release()</t> +<t tx="davidmcnab.041004144338.101">def logException(level, msg=''): + s = StringIO.StringIO() + traceback.print_exc(file=s) + log(level, "%s\n%s" % (s.getvalue(), msg), 1) +</t> +<t tx="davidmcnab.041004144338.102">def usage(detailed=0): + + print "Usage: %s <options> [<command>]" % sys.argv[0] + if not detailed: + print "Run with '-h' to get detailed help" + sys.exit(0) + + print "I2PSAM is a bridge that allows I2P client programs to access the" + print "I2P network by talking over a plaintext socket connection." + print "References:" + print " - http://www.freenet.org.nz/i2p - source, doco, downloadables" + print " - http://drupal.i2p.net/node/view/144 - I2P SAM specification" + print + print "Options:" + print " -h, -?, --help - display this help" + print " -v, --version - print program version" + print " -V, --verbosity=n - set verbosity to n, default 2, 1==quiet, 4==noisy" + print " -H, --listenhost=host - specify host to listen on for client connections" + print " -P, --listenport=port - port to listen on for client connections" + print " --i2cphost=host - hostname of I2P router's I2CP interface" + print " --i2cpport=port - port of I2P router's I2CP interface" + print + print "Commands:" + print " (run with no commands to launch SAM server)" + print " samserver - runs as a SAM server" + print " test - run a suite of self-tests" + print + + sys.exit(0) + + + +</t> +<t tx="davidmcnab.041004144338.103">def main(): + + argv = sys.argv + argc = len(argv) + + try: + opts, args = getopt.getopt(sys.argv[1:], + "h?vV:H:P:", + ['help', 'version', 'verbosity=', + 'listenhost=', 'listenport=', + 'i2cphost=', 'i2cpport=', + ]) + except: + traceback.print_exc(file=sys.stdout) + usage("You entered an invalid option") + + cmd = 'samserver' + + # we prolly should pass all these parms in constructor call, but + # what the heck! + #global verbosity, i2psamhost, i2psamport, i2cpHost, i2cpPort + + serveropts = {} + + for opt, val in opts: + if opt in ['-h', '-?', '--help']: + usage(1) + elif opt in ['-v', '--version']: + print "I2P SAM version %s" % version + sys.exit(0) + elif opt in ['-V', '--verbosity']: + serveropts['verbosity'] = int(val) + elif opt in ['-H', '--listenhost']: + serveropts['host'] = val + elif opt in ['-P', '--listenport']: + serveropts['port'] = int(val) + elif opt in ['--i2cphost']: + serveropts['i2cphost'] = val + elif opt in ['--i2cpport']: + serveropts['i2cpport'] = int(val) + else: + usage(0) + + if len(args) == 0: + cmd = 'samserver' + else: + cmd = args[0] + + if cmd == 'samserver': + + log(2, "Running I2P SAM Server...") + server = I2PSamServer(**serveropts) + server.run() + + elif cmd == 'test': + + print "RUNNING I2P Jython TESTS" + testsigs() + testdests() + testsession() + testsocket() + + else: + usage(0) +</t> +<t tx="davidmcnab.041004144338.104"></t> +<t tx="davidmcnab.041004144338.105">def testdests(): + """ + Demo function which tests out dest generation and import/export + """ + print + print "********************************************" + print "Testing I2P destination create/export/import" + print "********************************************" + print + + print "Generating a destination" + d1 = I2PDestination() + + print "Exporting and importing dest1 in several forms" + + print "public binary string..." + d1_bin = d1.toBin() + d2_bin = I2PDestination(bin=d1_bin) + + print "public binary file..." + d1.toBinFile("temp-d1-bin") + d2_binfile = I2PDestination(binfile="temp-d1-bin") + + print "private binary string..." + d1_binprivate = d1.toBinPrivate() + d2_binprivate = I2PDestination(binprivate=d1_binprivate) + + print "private binary file..." + d1.toBinFilePrivate("temp-d1-bin-private") + d2_binfileprivate = I2PDestination(binfileprivate="temp-d1-bin-private") + + print "public base64 string..." + d1_b64 = d1.toBase64() + d2_b64 = I2PDestination(base64=d1_b64) + + print "public base64 file..." + d1.toBase64File("temp-d1-b64") + d2_b64file = I2PDestination(base64file="temp-d1-b64") + + print "private base64 string..." + d1_base64private = d1.toBase64Private() + d2_b64private = I2PDestination(base64private=d1_base64private) + + print "private base64 file..." + d1.toBase64FilePrivate("temp-d1-b64-private") + d2_b64fileprivate = I2PDestination(base64fileprivate="temp-d1-b64-private") + + print "All destination creation/import/export tests passed!" + + +</t> +<t tx="davidmcnab.041004144338.106">def testsigs(): + global d1, d1pub, d1sig, d1res + + print + print "********************************************" + print "Testing I2P dest-based signatures" + print "********************************************" + print + + print "Creating dest..." + d1 = I2PDestination() + + s_good = "original stuff that we're signing" + s_bad = "non-original stuff we're trying to forge" + + print "Signing some shit against d1..." + d1sig = d1.sign(s_good) + + print "Creating public dest d1pub" + d1pub = I2PDestination(bin=d1.toBin()) + + print "Verifying original data with d1pub" + res = d1pub.verify(s_good, d1sig) + print "Result: %s (should be 1)" % repr(res) + + print "Trying to verify on a different string" + res1 = d1pub.verify(s_bad, d1sig) + print "Result: %s (should be 0)" % repr(res1) + + if res and not res1: + print "signing/verifying test passed" + else: + print "SIGNING/VERIFYING TEST FAILED" + +</t> +<t tx="davidmcnab.041004144338.107">def testsession(): + + global c, d1, d2, s1, s2 + + print + print "********************************************" + print "Testing I2P dest->dest messaging" + print "********************************************" + print + + print "Creating I2P client..." + c = I2PClient() + + print "Creating destination d1..." + d1 = c.createDestination() + + print "Creating destination d2..." + d2 = c.createDestination() + + print "Creating destination d3..." + d3 = c.createDestination() + + print "Creating session s1 on dest d1..." + s1 = c.createSession(d1, host='localhost', port=7654) + + print "Creating session s2 on dest d2..." + s2 = c.createSession(d2) + + print "Connecting session s1..." + s1.connect() + + print "Connecting session s2..." + s2.connect() + + print "Sending message from s1 to d2..." + s1.sendMessage(d2, "Hi there, s2!!") + + print "Retrieving message from s2..." + print "got: %s" % repr(s2.getMessage()) + + print "Sending second message from s1 to d2..." + s1.sendMessage(d2, "Hi there again, s2!!") + + print "Retrieving message from s2..." + print "got: %s" % repr(s2.getMessage()) + + print "Sending message from s1 to d3 (should take ages then fail)..." + res = s1.sendMessage(d3, "This is futile!!") + print "result of that send was %s (should have been 0)" % res + + print "Destroying session s1..." + s1.destroySession() + + print "Destroying session s2..." + s2.destroySession() + + print "session tests passed!" +</t> +<t tx="davidmcnab.041004144338.108">def testsocket(): + + global d1, d2, s1, s2 + + print + print "********************************************" + print "Testing I2P streaming interface" + print "********************************************" + print + + print "Creating destinations..." + dServer = I2PDestination() + dClient = I2PDestination() + + print "Creating sockets..." + sServer = I2PSocket(dServer) + sClient = I2PSocket(dClient) + + # server thread which simply reads a line at a time, then echoes + # that line back to the client + def servThread(s): + print "server: binding socket" + s.bind() + print "server: setting socket to listen" + s.listen() + print "server: awaiting connection" + sock = s.accept() + print "server: got connection" + + sock.send("Hello, echoing...\n") + buf = '' + while 1: + c = sock.recv(1) + if c == '': + sock.close() + print "server: socket closed" + break + + buf += c + if c == '\n': + sock.send("SERVER: "+buf) + buf = '' + + # client thread which reads lines and prints them to stdout + def clientThread(s): + buf = '' + while 1: + c = s.recv(1) + if c == '': + s.close() + print "client: socket closed" + break + buf += c + if c == '\n': + print "client: got %s" % repr(buf) + buf = '' + + print "launching server thread..." + thread.start_new_thread(servThread, (sServer,)) + + print "client: trying to connect" + sClient.connect(dServer) + + print "client: connected, launching rx thread" + thread.start_new_thread(clientThread, (sClient,)) + + while 1: + line = raw_input("Enter something (q to quit)> ") + if line == 'q': + print "closing client socket" + sClient.close() + break + sClient.send(line+"\n") + + print "I2PSocket test apparently succeeded" + +</t> +<t tx="davidmcnab.041004144338.109">if __name__ == '__main__': + main() + +</t> +<t tx="davidmcnab.041004144551">@first #!/usr/bin/env python +""" +Implements a client API for I2CP messaging via SAM + +Very simple I2P messaging interface, which should prove easy +to reimplement in your language of choice + +This module can be used from cpython or jython + +Run this module without arguments to see a demo in action +(requires SAM server to be already running) +""" +@others + +</t> +<t tx="davidmcnab.041004144551.1">import sys, os, socket, thread, threading, Queue, traceback, StringIO, time + +</t> +<t tx="davidmcnab.041004144551.2"># ----------------------------------------- +# server access settings + +i2psamhost = '127.0.0.1' +i2psamport = 7656 + +# ------------------------------------------ +# logging settings + +# 1=v.quiet, 2=normal, 3=verbose, 4=debug, 5=painful +verbosity = 5 + +# change to a filename to log there instead +logfile = sys.stdout + +# when set to 1, and when logfile != sys.stdout, log msgs are written +# both to logfile and console stdout +log2console = 1 + +# don't touch this! +loglock = threading.Lock() + +</t> +<t tx="davidmcnab.041004144551.3">class I2PServerFail(Exception): + """ + A failure in connecting to the I2CP server + """ + +class I2PCommandFail(Exception): + """ + A failure in an I2CP command + """ + pass +</t> +<t tx="davidmcnab.041004144551.4">class I2PSamClient: + """ + Implements a reference client for accessing I2CP via i2psam + + Connects to i2psam's I2PSamServer, sends commands + and receives results + + The primitives should be reasonably self-explanatory + + Usage summary: + 1. create one or more I2PSamClient instances per process (1 should be fine) + 2. invoke the L{genkeys} method to create destination keypairs + 3. create sessions objects via the L{createSession} method + 4. use these session objects to send and receive data + 5. destroy the session objects when you're done + + Refer to the function L{demo} for a simple example + """ + @others +</t> +<t tx="davidmcnab.041004144551.5"># server host/port settings exist here in case you might +# have a reason for overriding in a subclass + +host = i2psamhost +port = i2psamport + +i2cpHost = None +i2cpPort = None + +</t> +<t tx="davidmcnab.041004144551.6">def __init__(self, **kw): + """ + Creates a client connection to i2psam listener + + Keywords: + - host - host to connect to (default 127.0.0.1) + - port - port to connect to (default 7656) + """ + # get optional host/port + log(4, "entered") + + self.host = kw.get('host', self.host) + self.port = int(kw.get('port', self.port)) + + self.cmdLock = threading.Lock() + + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + self.lockHello = threading.Lock() + self.sendLock = threading.Lock() + self.qNewDests = Queue.Queue() + self.qSession = Queue.Queue() + self.qDatagrams = Queue.Queue() + self.qRawMessages = Queue.Queue() + self.namingReplies = {} + self.namingCache = {} + self.isRunning = 1 + + log(4, "trying connection to SAM server...") + try: + self.sock.connect((self.host, self.port)) + except: + raise I2PServerFail( + "Connection to i2psam server failed\n" + "(are you sure your I2P router is running, and\n" + "listening for I2CP connections on %s:%s?)" % (self.host, self.port) + ) + + # fire up receiver thread + thread.start_new_thread(self.threadRx, ()) + + # ping the server + try: + log(4, "trying to ping SAM server...") + self.samHello() + except: + logException(4, "Exception on handshaking") + raise I2PServerFail("Failed to handshake with i2psam server") + + # connected fine + log(2, "I2CP Client successfully connected") +</t> +<t tx="davidmcnab.041004144551.7">def createSession(self, privdest): + """ + DEPRECATED - use sam* methods instead! + + Creates a session using private destkey + """ + #3. createsession: + # - client->server: + # - createsession <base64private>\n + # - server->client: + # - ok\n OR + # - error[ <reason>]\n + + self.cmdLock.acquire() + try: + self._sendline("createsession %s" % privdest) + respitems = self._recvline().split(" ", 1) + if respitems[0] == 'ok': + res = None + else: + res = respitems[1] + except: + logException(2, "createsession fail") + self.cmdLock.release() + raise + + self.cmdLock.release() + + if res: + raise I2PCommandFail("createsession fail: "+res) + + return I2PRemoteSession(self, privdest) + +</t> +<t tx="davidmcnab.041004144551.8">def destroySession(self, privdest): + """ + DEPRECATED - use sam* methods instead! + + Destrlys a session using private destkey + """ + #4. destroysession: + # - client->server: + # - destroysession <base64private>\n + # - server->client: + # - ok\n OR + # - error[ <reason>]\n + + self.cmdLock.acquire() + try: + self._sendline("destroysession %s" % privdest) + respitems = self._recvline().split(" ", 1) + if respitems[0] == 'ok': + res = None + else: + res = respitems[1] + except: + logException(2, "destroysession fail") + self.cmdLock.release() + raise + + self.cmdLock.release() + + if res: + raise I2PCommandFail("destroysession fail: " + res) + + return res + +</t> +<t tx="davidmcnab.041004144551.9">def send(self, privdest, peerdest, msg): + """ + DEPRECATED - use sam* methods instead! + + Sends a block of data from local dest to remote dest + """ + #5. send: + # - client->server: + # - send <size> <localbase64private> <remotebase64dest>\ndata + # - server->client: + # - ok\n OR + # - error[ <reason>]\n + + self.cmdLock.acquire() + try: + self._sendline("send %s %s %s" % (len(msg), privdest, peerdest)) + self._sendbytes(msg) + line = self._recvline() + #print "** %s" % line + respitems = line.split(" ", 1) + if respitems[0] == 'ok': + res = None + else: + res = " ".join(respitems[1:]) + except: + logException(2, "send fail") + self.cmdLock.release() + raise + + self.cmdLock.release() + + if res: + raise I2PCommandFail("send fail: " + res) + + return res + +</t> +<t tx="davidmcnab.041004144551.10">def receive(self, privdest): + """ + DEPRECATED - use sam* methods instead! + + receives a block of data, returning string, or None if no data available + """ + #6. receive: + # - client->server: + # - receive <localbase64private>\n + # - server->client: + # - ok <size>\ndata OR + # - error[ <reason>]\n + + self.cmdLock.acquire() + try: + self._sendline("receive %s" % privdest) + respitems = self._recvline().split(" ", 1) + if respitems[0] == 'ok': + res = None + size = int(respitems[1]) + msg = self._recvbytes(size) + res = None + else: + res = respitems[1] + except: + logException(2, "receive fail") + self.cmdLock.release() + raise + + self.cmdLock.release() + + if res: + raise I2PCommandFail("destroysession fail: " + res) + + return msg +</t> +<t tx="davidmcnab.041004144551.11"></t> +<t tx="davidmcnab.041004144551.12">def samHello(self): + """ + Sends a quick HELLO PING to SAM server and awaits response + Arguments: + - none + + Keywords: + - none + + Returns: + - nothing (None) if ping sent and pong received, or raises an exception if + failed + """ + self.lockHello.acquire() + self.samSend("HELLO", "PING") + self.lockHello.acquire() + self.lockHello.release() +</t> +<t tx="davidmcnab.041004144551.13">def samSessionCreate(self, style, dest, **kw): + """ + Creates a SAM session + + Arguments: + - style - one of 'STREAM', 'DATAGRAM' or 'RAW' + - dest - base64 private destination + + Keywords: + - i2cphost - hostname for the SAM bridge to contact i2p router on + - i2cpport - port for the SAM bridge to contact i2p router on + + Returns: + - 'OK' if session was created successfully, or a tuple + (keyword, message) if not + """ + kw1 = dict(kw) + kw1['STYLE'] = self.samStyle = style + kw1['DESTINATION'] = dest + + # stick in i2cp host/port if specified + if kw.has_key('i2cphost'): + kw1['I2CP.HOST'] = kw['i2cphost'] + if kw.has_key('i2cpport'): + kw1['I2CP.PORT'] = kw['i2cpport'] + + self.samSend("SESSION", "CREATE", + **kw1) + subtopic, args = self.qSession.get() + + if args['RESULT'] == 'OK': + return 'OK' + else: + return (args['RESULT'], args['MESSAGE']) +</t> +<t tx="davidmcnab.041004144551.14">def samDestGenerate(self): + """ + Creates a whole new dest and returns an tuple pub, priv as + base64 public and private destination keys + """ + self.samSend("DEST", "GENERATE") + pub, priv = self.qNewDests.get() + return pub, priv +</t> +<t tx="davidmcnab.041004144551.15">def samRawSend(self, peerdest, msg): + """ + Sends a raw anon message to another peer + + peerdest is the public base64 destination key of the peer + """ + self.samSend("RAW", "SEND", msg, + DESTINATION=peerdest, + ) +</t> +<t tx="davidmcnab.041004144551.16">def samRawCheck(self): + """ + Returns 1 if there are received raw messages available, 0 if not + """ + return not self.qRawMessages.empty() +</t> +<t tx="davidmcnab.041004144551.17">def samRawReceive(self, blocking=1): + """ + Returns the next raw message available, + blocking if none is available and the blocking arg is set to 0 + + If blocking is 0, and no messages are available, returns None. + + Remember that you can check for availability with + the .samRawCheck() method + """ + if not blocking: + if self.qRawMessages.empty(): + return None + return self.qRawMessages.get() + </t> +<t tx="davidmcnab.041004144551.18">def samDatagramSend(self, peerdest, msg): + """ + Sends a repliable datagram message to another peer + + peerdest is the public base64 destination key of the peer + """ + self.samSend("DATAGRAM", "SEND", msg, + DESTINATION=peerdest, + ) +</t> +<t tx="davidmcnab.041004144551.19">def samDatagramCheck(self): + """ + Returns 1 if there are datagram messages received messages available, 0 if not + """ + return not self.qDatagrams.empty() +</t> +<t tx="davidmcnab.041004144551.20">def samDatagramReceive(self, blocking=1): + """ + Returns the next datagram message available, + blocking if none is available. + + If blocking is set to 0, and no messages are available, + returns None. + + Remember that you can check for availability with + the .samRawCheck() method + + Returns 2-tuple: dest, msg + where dest is the base64 destination of the peer from + whom the message was received + """ + if not blocking: + if self.qDatagrams.empty(): + return None + return self.qDatagrams.get() +</t> +<t tx="davidmcnab.041004144551.21">def samNamingLookup(self, host): + """ + Looks up a host in hosts.txt + """ + # try the cache first + if self.namingCache.has_key(host): + log(4, "found host %s in cache" % host) + return self.namingCache[host] + + # make a queue for reply + q = self.namingReplies[host] = Queue.Queue() + + # send off req + self.samSend("NAMING", "LOOKUP", + NAME=host, + ) + + # get resp + resp = q.get() + + result = resp.get('RESULT', 'none') + if result == 'OK': + log(4, "adding host %s to cache" % host) + val = resp['VALUE'] + self.namingCache[host] = val + return val + else: + raise I2PCommandFail("Error looking up '%s': %s %s" % ( + host, result, resp.get('MESSAGE', ''))) + +</t> +<t tx="davidmcnab.041004144551.22">def samParse(self, flds): + """ + carves up a SAM command, returns it as a 3-tuple: + - cmd - command string + - subcmd - subcommand string + - dargs - dict of args + """ + cmd = flds[0] + subcmd = flds[1] + args = flds[2:] + + dargs = {} + for arg in args: + try: + name, val = arg.split("=", 1) + except: + logException(3, "failed to process %s" % repr(arg)) + raise + dargs[name] = val + + # read and add data if any + if dargs.has_key('SIZE'): + size = dargs['SIZE'] = int(dargs['SIZE']) + dargs['DATA'] = self._recvbytes(size) + + #log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v[:40])) for k,v in dargs.items()])) + log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v)) for k,v in dargs.items()])) + + return cmd, subcmd, dargs + + + +</t> +<t tx="davidmcnab.041004144551.23">def samSend(self, topic, subtopic, data=None, **kw): + """ + Sends a SAM message (reply?) back to client + + Arguments: + - topic - the first word in the reply, eg 'STREAM' + - subtopic - the second word of the reply, eg 'CONNECTED' + - data - a string of raw data to send back (optional) + Keywords: + - extra 'name=value' items to pass back. + + Notes: + 1. SIZE is not required. If sending back data, it will + be sized and a SIZE arg inserted automatically. + 2. a dict of values can be passed to the 'args' keyword, in lieu + of direct keywords. This allows for cases where arg names would + cause python syntax clashes, eg 'tunnels.depthInbound' + """ + items = [topic, subtopic] + + # stick in SIZE if needed + if data is not None: + kw['SIZE'] = str(len(data)) + else: + data = '' # for later + + self.samCreateArgsList(kw, items) + + # and whack it together + buf = " ".join(items) + '\n' + data + + # and ship it + self.sendLock.acquire() + try: + self._sendbytes(buf) + except: + self.sendLock.release() + raise + self.sendLock.release() + +</t> +<t tx="davidmcnab.041004144551.24">def samCreateArgsList(self, kw1, lst): + for k,v in kw1.items(): + if k == 'args': + self.samCreateArgsList(v, lst) + else: + lst.append("=".join([str(k), str(v)])) +</t> +<t tx="davidmcnab.041004144551.25"></t> +<t tx="davidmcnab.041004144551.26">def threadRx(self): + """ + Handles all incoming stuff from SAM, storing in + local queues as appropriate + """ + while self.isRunning: + try: + log(4, "Awaiting next message from server") + line = self._recvline() + if line == '': + log(3, "I2P server socket closed") + return + flds = line.split(" ") + topic, subtopic, args = self.samParse(flds) + log(4, "Got %s %s %s" % (topic, subtopic, args)) + handleMsg = getattr(self, "on_"+topic, None) + if handleMsg: + handleMsg(topic, subtopic, args) + else: + log(2, "No handler for '%s' message" % topic) + except: + #logException(3, "Exception handling %s %s\n%s" % (topic, subtopic, args)) + logException(3, "Exception handling %s" % repr(line)) +</t> +<t tx="davidmcnab.041004144551.27">def on_HELLO(self, topic, subtopic, args): + """ + Handles HELLO PONG messages from server + """ + # just wake up the caller + log(4, "got HELLO") + self.lockHello.release() +</t> +<t tx="davidmcnab.041004144551.28">def on_SESSION(self, topic, subtopic, args): + """ + Handles SESSION messages from server + """ + # just stick whatever on the queue and wake up the caller + res = subtopic, args + self.qSession.put(res) +</t> +<t tx="davidmcnab.041004144551.29">def on_STREAM(self, topic, subtopic, args): + """ + Handles STREAM messages from server + """ +</t> +<t tx="davidmcnab.041004144551.30">def on_DATAGRAM(self, topic, subtopic, args): + """ + Handles DATAGRAM messages from server + """ + remdest = args['DESTINATION'] + data = args['DATA'] + + self.qDatagrams.put((remdest, data)) +</t> +<t tx="davidmcnab.041004144551.31">def on_RAW(self, topic, subtopic, args): + """ + Handles RAW messages from server + """ + data = args['DATA'] + + log(3, "Got anonymous datagram %s" % repr(data)) + self.qRawMessages.put(data) +</t> +<t tx="davidmcnab.041004144551.32">def on_NAMING(self, topic, subtopic, args): + """ + Handles NAMING messages from server + """ + # just find out hostname, and stick it on resp q + host = args['NAME'] + self.namingReplies[host].put(args) +</t> +<t tx="davidmcnab.041004144551.33">def on_DEST(self, topic, subtopic, args): + """ + Handles DEST messages from server + """ + pubkey = args['PUB'] + privkey = args['PRIV'] + res = pubkey, privkey + self.qNewDests.put(res) +</t> +<t tx="davidmcnab.041004144551.34"></t> +<t tx="davidmcnab.041004144551.35">def _recvline(self): + """ + Guaranteed read of a full line + """ + chars = [] + while 1: + c = self.sock.recv(1) + if c in ['', '\n']: + break + chars.append(c) + return "".join(chars) +</t> +<t tx="davidmcnab.041004144551.36">def _recvbytes(self, num): + """ + Guaranteed read of num bytes + """ + if num <= 0: + return "" + + reqd = num + chunks = [] + while reqd > 0: + chunk = self.sock.recv(reqd) + if not chunk: + raise I2PServerFail("Buffer read fail") + chunks.append(chunk) + reqd -= len(chunk) + return "".join(chunks) +</t> +<t tx="davidmcnab.041004144551.37">def _sendbytes(self, buf): + """ + Guaranteed complete send of a buffer + """ + reqd = len(buf) + while reqd > 0: + nsent = self.sock.send(buf) + if nsent == 0: + raise I2PServerFail("Send to server failed") + buf = buf[nsent:] + reqd -= nsent +</t> +<t tx="davidmcnab.041004144551.38">def _sendline(self, line): + """ + just tacks on a newline and sends + """ + self._sendbytes(line+"\n") +</t> +<t tx="davidmcnab.041004144551.39">class I2PRemoteSession: + """ + DEPRECATED + + Wrapper for I2CP connections + + Do not instantiate this directly - it gets created by + I2PSamClient.createSession() + """ + @others +</t> +<t tx="davidmcnab.041004144551.40">def __init__(self, client, dest): + """ + Do not instantiate this directly + """ + self.client = client + self.dest = dest +</t> +<t tx="davidmcnab.041004144551.41">def send(self, peerdest, msg): + + return self.client.send(self.dest, peerdest, msg) +</t> +<t tx="davidmcnab.041004144551.42">def receive(self): + + return self.client.receive(self.dest) +</t> +<t tx="davidmcnab.041004144551.43">def destroy(self): + + return self.client.destroySession(self.dest) + +</t> +<t tx="davidmcnab.041004144551.44"></t> +<t tx="davidmcnab.041004144551.45">def log(level, msg, nPrev=0): + + # ignore messages that are too trivial for chosen verbosity + if level > verbosity: + return + + loglock.acquire() + try: + # rip the stack + caller = traceback.extract_stack()[-(2+nPrev)] + path, line, func = caller[:3] + path = os.path.split(path)[1] + full = "%s:%s:%s():\n* %s" % ( + path, + line, + func, + msg.replace("\n", "\n + ")) + now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + msg = "%s %s\n" % (now, full) + + if logfile == sys.stdout: + print msg + else: + file(logfile, "a").write(msg+"\n") + except: + s = StringIO.StringIO() + traceback.print_exc(file=s) + print s.getvalue() + print "Logger crashed" + loglock.release()</t> +<t tx="davidmcnab.041004144551.46">def logException(level, msg=''): + s = StringIO.StringIO() + traceback.print_exc(file=s) + log(level, "%s\n%s" % (s.getvalue(), msg), 1) +</t> +<t tx="davidmcnab.041004144551.47">def demoNAMING(): + """ + Demonstrates the NAMING service + """ + print "Starting SAM NAMING demo..." + print + + print "Instantiating client connection..." + c0 = I2PSamClient() + print "Client connection created" + + for host in ['duck.i2p', 'nonexistent.i2p']: + print "Sending query for host '%s'..." % host + try: + res = c0.samNamingLookup(host) + print "query for %s returned:" % host + print repr(res) + except I2PCommandFail, e: + print "got exception: %s" % repr(e.args) + + print + print "---------------------------------" + print "NAMING service tests succeeded" + print "---------------------------------" + print + + +</t> +<t tx="davidmcnab.041004144551.48">def demoRAW(): + """ + Runs a demo of SAM RAW messaging + """ + print "Starting SAM RAW demo..." + print + + print "Instantiating client connections..." + c1 = I2PSamClient() + c2 = I2PSamClient() + + print "Creating dests via SAM" + pub1, priv1 = c1.samDestGenerate() + pub2, priv2 = c2.samDestGenerate() + print "SAM Dests generated ok" + + print "Creating SAM RAW SESSION on connection c1..." + res = c1.samSessionCreate("RAW", priv1) + if res != 'OK': + print "Failed to create session on connection c1: %s" % repr(res) + return + print "Session on connection c1 created successfully" + + print "Creating SAM SESSION on connection c2..." + res = c2.samSessionCreate("RAW", priv2) + if res != 'OK': + print "Failed to create session on connection c2: %s" % repr(res) + return + print "Session on connection c2 created successfully" + + msg = "Hi there!" + print "sending from c1 to c2: %s" % repr(msg) + c1.samRawSend(pub2, msg) + + print "now try to receive from c2 (will block)..." + msg1 = c2.samRawReceive() + print "Connection c2 got %s" % repr(msg1) + + print + print "---------------------------------" + print "RAW data transfer tests succeeded" + print "---------------------------------" + print + +</t> +<t tx="davidmcnab.041004144551.49">def demoDATAGRAM(): + """ + Runs a demo of SAM DATAGRAM messaging + """ + print "Starting SAM DATAGRAM demo..." + print + + print "Instantiating 2 more client connections..." + c3 = I2PSamClient() + c4 = I2PSamClient() + + print "Creating more dests via SAM" + pub3, priv3 = c3.samDestGenerate() + pub4, priv4 = c4.samDestGenerate() + + print "Creating SAM DATAGRAM SESSION on connection c3..." + res = c3.samSessionCreate("DATAGRAM", priv3) + if res != 'OK': + print "Failed to create DATAGRAM session on connection c3: %s" % repr(res) + return + print "DATAGRAM Session on connection c3 created successfully" + + print "Creating SAM DATAGRAM SESSION on connection c4..." + res = c4.samSessionCreate("DATAGRAM", priv4) + if res != 'OK': + print "Failed to create DATAGRAM session on connection c4: %s" % repr(res) + return + print "Session on connection c4 created successfully" + + msg = "Hi there, this is a datagram!" + print "sending from c3 to c4: %s" % repr(msg) + c3.samDatagramSend(pub4, msg) + + print "now try to receive from c4 (will block)..." + remdest, msg1 = c4.samDatagramReceive() + print "Connection c4 got %s from %s..." % (repr(msg1), repr(remdest)) + + + print + print "--------------------------------------" + print "DATAGRAM data transfer tests succeeded" + print "--------------------------------------" + print + +</t> +<t tx="davidmcnab.041004144551.50">def demoSTREAM(): + """ + Runs a demo of SAM STREAM messaging + """ + print "Starting SAM STREAM demo..." + print + + print "Instantiating 2 more client connections..." + c5 = I2PSamClient() + c6 = I2PSamClient() + + print "Creating more dests via SAM" + pub5, priv5 = c5.samDestGenerate() + pub6, priv6 = c6.samDestGenerate() + + print "Creating SAM STREAM SESSION on connection c3..." + res = c5.samSessionCreate("STREAM", priv5) + if res != 'OK': + print "Failed to create STREAM session on connection c5: %s" % repr(res) + return + print "STREAM Session on connection c5 created successfully" + + print "Creating SAM STREAM SESSION on connection c6..." + res = c6.samSessionCreate("STREAM", priv6) + if res != 'OK': + print "Failed to create STREAM session on connection c4: %s" % repr(res) + return + print "STREAM Session on connection c4 created successfully" + + msg = "Hi there, this is a datagram!" + print "sending from c5 to c6: %s" % repr(msg) + c5.samStreamSend(pub6, msg) + + print "now try to receive from c6 (will block)..." + msg1 = c6.samStreamReceive() + print "Connection c6 got %s from %s..." % (repr(msg1), repr(remdest)) + + print + print "--------------------------------------" + print "DATAGRAM data transfer tests succeeded" + print "--------------------------------------" + print + +</t> +<t tx="davidmcnab.041004144551.51">def demo(): + """ + This is a simple and straightforward demo of talking to + the i2psam server socket via the I2PSamClient class. + + Read the source, Luke, it's never been so easy... + """ + print + print "-----------------------------------------" + print "Running i2psamclient demo..." + print "-----------------------------------------" + print + + demoNAMING() + demoRAW() + demoDATAGRAM() + #demoSTREAM() + + print + print "-----------------------------------------" + print "Demo Finished" + print "-----------------------------------------" + + return +</t> +<t tx="davidmcnab.041004144551.52">if __name__ == '__main__': + + demo() +</t> +</tnodes> +</leo_file>