forked from I2P_Developers/i2p.i2p

client sources in a dissected XML form. If you're working on I2P SAM, we strongly encourage you to install the Leo editor (http://leo.sf.net), and use it to edit the sources. Otherwise, we're stuck with the menial task of re-importing your changes into the Leo tree. Thanks for your understanding and co-operation.
3431 lines
105 KiB
XML
3431 lines
105 KiB
XML
<?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>
|