beginning of branch i2p.i2p.i2p

This commit is contained in:
cvs_import
2004-04-08 04:41:54 +00:00
committed by zzz
commit 77bd69c5e5
292 changed files with 41035 additions and 0 deletions

278
apps/httptunnel/doc/COPYING Normal file
View File

@ -0,0 +1,278 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

View File

@ -0,0 +1,11 @@
$Id$
the i2p/apps/httptunnel module is the root of the
HTTPTunnel application, and everything within it
is released according to the terms of the I2P
license policy. That means everything contained
within the i2p/apps/httptunnel module is released
under the GPL plus the java exception unless
otherwise marked. Alternate licenses that may be
used include BSD, Cryptix, and MIT, as well as
code granted into the public domain.

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="httptunnel">
<target name="all" depends="clean, build" />
<target name="build" depends="builddep, jar" />
<target name="builddep">
<ant dir="../../ministreaming/java/" target="build" />
<ant dir="../../../core/java/" target="build" />
</target>
<target name="compile">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac
srcdir="./src"
debug="true"
destdir="./build/obj"
classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />
</target>
<target name="jar" depends="compile">
<jar destfile="./build/httptunnel.jar" basedir="./build/obj" includes="**/*.class">
<manifest>
<attribute name="Main-Class" value="net.i2p.httptunnel.HTTPTunnel" />
<attribute name="Class-Path" value="i2p.jar mstreaming.jar" />
</manifest>
</jar>
</target>
<target name="javadoc">
<mkdir dir="./build" />
<mkdir dir="./build/javadoc" />
<javadoc
sourcepath="./src:../../../core/java/src:../../ministreaming/java/src" destdir="./build/javadoc"
packagenames="*"
use="true"
splitindex="true"
windowtitle="HTTPTunnel" />
</target>
<target name="clean">
<delete dir="./build" />
</target>
<target name="cleandep" depends="clean">
<ant dir="../../../core/java/" target="cleandep" />
<ant dir="../../ministreaming/java/" target="distclean" />
</target>
<target name="distclean" depends="clean">
<ant dir="../../../core/java/" target="distclean" />
<ant dir="../../ministreaming/java/" target="distclean" />
</target>
</project>

View File

@ -0,0 +1,67 @@
package net.i2p.httptunnel;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import net.i2p.util.Log;
/**
* Listens on a port for HTTP connections.
*/
public class HTTPListener extends Thread {
private static final Log _log = new Log(HTTPListener.class);
private int port;
private String listenHost;
private SocketManagerProducer smp;
public HTTPListener(SocketManagerProducer smp, int port,
String listenHost) {
this.smp = smp;
this.port = port;
start();
}
public void run() {
try {
InetAddress lh = listenHost == null
? null
: InetAddress.getByName(listenHost);
ServerSocket ss = new ServerSocket(port, 0, lh);
while(true) {
Socket s = ss.accept();
new HTTPSocketHandler(this, s);
}
} catch (IOException ex) {
_log.error("Error while accepting connections", ex);
}
}
private boolean proxyUsed=false;
public boolean firstProxyUse() {
// FIXME: check a config option here
if (true) return false;
if (proxyUsed) {
return false;
} else {
proxyUsed=true;
return true;
}
}
public SocketManagerProducer getSMP() {
return smp;
}
/** @deprecated */
public void handleNotImplemented(OutputStream out) throws IOException {
out.write(("HTTP/1.1 200 Document following\n\n"+
"<h1>Feature not implemented</h1>").getBytes("ISO-8859-1"));
out.flush();
}
}

View File

@ -0,0 +1,54 @@
package net.i2p.httptunnel;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import net.i2p.httptunnel.handler.RootHandler;
import net.i2p.util.Log;
/**
* Handles a single HTTP socket connection.
*/
public class HTTPSocketHandler extends Thread {
private static final Log _log = new Log(HTTPSocketHandler.class);
private Socket s;
private HTTPListener httpl;
private RootHandler h;
public HTTPSocketHandler(HTTPListener httpl, Socket s) {
this.httpl = httpl;
this.s=s;
h = RootHandler.getInstance();
start();
}
public void run() {
InputStream in = null;
OutputStream out = null;
try {
in = new BufferedInputStream(s.getInputStream());
out = new BufferedOutputStream(s.getOutputStream());
Request req = new Request(in);
h.handle(req, httpl, out);
} catch (IOException ex) {
_log.error("Error while handling data", ex);
} finally {
try {
if (in != null) in.close();
if (out != null) {
out.flush();
out.close();
}
s.close();
} catch (IOException ex) {
_log.error("IOException in finalizer", ex);
}
}
}
}

View File

@ -0,0 +1,110 @@
/*
* HTTPTunnel
* (c) 2003 - 2004 mihi
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* In addition, as a special exception, mihi gives permission to link
* the code of this program with the proprietary Java implementation
* provided by Sun (or other vendors as well), and distribute linked
* combinations including the two. You must obey the GNU General
* Public License in all respects for all of the code used other than
* the proprietary Java implementation. If you modify this file, you
* may extend this exception to your version of the file, but you are
* not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
package net.i2p.httptunnel;
import net.i2p.client.streaming.I2PSocketManager;
/**
* HTTPTunnel main class.
*/
public class HTTPTunnel {
/**
* Create a HTTPTunnel instance.
*
* @param initialManagers a list of socket managers to use
* @param maxManagers how many managers to have in the cache
* @param mcDonaldsMode whether to throw away a manager after use
* @param listenPort which port to listen on
*/
public HTTPTunnel(I2PSocketManager[] initialManagers, int maxManagers,
boolean mcDonaldsMode, int listenPort) {
this(initialManagers, maxManagers, mcDonaldsMode, listenPort,
"127.0.0.1", 7654);
}
/**
* Create a HTTPTunnel instance.
*
* @param initialManagers a list of socket managers to use
* @param maxManagers how many managers to have in the cache
* @param mcDonaldsMode whether to throw away a manager after use
* @param listenPort which port to listen on
* @param i2cpAddress the I2CP address
* @param i2cpPort the I2CP port
*/
public HTTPTunnel(I2PSocketManager[] initialManagers, int maxManagers,
boolean mcDonaldsMode, int listenPort,
String i2cpAddress, int i2cpPort) {
SocketManagerProducer smp =
new SocketManagerProducer(initialManagers, maxManagers,
mcDonaldsMode, i2cpAddress, i2cpPort);
new HTTPListener(smp, listenPort, "127.0.0.1");
}
public static void main(String[] args) {
String host = "127.0.0.1";
int port = 7654, max = 1;
boolean mc = false;
if (args.length >1) {
if (args.length == 4) {
host = args[2];
port = Integer.parseInt(args[3]);
} else if (args.length != 2) {
showInfo(); return;
}
max = Integer.parseInt(args[1]);
} else if (args.length != 1) {
showInfo(); return;
}
if (max == 0) {
max = 1;
} else if (max <0) {
max = -max;
mc = true;
}
new HTTPTunnel(null, max, mc, Integer.parseInt(args[0]), host, port);
}
private static void showInfo() {
System.out.println
("Usage: java HTTPTunnel <listenPort> [<max> "+
"[<i2cphost> <i2cpport>]]\n"+
" <listenPort> port to listen for browsers\n"+
" <max> max number of SocketMangers in pool, "+
"use neg. number\n"+
" to use each SocketManager only once "+
"(default: 1)\n"+
" <i2cphost> host to connect to the router "+
"(default: 127.0.0.1)\n"+
" <i2cpport> port to connect to the router "+
"(default: 7654)");
}
}

View File

@ -0,0 +1,130 @@
package net.i2p.httptunnel;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import net.i2p.util.Log;
/**
* A HTTP request (GET or POST). This will be passed to a hander for
* handling it.
*/
public class Request {
private static final Log _log = new Log(Request.class);
// all strings are forced to be ISO-8859-1 encoding
private String method;
private String url;
private String proto;
private String params;
private String postData;
public Request(InputStream in) throws IOException {
BufferedReader br = new BufferedReader
(new InputStreamReader(in, "ISO-8859-1"));
String line = br.readLine();
if (line == null) { // no data at all
method = null;
_log.error("Connection but no data");
return;
}
int pos = line.indexOf(" ");
if (pos == -1) {
method = line;
url="";
_log.error("Malformed HTTP request: "+line);
} else {
method = line.substring(0,pos);
url=line.substring(pos+1);
}
proto="";
pos = url.indexOf(" ");
if (pos != -1) {
proto=url.substring(pos); // leading space intended
url = url.substring(0,pos);
}
StringBuffer sb = new StringBuffer(512);
while((line=br.readLine()) != null) {
if (line.length() == 0) break;
sb.append(line).append("\r\n");
}
params = sb.toString(); // no leading empty line!
sb = new StringBuffer();
// hack for POST requests, ripped from HttpClient
// this won't work for large POSTDATA
// FIXME: do this better, please.
if (!method.equals("GET")) {
while (br.ready()) { // empty the buffer (POST requests)
int i=br.read();
if (i != -1) {
sb.append((char)i);
}
}
postData = sb.toString();
} else {
postData="";
}
}
public byte[] toByteArray() throws IOException {
if (method == null) return null;
return toISO8859_1String().getBytes("ISO-8859-1");
}
private String toISO8859_1String() throws IOException {
if (method == null) return null;
return method+" "+url+proto+"\r\n"+params+"\r\n"+postData;
}
public String getURL() {
return url;
}
public void setURL(String newURL) {
url=newURL;
}
public String getParam(String name) {
try {
BufferedReader br= new BufferedReader(new StringReader(params));
String line;
while ((line = br.readLine()) != null) {
if (line.startsWith(name)) {
return line.substring(name.length());
}
}
return null;
} catch (IOException ex) {
_log.error("Error getting parameter", ex);
return null;
}
}
public void setParam(String name, String value) {
try {
StringBuffer sb = new StringBuffer(params.length()+value.length());
BufferedReader br= new BufferedReader(new StringReader(params));
String line;
boolean replaced = false;
while((line=br.readLine()) != null) {
if (line.startsWith(name)) {
replaced=true;
if (value == null) continue; // kill param
line = name+value;
}
sb.append(line).append("\r\n");
}
if (!replaced && value != null) {
sb.append(name).append(value).append("\r\n");
}
params=sb.toString();
} catch (IOException ex) {
_log.error("Error getting parameter", ex);
}
}
}

View File

@ -0,0 +1,111 @@
package net.i2p.httptunnel;
import java.util.*;
import net.i2p.client.streaming.*;
/**
* Produces SocketManagers in a thread and gives them to those who
* need them.
*/
public class SocketManagerProducer extends Thread {
private ArrayList myManagers = new ArrayList();
private HashMap usedManagers = new HashMap();
private int port;
private String host;
private int maxManagers;
private boolean mcDonalds;
public SocketManagerProducer(I2PSocketManager[] initialManagers,
int maxManagers,
boolean mcDonaldsMode,
String host, int port) {
if (maxManagers < 1) {
throw new IllegalArgumentException("maxManagers < 1");
}
this.host=host;
this.port=port;
mcDonalds=mcDonaldsMode;
if (initialManagers != null) {
myManagers.addAll(Arrays.asList(initialManagers));
}
this.maxManagers=maxManagers;
mcDonalds=mcDonaldsMode;
setDaemon(true);
start();
}
/**
* Thread producing new SocketManagers.
*/
public void run() {
while (true) {
synchronized(this) {
// without mcDonalds mode, we most probably need no
// new managers.
while (!mcDonalds && myManagers.size() == maxManagers) {
myWait();
}
}
// produce a new manager, regardless whether it is needed
// or not. Do not synchronized this part, since it can be
// quite time-consuming.
I2PSocketManager newManager =
I2PSocketManagerFactory.createManager(host, port,
new Properties());
// when done, check if it is needed.
synchronized(this) {
while(myManagers.size() == maxManagers) {
myWait();
}
myManagers.add(newManager);
notifyAll();
}
}
}
/**
* Get a manager for connecting to a given destination. Each
* destination will always get the same manager.
*
* @param dest the destination to connect to
* @return the SocketManager to use
*/
public synchronized I2PSocketManager getManager(String dest) {
I2PSocketManager result = (I2PSocketManager) usedManagers.get(dest);
if (result == null) {
result = getManager();
usedManagers.put(dest,result);
}
return result;
}
/**
* Get a "new" SocketManager. Depending on the anonymity settings,
* this can be a completely new one or one randomly selected from
* a pool.
*
* @return the SocketManager to use
*/
public synchronized I2PSocketManager getManager() {
while (myManagers.size() == 0) {
myWait(); // no manager here, so wait until one is produced
}
int which = (int)(Math.random()*myManagers.size());
I2PSocketManager result = (I2PSocketManager) myManagers.get(which);
if (mcDonalds) {
myManagers.remove(which);
notifyAll();
}
return result;
}
public void myWait() {
try {
wait();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}

View File

@ -0,0 +1,50 @@
package net.i2p.httptunnel.filter;
import net.i2p.util.Log;
import java.io.*;
import java.util.*;
/**
* Chain multiple filters. Decorator pattern...
*/
public class ChainFilter implements Filter {
private static final Log _log = new Log(ChainFilter.class);
public Collection filters;
public ChainFilter(Collection filters) {
this.filters=filters;
}
public byte[] filter(byte[] toFilter) {
byte[] buf = toFilter;
for (Iterator it = filters.iterator(); it.hasNext();) {
Filter f = (Filter) it.next();
buf = f.filter(buf);
}
return buf;
}
public byte[] finish() {
// this is a bit complicated. Think about it...
try {
byte[] buf = EMPTY;
for (Iterator it = filters.iterator(); it.hasNext();) {
Filter f = (Filter) it.next();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (buf.length != 0) {
baos.write(f.filter(buf));
}
baos.write(f.finish());
buf = baos.toByteArray();
}
return buf;
} catch (IOException ex) {
_log.error("Error chaining filters", ex);
return EMPTY;
}
}
}

View File

@ -0,0 +1,22 @@
package net.i2p.httptunnel.filter;
/**
* A generic filtering interface.
*/
public interface Filter {
/**
* An empty byte array.
*/
public static final byte[] EMPTY = new byte[0];
/**
* Filter some data. Not all filtered data need to be returned.
*/
public byte[] filter(byte[] toFilter);
/**
* Data stream has finished. Return all of the rest data.
*/
public byte[] finish();
}

View File

@ -0,0 +1,15 @@
package net.i2p.httptunnel.filter;
/**
* A filter letting everything pass as is.
*/
public class NullFilter implements Filter {
public byte[] filter(byte[] toFilter) {
return toFilter;
}
public byte[] finish() {
return EMPTY;
}
}

View File

@ -0,0 +1,93 @@
package net.i2p.httptunnel.handler;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.I2PException;
import net.i2p.client.naming.NamingService;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.Destination;
import net.i2p.httptunnel.HTTPListener;
import net.i2p.httptunnel.Request;
import net.i2p.httptunnel.SocketManagerProducer;
import net.i2p.httptunnel.filter.Filter;
import net.i2p.httptunnel.filter.NullFilter;
import net.i2p.util.Log;
/**
* Handler for browsing Eepsites.
*/
public class EepHandler {
private static final Log _log = new Log(EepHandler.class);
protected ErrorHandler errorHandler;
/* package private */ EepHandler(ErrorHandler eh) {
errorHandler=eh;
}
public void handle(Request req, HTTPListener httpl, OutputStream out,
boolean fromProxy, String destination)
throws IOException {
SocketManagerProducer smp = httpl.getSMP();
Destination dest = NamingService.getInstance().lookup(destination);
if (dest == null) {
errorHandler.handle(req, httpl, out,
"Could not lookup host: "+destination);
return;
}
I2PSocketManager sm = smp.getManager(destination);
Filter f = new NullFilter(); //FIXME: use other filter
req.setParam("Host: ", dest.toBase64());
if (!handle(req, f, out, dest, sm)) {
errorHandler.handle(req, httpl, out, "Unable to reach peer");
}
}
public boolean handle(Request req, Filter f, OutputStream out,
Destination dest, I2PSocketManager sm)
throws IOException {
I2PSocket s = null;
boolean written = false;
try {
synchronized(sm) {
s = sm.connect(dest, new I2PSocketOptions());
}
InputStream in = new BufferedInputStream(s.getInputStream());
OutputStream sout = new BufferedOutputStream(s.getOutputStream());
sout.write(req.toByteArray());
sout.flush();
byte[] buffer = new byte[16384], filtered;
int len;
while ((len=in.read(buffer)) != -1) {
if (len != buffer.length) {
byte[] b2 = new byte[len];
System.arraycopy(buffer, 0, b2, 0, len);
filtered=f.filter(b2);
} else {
filtered=f.filter(buffer);
}
written=true;
out.write(filtered);
}
filtered=f.finish();
written=true;
out.write(filtered);
out.flush();
} catch (IOException ex) {
_log.error("Error while handling eepsite request");
return written;
} catch (I2PException ex) {
_log.error("Error while handling eepsite request");
return written;
} finally {
if (s != null) s.close();
}
return true;
}
}

View File

@ -0,0 +1,36 @@
package net.i2p.httptunnel.handler;
import java.io.IOException;
import java.io.OutputStream;
import net.i2p.httptunnel.HTTPListener;
import net.i2p.httptunnel.Request;
import net.i2p.util.Log;
/**
* Handler for general error messages.
*/
public class ErrorHandler {
private static final Log _log = new Log(ErrorHandler.class);
/* package private */ ErrorHandler() {
}
public void handle(Request req, HTTPListener httpl,
OutputStream out, String error) throws IOException {
// FIXME: Make nicer messages for more likely errors.
out.write(("HTTP/1.1 500 Internal Server Error\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n\r\n")
.getBytes("ISO-8859-1"));
out.write(("<html><head><title>"+error+"</title></head><body><h1>"+
error+"</h1>An internal error occurred while "+
"handling a request by HTTPTunnel:<br><b>"+error+
"</b><h2>Complete request:</h2><b>---</b><br><i><pre>\r\n")
.getBytes("ISO-8859-1"));
out.write(req.toByteArray());
out.write(("</pre></i><br><b>---</b></body></html>")
.getBytes("ISO-8859-1"));
out.flush();
}
}

View File

@ -0,0 +1,49 @@
package net.i2p.httptunnel.handler;
import java.io.IOException;
import java.io.OutputStream;
import net.i2p.httptunnel.HTTPListener;
import net.i2p.httptunnel.Request;
import net.i2p.util.Log;
/**
* Handler for requests that do not require any connection to anyone
* (except errors).
*/
public class LocalHandler {
private static final Log _log = new Log(LocalHandler.class);
/* package private */ LocalHandler() {
}
public void handle(Request req, HTTPListener httpl, OutputStream out,
boolean fromProxy) throws IOException {
//FIXME: separate multiple pages, not only a start page
//FIXME: provide some info on this page
out.write(("HTTP/1.1 200 Document following\r\n"+
"Content-Type: text/html; charset=iso-8859-1\r\n\r\n"+
"<html><head><title>Welcome to I2P HTTPTunnel</title>"+
"</head><body><h1>Welcome to I2P HTTPTunnel</h1>You can "+
"browse Eepsites by adding an eepsite name to the request."+
"</body></html>").getBytes("ISO-8859-1"));
out.flush();
}
public void handleProxyConfWarning(Request req, HTTPListener httpl,
OutputStream out) throws IOException {
//FIXME
throw new IOException("jrandom ate the deprecated method. mooo");
//httpl.handleNotImplemented(out);
}
public void handleHTTPWarning(Request req, HTTPListener httpl,
OutputStream out, boolean fromProxy)
throws IOException {
// FIXME
throw new IOException("jrandom ate the deprecated method. mooo");
//httpl.handleNotImplemented(out);
}
}

View File

@ -0,0 +1,47 @@
package net.i2p.httptunnel.handler;
import java.io.IOException;
import java.io.OutputStream;
import net.i2p.client.naming.NamingService;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.data.Destination;
import net.i2p.httptunnel.HTTPListener;
import net.i2p.httptunnel.Request;
import net.i2p.httptunnel.SocketManagerProducer;
import net.i2p.httptunnel.filter.Filter;
import net.i2p.httptunnel.filter.NullFilter;
import net.i2p.util.Log;
/**
* Handler for proxying "normal" HTTP requests.
*/
public class ProxyHandler extends EepHandler {
private static final Log _log = new Log(ErrorHandler.class);
/* package private */ ProxyHandler(ErrorHandler eh) {
super(eh);
}
public void handle(Request req, HTTPListener httpl, OutputStream out,
boolean fromProxy) throws IOException {
SocketManagerProducer smp = httpl.getSMP();
Destination dest = findProxy();
if (dest == null) {
errorHandler.handle(req, httpl, out,
"Could not find proxy");
return;
}
// one manager for all proxy requests
I2PSocketManager sm = smp.getManager("--proxy--");
Filter f = new NullFilter(); //FIXME: use other filter
if (!handle(req, f, out, dest, sm)) {
errorHandler.handle(req, httpl, out, "Unable to reach peer");
}
}
private Destination findProxy() {
//FIXME!
return NamingService.getInstance().lookup("squid.i2p");
}
}

View File

@ -0,0 +1,109 @@
package net.i2p.httptunnel.handler;
import java.io.IOException;
import java.io.OutputStream;
import net.i2p.httptunnel.HTTPListener;
import net.i2p.httptunnel.Request;
import net.i2p.util.Log;
/**
* Main handler for all requests. Dispatches requests to other handlers.
*/
public class RootHandler {
private static final Log _log = new Log(RootHandler.class);
private RootHandler() {
errorHandler=new ErrorHandler();
localHandler=new LocalHandler();
proxyHandler=new ProxyHandler(errorHandler);
eepHandler=new EepHandler(errorHandler);
}
private ErrorHandler errorHandler;
private ProxyHandler proxyHandler;
private LocalHandler localHandler;
private EepHandler eepHandler;
private static RootHandler instance;
public static synchronized RootHandler getInstance() {
if (instance == null) {
instance = new RootHandler();
}
return instance;
}
public void handle(Request req, HTTPListener httpl,
OutputStream out) throws IOException {
String url=req.getURL();
System.out.println(url);
boolean byProxy = false;
int pos;
if (url.startsWith("http://")) { // access via proxy
byProxy=true;
if (httpl.firstProxyUse()) {
localHandler.handleProxyConfWarning(req,httpl,out);
return;
}
url = url.substring(7);
pos = url.indexOf("/");
String host;
String rest;
if (pos == -1) {
errorHandler.handle(req, httpl, out, "No host end in URL");
return;
} else {
host = url.substring(0,pos);
url = url.substring(pos);
if ("i2p".equals(host) || "i2p.i2p".equals(host)) {
// normal request; go on below...
} else if (host.endsWith(".i2p")) {
// "old" service request, send a redirect...
out.write(("HTTP/1.1 302 Moved\r\nLocation: "+
"http://i2p.i2p/"+host+url+
"\r\n\r\n").getBytes("ISO-8859-1"));
return;
} else {
// this is for proxying to the real web
proxyHandler.handle(req, httpl, out, true);
return;
}
}
}
if (url.equals("/")) { // main page
url="/_/local/index";
} else if (!url.startsWith("/")) {
errorHandler.handle(req, httpl, out,
"No leading slash in URL: "+url);
return;
}
String dest;
url=url.substring(1);
pos = url.indexOf("/");
if (pos == -1) {
dest=url;
url="/";
} else {
dest = url.substring(0,pos);
url=url.substring(pos);
}
req.setURL(url);
if (dest.equals("_")) { // no eepsite
if (url.startsWith("/local/")) { // local request
req.setURL(url.substring(6));
localHandler.handle(req, httpl, out, byProxy);
} else if (url.startsWith("/http/")) { // http warning
localHandler.handleHTTPWarning(req, httpl, out, byProxy);
} else if (url.startsWith("/proxy/")) { // http proxying
req.setURL("http://"+url.substring(7));
proxyHandler.handle(req, httpl, out, byProxy);
} else {
errorHandler.handle(req, httpl, out,
"No local handler for this URL: "+url);
}
} else {
eepHandler.handle(req, httpl, out, byProxy, dest);
}
}
}

278
apps/i2ptunnel/doc/COPYING Normal file
View File

@ -0,0 +1,278 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

View File

@ -0,0 +1,11 @@
$Id$
the i2p/apps/i2ptunnel module is the root of the
I2PTunnel application, and everything within it
is released according to the terms of the I2P
license policy. That means everything contained
within the i2p/apps/i2ptunnel module is released
under the GPL plus the java exception unless
otherwise marked. Alternate licenses that may be
used include BSD, Cryptix, and MIT, as well as
code granted into the public domain.

View File

@ -0,0 +1,21 @@
I2PTunnel allows to tunnel a usual TCP connection over I2P.
A server needs to have a public key; a client connects to that.
Starting a client:
tunnel <localport> <pubkey>
localport: the port where you want to connect to
pubkey: the public key of the server
Starting a server:
tunnel <remotehost> <remoteport> <privkey>
remotehost: the host to connect to when a connection comes.
remoteport: the port to connect to when a connection comes.
privkey: your private (secret) key
To stop a client/server, hit <Return>
Have fun!

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="i2ptunnel">
<target name="all" depends="clean, build" />
<target name="build" depends="builddep, jar" />
<target name="builddep">
<ant dir="../../ministreaming/java/" target="build" />
<ant dir="../../../core/java/" target="build" />
</target>
<target name="compile">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac
srcdir="./src"
debug="true"
destdir="./build/obj"
classpath="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar" />
</target>
<target name="jar" depends="compile">
<jar destfile="./build/i2ptunnel.jar" basedir="./build/obj" includes="**/*.class">
<manifest>
<attribute name="Main-Class" value="net.i2p.i2ptunnel.I2PTunnel" />
<attribute name="Class-Path" value="i2p.jar mstreaming.jar" />
</manifest>
</jar>
</target>
<target name="javadoc">
<mkdir dir="./build" />
<mkdir dir="./build/javadoc" />
<javadoc
sourcepath="./src:../../../core/java/src:../../ministreaming/java/src" destdir="./build/javadoc"
packagenames="*"
use="true"
splitindex="true"
windowtitle="I2PTunnel" />
</target>
<target name="clean">
<delete dir="./build" />
</target>
<target name="cleandep" depends="clean">
<ant dir="../../../core/java/" target="cleandep" />
<ant dir="../../ministreaming/java/" target="distclean" />
</target>
<target name="distclean" depends="clean">
<ant dir="../../../core/java/" target="distclean" />
<ant dir="../../ministreaming/java/" target="distclean" />
</target>
</project>

View File

@ -0,0 +1,61 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import net.i2p.util.Log;
/**
* Read what i2ptunnel logs, and expose it in a buffer
*
*/
class BufferLogger implements Logging {
private final static Log _log = new Log(BufferLogger.class);
private ByteArrayOutputStream _baos;
private boolean _ignore;
public BufferLogger() {
_baos = new ByteArrayOutputStream(512);
_ignore = false;
}
private final static String EMPTY = "";
public String getBuffer() {
if (_ignore) return EMPTY;
else return new String(_baos.toByteArray());
}
/**
* We don't care about anything else the logger receives. This is useful
* for loggers passed in to servers and clients, since they will continue
* to add info to the logger, but if we're instantiated by the tunnel manager,
* its likely we only care about the first few messages it sends us.
*
*/
public void ignoreFurtherActions() {
_ignore = true;
synchronized (_baos) {
_baos.reset();
}
_baos = null;
}
/**
* Pass in some random data
*/
public void log(String s) {
if (_ignore) return;
if (s != null) {
_log.debug("logging [" + s + "]");
try {
_baos.write(s.getBytes());
_baos.write('\n');
} catch (IOException ioe) {
_log.error("Error logging [" + s + "]");
}
}
}
}

View File

@ -0,0 +1,998 @@
/*
* I2PTunnel
* (c) 2003 - 2004 mihi
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* In addition, as a special exception, mihi gives permission to link
* the code of this program with the proprietary Java implementation
* provided by Sun (or other vendors as well), and distribute linked
* combinations including the two. You must obey the GNU General
* Public License in all respects for all of the code used other than
* the proprietary Java implementation. If you modify this file, you
* may extend this exception to your version of the file, but you are
* not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
package net.i2p.i2ptunnel;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import net.i2p.I2PException;
import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.naming.NamingService;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.util.EventDispatcher;
import net.i2p.util.EventDispatcherImpl;
import net.i2p.util.Log;
public class I2PTunnel implements Logging, EventDispatcher {
private final static Log _log = new Log(I2PTunnel.class);
private final EventDispatcherImpl _event = new EventDispatcherImpl();
public static final int PACKET_DELAY=100;
public static boolean ownDest = false;
public static String port =
System.getProperty(I2PClient.PROP_TCP_PORT, "7654");
public static String host = System.getProperty
(I2PClient.PROP_TCP_HOST,"127.0.0.1");
public static String listenHost = host;
private static final String nocli_args[] = {"-nocli", "-die"};
private List tasks=new ArrayList();
private int next_task_id = 1;
private Set listeners = new HashSet();
public static void main(String[] args) throws IOException {
new I2PTunnel(args);
}
public I2PTunnel() {
this(nocli_args);
}
public I2PTunnel(String[] args) {
this(args, null);
}
public I2PTunnel(String[] args, ConnectionEventListener lsnr) {
addConnectionEventListener(lsnr);
boolean gui=true;
boolean checkRunByE = true;;
boolean cli=true;
boolean dontDie = true;
for(int i=0;i<args.length;i++) {
if (args[i].equals("-die")) {
dontDie = false;
gui=false;
cli=false;
checkRunByE=false;
} else if (args[i].equals("-nogui")) {
gui=false;
_log.warn("The `-nogui' option of I2PTunnel is deprecated.\n"+
"Use `-cli', `-nocli' (aka `-wait') or `-die' instead.");
} else if (args[i].equals("-cli")) {
gui=false;
cli=true;
checkRunByE=false;
} else if (args[i].equals("-nocli") || args[i].equals("-wait")) {
gui=false;
cli=false;
checkRunByE=false;
} else if (args[i].equals("-e")) {
runCommand(args[i+1], this);
i++;
if (checkRunByE) {
checkRunByE = false;
cli=false;
}
} else if (new File(args[i]).exists()) {
runCommand("run "+args[i], this);
} else {
System.out.println("Unknown parameter "+args[i]);
}
}
if (gui) {
new I2PTunnelGUI(this);
} else if (cli) {
try {
System.out.println("Enter 'help' for help.");
BufferedReader r = new BufferedReader
(new InputStreamReader(System.in));
while(true) {
System.out.print("I2PTunnel>");
String cmd = r.readLine();
if (cmd == null) break;
runCommand(cmd, this);
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
while (dontDie) {
synchronized (this) {
try { wait(); } catch (InterruptedException ie) {}
}
}
}
private void addtask (I2PTunnelTask tsk)
{
tsk.setTunnel(this);
if (tsk.isOpen()) {
tsk.setId(next_task_id);
next_task_id++;
synchronized (tasks) {
tasks.add(tsk);
}
}
}
/** java 1.3 vs 1.4 :)
*/
private static String[] split(String src, String delim) {
StringTokenizer tok = new StringTokenizer(src, delim);
String vals[] = new String[tok.countTokens()];
for (int i = 0; i < vals.length; i++)
vals[i] = tok.nextToken();
return vals;
}
public void runCommand(String cmd, Logging l) {
if (cmd.indexOf(" ")== -1) cmd+=" ";
int iii=cmd.indexOf(" ");
String cmdname= cmd.substring(0,iii).toLowerCase();
String allargs = cmd.substring(iii+1);
String[] args = split(allargs, " "); // .split(" "); // java 1.4
if ("help".equals(cmdname)) {
runHelp(l);
} else if ("server".equals(cmdname)) {
runServer(args, l);
} else if ("textserver".equals(cmdname)) {
runTextServer(args, l);
} else if ("client".equals(cmdname)) {
runClient(args, l);
} else if ("httpclient".equals(cmdname)) {
runHttpClient(args, l);
} else if ("sockstunnel".equals(cmdname)) {
runSOCKSTunnel(args, l);
} else if ("config".equals(cmdname)) {
runConfig(args, l);
} else if ("listen_on".equals(cmdname)) {
runListenOn(args, l);
} else if ("genkeys".equals(cmdname)) {
runGenKeys(args, l);
} else if ("gentextkeys".equals(cmdname)) {
runGenTextKeys(l);
} else if (cmdname.equals("quit")) {
runQuit(l);
} else if (cmdname.equals("list")) {
runList(l);
} else if (cmdname.equals("close")) {
runClose(args, l);
} else if (cmdname.equals("run")) {
runRun(args, l);
} else if (cmdname.equals("lookup")) {
runLookup(args, l);
} else if (cmdname.equals("ping")) {
runPing(allargs, l);
} else if (cmdname.equals("owndest")) {
runOwnDest(args, l);
} else {
l.log("Unknown command [" + cmdname + "]");
}
}
/**
* Display help information through the given logger.
*
* Does not fire any events to the logger
*
* @param l logger to receive events and output
*/
public void runHelp(Logging l) {
l.log("Command list:");
l.log("config <i2phost> <i2pport>");
l.log("listen_on <ip>");
l.log("owndest yes|no");
l.log("ping <args>");
l.log("server <host> <port> <privkeyfile>");
l.log("textserver <host> <port> <privkey>");
l.log("genkeys <privkeyfile> [<pubkeyfile>]");
l.log("gentextkeys");
l.log("client <port> <pubkey>|file:<pubkeyfile>");
l.log("httpclient <port>");
l.log("lookup <name>");
l.log("quit");
l.log("close [forced] <jobnumber>|all");
l.log("list");
l.log("run <commandfile>");
}
/**
* Run the server pointing at the host and port specified using the private i2p
* destination loaded from the specified file
*
* Sets the event "serverTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error)
* Also sets the event "openServerResult" = "ok" or "error" (displaying "Ready!" on the logger after
* 'ok'). So, success = serverTaskId != -1 and openServerResult = ok.
*
* @param args {hostname, portNumber, privKeyFilename}
* @param l logger to receive events and output
*/
public void runServer(String args[], Logging l) {
if (args.length==3) {
InetAddress serverHost = null;
int portNum = -1;
File privKeyFile = null;
try {
serverHost = InetAddress.getByName(args[0]);
} catch (UnknownHostException uhe) {
l.log("unknown host");
_log.error("Error resolving " + args[0], uhe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
try {
portNum = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error("Port specified is not valid: " + args[1], nfe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
privKeyFile = new File(args[2]);
if (!privKeyFile.canRead()) {
l.log("private key file does not exist");
_log.error("Private key file does not exist or is not readable: " + args[2]);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
I2PTunnelTask task;
task = new I2PTunnelServer(serverHost, portNum, privKeyFile,
args[2], l, (EventDispatcher)this);
addtask(task);
notifyEvent("serverTaskId", new Integer(task.getId()));
return;
} else {
l.log("server <host> <port> <privkeyfile>");
l.log(" creates a server that sends all incoming data\n"+
" of its destination to host:port.");
notifyEvent("serverTaskId", new Integer(-1));
}
}
/**
* Run the server pointing at the host and port specified using the private i2p
* destination loaded from the given base64 stream
*
* Sets the event "serverTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error)
* Also sets the event "openServerResult" = "ok" or "error" (displaying "Ready!" on the logger after
* 'ok'). So, success = serverTaskId != -1 and openServerResult = ok.
*
* @param args {hostname, portNumber, privKeyBase64}
* @param l logger to receive events and output
*/
public void runTextServer(String args[], Logging l) {
if (args.length==3) {
InetAddress serverHost = null;
int portNum = -1;
try {
serverHost = InetAddress.getByName(args[0]);
} catch (UnknownHostException uhe) {
l.log("unknown host");
_log.error("Error resolving " + args[0], uhe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
try {
portNum = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error("Port specified is not valid: " + args[1], nfe);
notifyEvent("serverTaskId", new Integer(-1));
return;
}
I2PTunnelTask task;
task = new I2PTunnelServer(serverHost, portNum, args[2], l,
(EventDispatcher)this);
addtask(task);
notifyEvent("serverTaskId", new Integer(task.getId()));
} else {
l.log("textserver <host> <port> <privkey>");
l.log(" creates a server that sends all incoming data\n"+
" of its destination to host:port.");
notifyEvent("textserverTaskId", new Integer(-1));
}
}
/**
* Run the client on the given port number pointing at the specified destination
* (either the base64 of the destination or file:fileNameContainingDestination)
*
* Sets the event "clientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error)
* Also sets the event "openClientResult" = "error" or "ok" (before setting the value to "ok" it also
* adds "Ready! Port #" to the logger as well). In addition, it will also set "clientLocalPort" =
* Integer port number if the client is listening
*
* @param args {portNumber, destinationBase64 or "file:filename"}
* @param l logger to receive events and output
*/
public void runClient(String args[], Logging l) {
if (args.length==2) {
int port = -1;
try {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error("Port specified is not valid: " + args[0], nfe);
notifyEvent("clientTaskId", new Integer(-1));
return;
}
I2PTunnelTask task;
task = new I2PTunnelClient(port, args[1], l, ownDest,
(EventDispatcher)this);
addtask(task);
notifyEvent("clientTaskId", new Integer(task.getId()));
} else {
l.log("client <port> <pubkey>|file:<pubkeyfile>");
l.log(" creates a client that forwards port to the pubkey.\n"+
" use 0 as port to get a free port assigned.");
notifyEvent("clientTaskId", new Integer(-1));
}
}
/**
* Run an HTTP client on the given port number
*
* Sets the event "httpclientTaskId" = Integer(taskId) after the tunnel has been started (or -1 on error).
* Also sets "httpclientStatus" = "ok" or "error" after the client tunnel has started.
*
* @param args {portNumber and (optionally) proxy to be used for the WWW}
* @param l logger to receive events and output
*/
public void runHttpClient(String args[], Logging l) {
if (args.length >= 1 && args.length <= 2) {
int port = -1;
try {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error("Port specified is not valid: " + args[0], nfe);
notifyEvent("httpclientTaskId", new Integer(-1));
return;
}
String proxy = "squid.i2p";
if (args.length == 2) {
proxy = args[1];
}
I2PTunnelTask task;
task = new I2PTunnelHTTPClient(port, l, ownDest, proxy,
(EventDispatcher)this);
addtask(task);
notifyEvent("httpclientTaskId", new Integer(task.getId()));
} else {
l.log("httpclient <port> [<proxy>]");
l.log(" creates a client that distributes HTTP requests.");
l.log(" <proxy> (optional) indicates a proxy server to be used");
l.log(" when trying to access an address out of the .i2p domain");
l.log(" (the default proxy is squid.i2p).");
notifyEvent("httpclientTaskId", new Integer(-1));
}
}
/**
* Run an SOCKS tunnel on the given port number
*
* Sets the event "sockstunnelTaskId" = Integer(taskId) after the
* tunnel has been started (or -1 on error). Also sets
* "openSOCKSTunnelResult" = "ok" or "error" after the client tunnel has
* started.
*
* @param args {portNumber}
* @param l logger to receive events and output
*/
public void runSOCKSTunnel(String args[], Logging l) {
if (args.length >= 1 && args.length <= 2) {
int port = -1;
try {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
l.log("invalid port");
_log.error("Port specified is not valid: " + args[0], nfe);
notifyEvent("sockstunnelTaskId", new Integer(-1));
return;
}
I2PTunnelTask task;
task = new I2PSOCKSTunnel(port, l, ownDest, (EventDispatcher)this);
addtask(task);
notifyEvent("sockstunnelTaskId", new Integer(task.getId()));
} else {
l.log("sockstunnel <port>");
l.log(" creates a tunnel that distributes SOCKS requests.");
notifyEvent("sockstunnelTaskId", new Integer(-1));
}
}
/**
* Specify the i2cp host and port
*
* Sets the event "configResult" = "ok" or "error" after the configuration has been specified
*
* @param args {hostname, portNumber}
* @param l logger to receive events and output
*/
public void runConfig(String args[], Logging l) {
if (args.length==2) {
host=args[0];
listenHost=host;
port=args[1];
notifyEvent("configResult", "ok");
} else {
l.log("config <i2phost> <i2pport>");
l.log(" sets the connection to the i2p router.");
notifyEvent("configResult", "error");
}
}
/**
* Specify whether to use its own destination for each outgoing tunnel
*
* Sets the event "owndestResult" = "ok" or "error" after the configuration has been specified
*
* @param args {yes or no}
* @param l logger to receive events and output
*/
public void runOwnDest(String args[], Logging l) {
if (args.length==1 &&
(args[0].equalsIgnoreCase("yes")
|| args[0].equalsIgnoreCase("no"))) {
ownDest = args[0].equalsIgnoreCase("yes");
notifyEvent("owndestResult", "ok");
} else {
l.log("owndest yes|no");
l.log(" Specifies whether to use its own destination \n"+
" for each outgoing tunnel");
notifyEvent("owndestResult", "error");
}
}
/**
* Specify the hostname / IP address of the interface that the tunnels should bind to
*
* Sets the event "listen_onResult" = "ok" or "error" after the interface has been specified
*
* @param args {hostname}
* @param l logger to receive events and output
*/
public void runListenOn(String args[], Logging l) {
if (args.length==1) {
listenHost=args[0];
notifyEvent("listen_onResult", "ok");
} else {
l.log("listen_on <ip>");
l.log(" sets the interface to listen for the I2PClient.");
notifyEvent("listen_onResult", "ok");
}
}
/**
* Generate a new keypair
*
* Sets the event "genkeysResult" = "ok" or "error" after the generation is complete
*
* @param args {privateKeyFilename, publicKeyFilename} or {privateKeyFilename}
* @param l logger to receive events and output
*/
public void runGenKeys(String args[], Logging l) {
OutputStream pubdest=null;
if (args.length == 2) {
try {
pubdest=new FileOutputStream(args[1]);
} catch (IOException ioe) {
l.log("Error opening output stream");
_log.error("Error generating keys to out", ioe);
notifyEvent("genkeysResult", "error");
return;
}
} else if (args.length != 1) {
l.log("genkeys <privkeyfile> [<pubkeyfile>]");
l.log(" creates a new keypair and prints the public key.\n"+
" if pubkeyfile is given, saves the public key there."+
"\n"+
" if the privkeyfile already exists, just print/save"+
"the pubkey.");
notifyEvent("genkeysResult", "error");
}
try {
File privKeyFile = new File(args[0]);
if (privKeyFile.exists()) {
l.log("File already exists.");
showKey(new FileInputStream(privKeyFile), pubdest, l);
} else {
makeKey(new FileOutputStream(privKeyFile), pubdest, l);
}
notifyEvent("genkeysResult", "ok");
} catch (IOException ioe) {
l.log("Error generating keys - " + ioe.getMessage());
notifyEvent("genkeysResult", "error");
_log.error("Error generating keys", ioe);
}
}
/**
* Generate a new keypair
*
* Sets the event "privateKey" = base64 of the privateKey stream and
* sets the event "publicDestination" = base64 of the destination
*
* @param l logger to receive events and output
*/
public void runGenTextKeys(Logging l) {
ByteArrayOutputStream privkey = new ByteArrayOutputStream(512);
ByteArrayOutputStream pubkey = new ByteArrayOutputStream(512);
makeKey(privkey, pubkey, l);
l.log("Private key: "+Base64.encode(privkey.toByteArray()));
notifyEvent("privateKey", Base64.encode(privkey.toByteArray()));
notifyEvent("publicDestination", Base64.encode(pubkey.toByteArray()));
}
/**
* Exit the JVM if there are no more tasks left running. If there are tunnels
* running, it returns.
*
* Sets the event "quitResult" = "error" if there are tasks running (but if there
* aren't, well, there's no point in setting the quitResult to "ok", now is there?)
*
* @param l logger to receive events and output
*/
public void runQuit(Logging l) {
purgetasks(l);
synchronized (tasks) {
if (tasks.isEmpty()) {
System.exit(0);
}
}
l.log("There are running tasks. Try 'list'.");
notifyEvent("quitResult", "error");
}
/**
* Retrieve a list of currently running tasks
*
* Sets the event "listDone" = "done" after dumping the tasks to
* the logger
*
* @param l logger to receive events and output
*/
public void runList(Logging l) {
purgetasks(l);
synchronized (tasks) {
for (int i=0;i<tasks.size();i++) {
I2PTunnelTask t = (I2PTunnelTask) tasks.get(i);
l.log("[" + t.getId() + "] " + t.toString());
}
}
notifyEvent("listDone", "done");
}
/**
* Close the given task (or all tasks), optionally forcing them to die a hard
* death
*
* Sets the event "closeResult" = "ok" after the closing is complete
*
* @param args {jobNumber}, {"forced", jobNumber}, {"forced", "all"}
* @param l logger to receive events and output
*/
public void runClose(String args[], Logging l) {
if (args.length == 0 || args.length > 2) {
l.log("close [forced] <jobnumber>|all");
l.log(" stop running tasks. either only one or all.\n"+
" use 'forced' to also stop tasks with active connections.\n"+
" use the 'list' command to show the job numbers");
notifyEvent("closeResult", "error");
} else {
int argindex=0; // parse optional 'forced' keyword
boolean forced=false;
if (args[argindex].equalsIgnoreCase("forced")) {
forced=true;
argindex++;
}
if (args[argindex].equalsIgnoreCase("all")) {
List curTasks = null;
synchronized (tasks) {
curTasks = new LinkedList(tasks);
}
boolean error = false;
for (int i=0;i<curTasks.size();i++) {
I2PTunnelTask t = (I2PTunnelTask)curTasks.get(i);
if (!closetask(t, forced, l)) {
notifyEvent("closeResult", "error");
error = true;
} else if (!error) { // If there's an error, don't hide it
notifyEvent("closeResult", "ok");
}
}
} else {
try {
if (!closetask(Integer.parseInt(args[argindex]),forced,l)){
notifyEvent("closeResult", "error");
} else {
notifyEvent("closeResult", "ok");
}
} catch (NumberFormatException ex) {
l.log("Incorrect job number: " + args[argindex]);
notifyEvent("closeResult", "error");
}
}
}
}
/**
* Run all of the commands in the given file (one command per line)
*
* Sets the event "runResult" = "ok" or "error" after the closing is complete
*
* @param args {filename}
* @param l logger to receive events and output
*/
public void runRun(String args[], Logging l) {
if(args.length==1) {
try {
BufferedReader br =
new BufferedReader(new FileReader(args[0]));
String line;
while((line = br.readLine()) != null) {
runCommand(line,l);
}
br.close();
notifyEvent("runResult", "ok");
} catch (IOException ioe) {
l.log("IO error running the file");
_log.error("Error running the file", ioe);
notifyEvent("runResult", "error");
}
} else {
l.log("run <commandfile>");
l.log(" loads commandfile and runs each line in it. \n"+
" You can also give the filename on the commandline.");
notifyEvent("runResult", "error");
}
}
/**
* Perform a lookup of the name specified
*
* Sets the event "lookupResult" = base64 of the destination, or an error message
*
* @param args {name}
* @param l logger to receive events and output
*/
public void runLookup(String args[], Logging l) {
if (args.length != 1) {
l.log("lookup <name>");
l.log(" try to resolve the name into a destination key");
notifyEvent("lookupResult", "invalidUsage");
} else {
String target = args[0];
try {
Destination dest = destFromName(args[0]);
if (dest == null) {
l.log("Unknown host");
notifyEvent("lookupResult", "unkown host");
} else {
l.log(dest.toBase64());
notifyEvent("lookupResult", dest.toBase64());
}
} catch (DataFormatException dfe) {
l.log("Unknown or invalid host");
notifyEvent("lookupResult", "invalid host");
}
}
}
/**
* Start up a ping task with the specified args (currently supporting -ns, -h, -l)
*
* Sets the event "pingTaskId" = Integer of the taskId, or -1
*
* @param allargs arguments to pass to the I2Ping task
* @param l logger to receive events and output
*/
public void runPing(String allargs, Logging l) {
if(allargs.length() != 0) {
I2PTunnelTask task;
// pings always use the main destination
task = new I2Ping(allargs, l, false, (EventDispatcher)this);
addtask(task);
notifyEvent("pingTaskId", new Integer(task.getId()));
} else {
l.log("ping <opts> <dest>");
l.log("ping <opts> -h");
l.log("ping <opts> -l <destlistfile>");
l.log(" Tests communication with peers.\n"+
" opts can be -ns (nosync) or not.");
notifyEvent("pingTaskId", new Integer(-1));
}
}
/**
* Helper method to actually close the given task number (optionally forcing
* closure)
*
*/
private boolean closetask(int num, boolean forced, Logging l) {
boolean closed = false;
_log.debug("closetask(): looking for task " + num);
synchronized (tasks) {
for (Iterator it=tasks.iterator(); it.hasNext();) {
I2PTunnelTask t = (I2PTunnelTask) it.next();
int id = t.getId();
_log.debug("closetask(): parsing task " + id + " (" +
t.toString() + ")");
if (id == num) {
closed = closetask(t, forced, l);
break;
} else if (id > num) {
break;
}
}
}
return closed;
}
/**
* Helper method to actually close the given task number
* (optionally forcing closure)
*
*/
private boolean closetask(I2PTunnelTask t, boolean forced, Logging l) {
l.log("Closing task " + t.getId() + (forced ? " forced..." : "..."));
if (t.close(forced)) {
l.log("Task " + t.getId() + " closed.");
return true;
}
return false;
}
/**
* Helper task to remove closed / completed tasks.
*
*/
private void purgetasks(Logging l) {
synchronized (tasks) {
for (Iterator it=tasks.iterator(); it.hasNext();) {
I2PTunnelTask t = (I2PTunnelTask) it.next();
if (!t.isOpen()) {
_log.debug("Purging inactive tunnel: ["
+ t.getId() + "] "
+ t.toString());
it.remove();
}
}
}
}
/**
* Log the given message (using both the logging subsystem and standard output...)
*
*/
public void log(String s) {
System.out.println(s);
_log.info("Display: " + s);
}
/**
* Create a new destination, storing the destination and its private keys where
* instructed
*
* @param writeTo location to store the private keys
* @param pubDest location to store the destination
* @param l logger to send messages to
*/
public static void makeKey(OutputStream writeTo, OutputStream pubDest,
Logging l) {
try {
l.log("Generating new keys...");
ByteArrayOutputStream priv = new ByteArrayOutputStream(),
pub = new ByteArrayOutputStream();
I2PClient client = I2PClientFactory.createClient();
Destination d = client.createDestination(writeTo);
l.log("Secret key saved.");
l.log("Public key: "+d.toBase64());
writeTo.flush();
writeTo.close();
writePubKey(d, pubDest, l);
} catch (I2PException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
/**
* Read in the given destination, display it, and write it to the given location
*
* @param readFrom stream to read the destination from
* @param pubDest stream to write the destination to
* @param l logger to send messages to
*/
public static void showKey(InputStream readFrom, OutputStream pubDest,
Logging l) {
try {
I2PClient client = I2PClientFactory.createClient();
Destination d = new Destination();
d.readBytes(readFrom);
l.log("Public key: "+d.toBase64());
readFrom.close();
writePubKey(d, pubDest, l);
} catch (I2PException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
/**
* Write out the destination to the stream
*
* @param d Destination to write
* @param o stream to write the destination to
* @param l logger to send messages to
*/
private static void writePubKey(Destination d, OutputStream o, Logging l)
throws I2PException, IOException {
if (o==null) return;
d.writeBytes(o);
l.log("Public key saved.");
}
/**
* Generates a Destination from a name. Now only supports base64
* names - may support naming servers later. "file:<filename>" is
* also supported, where filename is a file that either contains a
* binary Destination structure or the Base64 encoding of that
* structure.
*/
public static Destination destFromName(String name)
throws DataFormatException {
if ( (name == null) || (name.trim().length() <= 0) )
throw new DataFormatException("Empty destination provided");
if (name.startsWith("file:")) {
Destination result=new Destination();
byte content[] = null;
FileInputStream in = null;
try {
in = new FileInputStream(name.substring("file:".length()));
byte buf[] = new byte[1024];
int read = DataHelper.read(in, buf);
content = new byte[read];
System.arraycopy(buf, 0, content, 0, read);
} catch (IOException ioe) {
System.out.println(ioe.getMessage());
return null;
} finally {
if (in != null) try { in.close(); } catch (IOException io) {}
}
try {
result.fromByteArray(content);
return result;
} catch (Exception ex) {
if (_log.shouldLog(Log.INFO))
_log.info("File is not a binary destination - trying base64");
try {
byte decoded[] = Base64.decode(new String(content));
result.fromByteArray(decoded);
return result;
} catch (DataFormatException dfe) {
if (_log.shouldLog(Log.WARN))
_log.warn("File is not a base64 destination either - failing!");
return null;
}
}
} else {
// ask naming service
NamingService inst = NamingService.getInstance();
return inst.lookup(name);
}
}
public void addConnectionEventListener(ConnectionEventListener lsnr) {
if (lsnr == null) return;
synchronized (listeners) {
listeners.add(lsnr);
}
}
public void removeConnectionEventListener(ConnectionEventListener lsnr) {
if (lsnr == null) return;
synchronized (listeners) {
listeners.remove(lsnr);
}
}
/**
* Call this whenever we lose touch with the router involuntarily (aka the router
* is off / crashed / etc)
*
*/
void routerDisconnected() {
_log.error("Router disconnected - firing notification events");
synchronized (listeners) {
for (Iterator iter = listeners.iterator(); iter.hasNext();) {
ConnectionEventListener lsnr = (ConnectionEventListener)iter.next();
if (lsnr != null)
lsnr.routerDisconnected();
}
}
}
/**
* Callback routine to find out
*/
public interface ConnectionEventListener {
public void routerDisconnected();
}
/* Required by the EventDispatcher interface */
public EventDispatcher getEventDispatcher() { return _event; }
public void attachEventDispatcher(EventDispatcher e) { _event.attachEventDispatcher(e.getEventDispatcher()); }
public void detachEventDispatcher(EventDispatcher e) { _event.detachEventDispatcher(e.getEventDispatcher()); }
public void notifyEvent(String e, Object a) { _event.notifyEvent(e,a); }
public Object getEventValue(String n) { return _event.getEventValue(n); }
public Set getEvents() { return _event.getEvents(); }
public void ignoreEvents() { _event.ignoreEvents(); }
public void unIgnoreEvents() { _event.unIgnoreEvents(); }
public Object waitEventValue(String n) { return _event.waitEventValue(n); }
}

View File

@ -0,0 +1,61 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.net.Socket;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
public class I2PTunnelClient extends I2PTunnelClientBase {
private static final Log _log = new Log(I2PTunnelClient.class);
protected Destination dest;
public I2PTunnelClient(int localPort, String destination,
Logging l, boolean ownDest,
EventDispatcher notifyThis) {
super(localPort, ownDest, l, notifyThis, "SynSender");
if (waitEventValue("openBaseClientResult").equals("error")) {
notifyEvent("openClientResult", "error");
return;
}
try {
dest=I2PTunnel.destFromName(destination);
if (dest == null) {
l.log("Could not resolve " + destination + ".");
return;
}
} catch (DataFormatException e) {
l.log("Bad format in destination \"" + destination + "\".");
notifyEvent("openClientResult", "error");
return;
}
setName(getLocalPort() + " -> " + destination);
startRunning();
notifyEvent("openClientResult", "ok");
}
protected void clientConnectionRun(Socket s) {
try {
I2PSocket i2ps = createI2PSocket(dest);
new I2PTunnelRunner(s, i2ps, sockLock, null);
} catch (I2PException ex) {
_log.info("Error connecting", ex);
l.log("Unable to reach peer");
// s has been initialized before the try block...
closeSocket(s);
}
}
}

View File

@ -0,0 +1,292 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.Destination;
import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
public abstract class I2PTunnelClientBase extends I2PTunnelTask
implements Runnable {
private static final Log _log = new Log(I2PTunnelClientBase.class);
protected Logging l;
private static final long DEFAULT_CONNECT_TIMEOUT = 60*1000;
protected Object sockLock = new Object(); // Guards sockMgr and mySockets
private I2PSocketManager sockMgr;
private List mySockets = new ArrayList();
protected Destination dest = null;
private int localPort;
private boolean listenerReady = false;
private ServerSocket ss;
private Object startLock = new Object();
private boolean startRunning = false;
private Object closeLock = new Object();
private byte[] pubkey;
private String handlerName;
//public I2PTunnelClientBase(int localPort, boolean ownDest,
// Logging l) {
// I2PTunnelClientBase(localPort, ownDest, l, (EventDispatcher)null);
//}
public I2PTunnelClientBase(int localPort, boolean ownDest,
Logging l, EventDispatcher notifyThis,
String handlerName) {
super(localPort+" (uninitialized)", notifyThis);
this.localPort=localPort;
this.l = l;
this.handlerName=handlerName;
synchronized(sockLock) {
if (ownDest) {
sockMgr=buildSocketManager();
} else {
sockMgr=getSocketManager();
}
}
if (sockMgr == null) throw new NullPointerException();
l.log("I2P session created");
Thread t = new Thread(this);
t.setName("Client");
listenerReady=false;
t.start();
open=true;
synchronized (this) {
while (!listenerReady) {
try {
wait();
}
catch (InterruptedException e) {
// ignore
}
}
}
if (open && listenerReady) {
l.log("Ready! Port " + getLocalPort());
notifyEvent("openBaseClientResult", "ok");
} else {
l.log("Error!");
notifyEvent("openBaseClientResult", "error");
}
}
private static I2PSocketManager socketManager;
protected static synchronized I2PSocketManager getSocketManager() {
if (socketManager == null) {
socketManager = buildSocketManager();
}
return socketManager;
}
protected static I2PSocketManager buildSocketManager() {
Properties props = new Properties();
props.putAll(System.getProperties());
return I2PSocketManagerFactory.createManager
(I2PTunnel.host, Integer.parseInt(I2PTunnel.port), props);
}
public final int getLocalPort() {
return localPort;
}
protected final InetAddress getListenHost(Logging l) {
try {
return InetAddress.getByName(I2PTunnel.listenHost);
} catch (UnknownHostException uhe) {
l.log("Could not find listen host to bind to [" +
I2PTunnel.host + "]");
_log.error("Error finding host to bind", uhe);
notifyEvent("openBaseClientResult", "error");
return null;
}
}
/**
* Actually start working on incoming connections. *Must* be
* called by derived classes after initialization.
*
*/
public final void startRunning() {
synchronized (startLock) {
startRunning = true;
startLock.notify();
}
}
/**
* create the default options (using the default timeout, etc)
*
*/
private I2PSocketOptions getDefaultOptions() {
I2PSocketOptions opts = new I2PSocketOptions();
opts.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);
return opts;
}
/**
* Create a new I2PSocket towards to the specified destination,
* adding it to the list of connections actually managed by this
* tunnel.
*
* @param dest The destination to connect to
* @return a new I2PSocket
*/
public I2PSocket createI2PSocket(Destination dest) throws I2PException {
return createI2PSocket(dest, getDefaultOptions());
}
/**
* Create a new I2PSocket towards to the specified destination,
* adding it to the list of connections actually managed by this
* tunnel.
*
* @param dest The destination to connect to
* @param opt Option to be used to open when opening the socket
* @return a new I2PSocket
*/
public I2PSocket createI2PSocket(Destination dest, I2PSocketOptions opt) throws I2PException {
I2PSocket i2ps;
synchronized (sockLock) {
i2ps = sockMgr.connect(dest, opt);
mySockets.add(i2ps);
}
return i2ps;
}
public final void run() {
try {
InetAddress addr = getListenHost(l);
if (addr == null) return;
ss = new ServerSocket(localPort, 0, addr);
// If a free port was requested, find out what we got
if (localPort == 0) {
localPort = ss.getLocalPort();
}
notifyEvent("clientLocalPort", new Integer(ss.getLocalPort()));
l.log("Listening for clients on port " + localPort +
" of " + I2PTunnel.listenHost);
// Notify constructor that port is ready
synchronized(this) {
listenerReady = true;
notify();
}
// Wait until we are authorized to process data
synchronized (startLock) {
while (!startRunning) {
try {
startLock.wait();
} catch (InterruptedException ie) {}
}
}
while (true) {
Socket s = ss.accept();
manageConnection(s);
}
} catch (IOException ex) {
_log.error("Error listening for connections", ex);
notifyEvent("openBaseClientResult", "error");
}
}
/**
* Manage the connection just opened on the specified socket
*
* @param s Socket to take care of
*/
protected void manageConnection(Socket s) {
new ClientConnectionRunner(s, handlerName);
}
public boolean close(boolean forced) {
if (!open) return true;
// FIXME: here we might have to wait quite a long time if
// there is a connection attempt atm. But without waiting we
// might risk to create an orphan socket. Would be better
// to return with an error in that situation quickly.
synchronized(sockLock) {
mySockets.retainAll(sockMgr.listSockets());
if (!forced && mySockets.size() != 0) {
l.log("There are still active connections!");
_log.debug("can't close: there are still active connections!");
for (Iterator it = mySockets.iterator(); it.hasNext();) {
l.log("->"+it.next());
}
return false;
}
l.log("Closing client "+toString());
try {
if (ss != null) ss.close();
} catch (IOException ex) {
ex.printStackTrace();
return false;
}
l.log("Client closed.");
open=false;
return true;
}
}
public static void closeSocket(Socket s) {
try {
s.close();
} catch (IOException ex) {
_log.error("Could not close socket", ex);
}
}
public class ClientConnectionRunner extends Thread {
private Socket s;
public ClientConnectionRunner(Socket s, String name) {
this.s=s;
setName(name);
start();
}
public void run() {
clientConnectionRun(s);
}
}
/**
* Manage a connection in a separate thread. This only works if
* you do not override manageConnection()
*/
protected abstract void clientConnectionRun(Socket s);
}

View File

@ -0,0 +1,48 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* AWT gui since kaffe doesn't support swing yet
*/
public class I2PTunnelGUI extends Frame implements ActionListener, Logging {
TextField input;
TextArea log;
I2PTunnel t;
public I2PTunnelGUI(I2PTunnel t) {
super("I2PTunnel control panel");
this.t=t;
setLayout(new BorderLayout());
add("South", input=new TextField());
input.addActionListener(this);
Font font = new Font("Monospaced",Font.PLAIN,12);
add("Center",log=new TextArea("",20,80,TextArea.SCROLLBARS_VERTICAL_ONLY));
log.setFont(font);
log.setEditable(false);
log("enter 'help' for help.");
pack();
show();
}
public void log(String s) {
log.append(s+"\n");
}
public void actionPerformed(ActionEvent evt) {
log("I2PTunnel>"+input.getText());
t.runCommand(input.getText(), this);
log("---");
input.setText("");
}
}

View File

@ -0,0 +1,334 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Date;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
public class I2PTunnelHTTPClient extends I2PTunnelClientBase
implements Runnable {
private static final Log _log =
new Log(I2PTunnelHTTPClient.class);
private String wwwProxy;
private final static byte[] ERR_REQUEST_DENIED = "HTTP/1.1 404 Not Found\r\nContent-Type: text/html; charset=iso-8859-1\r\nCache-control: no-cache\r\n\r\n<html><body><H1>I2P ERROR: REQUEST DENIED</H1>You attempted to connect to a non-I2P website or location.<BR>".getBytes();
private final static byte[] ERR_DESTINATION_UNKNOWN = "HTTP/1.1 404 Not Found\r\nContent-Type: text/html; charset=iso-8859-1\r\nCache-control: no-cache\r\n\r\n<html><body><H1>I2P ERROR: NOT FOUND</H1>That Desitination was not found. Perhaps you pasted in the wrong BASE64 I2P Destination or the link you are following is bad. The host (or the WWW proxy, if you're using one) could also be temporarily offline. Could not find the following Destination:<BR><BR>".getBytes();
private final static byte[] ERR_TIMEOUT = "HTTP/1.1 404 Not Found\r\nContent-Type: text/html; charset=iso-8859-1\r\nCache-control: no-cache\r\n\r\n<html><body><H1>I2P ERROR: TIMEOUT</H1>That Desitination was reachable, but timed out getting a response. This may be a temporary error, so you should simply try to refresh, though if the problem persists, the remote destination may have issues. Could not get a response from the following Destination:<BR><BR>".getBytes();
//public I2PTunnelHTTPClient(int localPort, Logging l,
// boolean ownDest,
// String wwwProxy) {
// I2PTunnelHTTPClient(localPort, l, ownDest, wwwProxy,
// (EventDispatcher)null);
//}
public I2PTunnelHTTPClient(int localPort, Logging l,
boolean ownDest,
String wwwProxy, EventDispatcher notifyThis) {
super(localPort, ownDest, l, notifyThis, "HTTPHandler");
if (waitEventValue("openBaseClientResult").equals("error")) {
notifyEvent("openHTTPClientResult", "error");
return;
}
this.wwwProxy = wwwProxy;
setName(getLocalPort()
+ " -> HTTPClient [WWW outproxy: " + this.wwwProxy + "]");
startRunning();
notifyEvent("openHTTPClientResult", "ok");
}
protected void clientConnectionRun(Socket s) {
OutputStream out = null;
String targetRequest = null;
boolean usingWWWProxy = false;
InactivityTimeoutThread timeoutThread = null;
try {
out = s.getOutputStream();
BufferedReader br = new BufferedReader
(new InputStreamReader(s.getInputStream(),
"ISO-8859-1"));
String line, method=null, protocol=null, host=null, destination=null;
StringBuffer newRequest=new StringBuffer();
while ((line=br.readLine()) != null) {
if (method==null) { // first line (GET /base64/realaddr)
int pos=line.indexOf(" ");
if (pos == -1) break;
method=line.substring(0, pos);
String request = line.substring(pos+1);
if (request.startsWith("/") &&
System.getProperty("i2ptunnel.noproxy") != null) {
request="http://i2p"+request;
}
pos = request.indexOf("//");
if (pos == -1) {
method=null;
break;
}
protocol=request.substring(0,pos+2);
request=request.substring(pos+2);
targetRequest = request;
pos = request.indexOf("/");
if (pos == -1) {
method=null;
break;
}
host=request.substring(0,pos);
// Quick hack for foo.bar.i2p
if (host.toLowerCase().endsWith( ".i2p")) {
destination=host;
host=getHostName(destination);
line=method+" "+request.substring(pos);
} else if (host.indexOf(".") != -1) {
// The request must be forwarded to a WWW proxy
destination = wwwProxy;
usingWWWProxy = true;
} else {
request=request.substring(pos+1);
pos = request.indexOf("/");
destination=request.substring(0,pos);
line=method+" "+request.substring(pos);
}
boolean isValid = usingWWWProxy ||
isSupportedAddress(host, protocol);
if (!isValid) {
if (_log.shouldLog(Log.INFO))
_log.info("notValid(" + host + ")");
method=null;
destination=null;
break;
} else if (!usingWWWProxy) {
if (_log.shouldLog(Log.INFO))
_log.info("host=getHostName(" + destination + ")");
host=getHostName(destination); // hide original host
}
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("METHOD:"+method+":");
_log.debug("PROTOC:"+protocol+":");
_log.debug("HOST :"+host+":");
_log.debug("DEST :"+destination+":");
}
} else if (line.startsWith("Host: ") && !usingWWWProxy) {
line="Host: "+host;
if (_log.shouldLog(Log.INFO))
_log.info("Setting host = " + host);
}
newRequest.append(line).append("\r\n"); // HTTP spec
if (line.length()==0) break;
}
while (br.ready()) { // empty the buffer (POST requests)
int i=br.read();
if (i != -1) {
newRequest.append((char)i);
}
}
if (method==null || destination==null) {
l.log("No HTTP method found in the request.");
if (out != null) {
out.write(ERR_REQUEST_DENIED);
out.write("<p /><i>Generated on: ".getBytes());
out.write(new Date().toString().getBytes());
out.write("</i></body></html>\n".getBytes());
out.flush();
}
s.close();
return;
}
Destination dest=I2PTunnel.destFromName(destination);
if (dest == null) {
l.log("Could not resolve "+destination+".");
writeErrorMessage(ERR_DESTINATION_UNKNOWN, out, targetRequest,
usingWWWProxy, destination);
s.close();
return;
}
String remoteID;
I2PSocket i2ps = createI2PSocket(dest);
byte[] data=newRequest.toString().getBytes("ISO-8859-1");
I2PTunnelRunner runner = new I2PTunnelRunner(s, i2ps, sockLock, data);
timeoutThread = new InactivityTimeoutThread(runner, out, targetRequest, usingWWWProxy, s);
timeoutThread.start();
} catch (IOException ex) {
if (timeoutThread != null) timeoutThread.disable();
_log.error("Error sending syn", ex);
handleHTTPClientException(ex, out, targetRequest,
usingWWWProxy, wwwProxy);
closeSocket(s);
} catch (I2PException ex) {
if (timeoutThread != null) timeoutThread.disable();
_log.info("Error sending syn", ex);
l.log("Unable to reach peer");
handleHTTPClientException(ex, out, targetRequest,
usingWWWProxy, wwwProxy);
closeSocket(s);
}
}
private static final long INACTIVITY_TIMEOUT = 120*1000;
private class InactivityTimeoutThread extends I2PThread {
private Socket s;
private I2PTunnelRunner _runner;
private OutputStream _out;
private String _targetRequest;
private boolean _useWWWProxy;
private boolean _disabled;
private Object _disableLock = new Object();
public InactivityTimeoutThread(I2PTunnelRunner runner, OutputStream out, String targetRequest, boolean useWWWProxy, Socket s) {
this.s=s;
_runner = runner;
_out = out;
_targetRequest = targetRequest;
_useWWWProxy = useWWWProxy;
_disabled = false;
}
public void disable() {
_disabled = true;
synchronized (_disableLock) { _disableLock.notifyAll(); }
}
public void run() {
while (!_disabled) {
if (_runner.isFinished()) {
if (_log.shouldLog(Log.INFO))
_log.info("HTTP client request completed prior to timeout");
return;
}
if (_runner.getLastActivityOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
if (_runner.getStartedOn() < Clock.getInstance().now() - INACTIVITY_TIMEOUT) {
if (_log.shouldLog(Log.WARN))
_log.warn("HTTP client request timed out (lastActivity: " + new Date(_runner.getLastActivityOn()) + ", startedOn: " + new Date(_runner.getLastActivityOn()) + ")");
timeout();
return;
} else {
// runner hasn't been going to long enough
}
} else {
// there has been activity in the period
}
synchronized (_disableLock) {
try {
_disableLock.wait(INACTIVITY_TIMEOUT);
} catch (InterruptedException ie) {}
}
}
}
private void timeout() {
_log.info("Inactivity timeout reached");
l.log("Inactivity timeout reached");
if (_out != null) {
try {
if (_runner.getLastActivityOn() > 0) {
// some data has been sent, so don't 404 it
} else {
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest,
_useWWWProxy, wwwProxy);
}
} catch (IOException ioe) {
_log.warn("Error writing out the 'timeout' message", ioe);
}
} else {
_log.warn("Client disconnected before we could say we timed out");
}
closeSocket(s);
}
}
private final static String getHostName(String host) {
try {
Destination dest=I2PTunnel.destFromName(host);
if (dest == null) return "i2p";
return dest.toBase64();
} catch (DataFormatException dfe) {
return "i2p";
}
}
private static void writeErrorMessage(byte[] errMessage, OutputStream out,
String targetRequest,
boolean usingWWWProxy,
String wwwProxy)
throws IOException {
if (out != null) {
out.write(errMessage);
if (targetRequest != null) {
out.write(targetRequest.getBytes());
if (usingWWWProxy)
out.write(("<br>WWW proxy: " +
wwwProxy).getBytes());
}
out.write("<p /><i>Generated on: ".getBytes());
out.write(new Date().toString().getBytes());
out.write("</i></body></html>\n".getBytes());
out.flush();
}
}
private static void handleHTTPClientException (Exception ex, OutputStream out,
String targetRequest,
boolean usingWWWProxy,
String wwwProxy) {
if (out != null) {
try {
writeErrorMessage(ERR_DESTINATION_UNKNOWN, out, targetRequest,
usingWWWProxy, wwwProxy);
} catch (IOException ioe) {
_log.warn("Error writing out the 'destination was unknown' "+
"message", ioe);
}
} else {
_log.warn("Client disconnected before we could say that destination "+
"was unknown", ex);
}
}
private final static String SUPPORTED_HOSTS[] = { "i2p", "www.i2p.com",
"i2p." };
private boolean isSupportedAddress(String host, String protocol) {
if ( (host == null) || (protocol == null) ) return false;
boolean found = false;
String lcHost = host.toLowerCase();
for (int i = 0; i < SUPPORTED_HOSTS.length; i++) {
if (SUPPORTED_HOSTS[i].equals(lcHost)) {
found = true;
break;
}
}
if (!found) {
try {
Destination d = I2PTunnel.destFromName(host);
if (d == null) return false;
} catch (DataFormatException dfe) {}
}
return protocol.equalsIgnoreCase("http://");
}
}

View File

@ -0,0 +1,184 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;
import net.i2p.client.I2PSession;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.util.Log;
import net.i2p.util.Clock;
public class I2PTunnelRunner extends Thread {
private final static Log _log = new Log(I2PTunnelRunner.class);
/**
* max bytes streamed in a packet - smaller ones might be filled
* up to this size. Larger ones are not split (at least not on
* Sun's impl of BufferedOutputStream), but that is the streaming
* api's job...
*/
static int MAX_PACKET_SIZE = 1024*32;
static final int NETWORK_BUFFER_SIZE = MAX_PACKET_SIZE;
private Socket s;
private I2PSocket i2ps;
Object slock, finishLock = new Object();
boolean finished=false;
HashMap ostreams, sockets;
I2PSession session;
byte[] initialData;
/** when the last data was sent/received (or -1 if never) */
private long lastActivityOn;
/** when the runner started up */
private long startedOn;
public I2PTunnelRunner(Socket s, I2PSocket i2ps, Object slock,
byte[] initialData) {
this.s=s;
this.i2ps=i2ps;
this.slock=slock;
this.initialData = initialData;
lastActivityOn = -1;
startedOn = -1;
_log.info("I2PTunnelRunner started");
setName("I2PTunnelRunner");
start();
}
/**
* have we closed at least one (if not both) of the streams
* [aka we're done running the streams]?
*
*/
public boolean isFinished() { return finished; }
/**
* When was the last data for this runner sent or received? (-1 if no data
* has been transferred yet)
*
*/
public long getLastActivityOn() { return lastActivityOn; }
private void updateActivity() { lastActivityOn = Clock.getInstance().now(); }
/**
* When this runner started up transferring data
*
*/
public long getStartedOn() { return startedOn; }
public void run() {
startedOn = Clock.getInstance().now();
try {
InputStream in = s.getInputStream();
OutputStream out = new BufferedOutputStream(s.getOutputStream(),
NETWORK_BUFFER_SIZE);
InputStream i2pin = i2ps.getInputStream();
OutputStream i2pout = new BufferedOutputStream
(i2ps.getOutputStream(), MAX_PACKET_SIZE);
if (initialData != null) {
synchronized(slock) {
i2pout.write(initialData);
i2pout.flush();
}
}
Thread t1 = new StreamForwarder(in, i2pout);
Thread t2 = new StreamForwarder(i2pin, out);
synchronized(finishLock) {
while (!finished) {
finishLock.wait();
}
}
// now one connection is dead - kill the other as well.
s.close();
s = null;
i2ps.close();
i2ps = null;
t1.join();
t2.join();
} catch (InterruptedException ex) {
_log.error("Interrupted", ex);
} catch (IOException ex) {
ex.printStackTrace();
_log.error("Error forwarding", ex);
} finally {
try {
if (s != null) s.close();
if (i2ps != null) i2ps.close();
} catch (IOException ex) {
ex.printStackTrace();
_log.error("Could not close socket", ex);
}
}
}
private class StreamForwarder extends Thread {
InputStream in;
OutputStream out;
private StreamForwarder(InputStream in, OutputStream out) {
this.in=in;
this.out=out;
setName("StreamForwarder");
start();
}
public void run() {
byte[] buffer = new byte[NETWORK_BUFFER_SIZE];
try {
int len;
while ((len=in.read(buffer)) != -1) {
out.write(buffer, 0, len);
if (len > 0)
updateActivity();
if (in.available()==0) {
try {
Thread.sleep(I2PTunnel.PACKET_DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (in.available()==0) {
out.flush(); // make sure the data get though
}
}
} catch (SocketException ex) {
// this *will* occur when the other threads closes the socket
synchronized(finishLock) {
if (!finished)
_log.error("Error reading and writing", ex);
else
_log.warn("You may ignore this", ex);
}
} catch (IOException ex) {
if (!finished)
_log.error("Error forwarding", ex);
else
_log.warn("You may ignore this", ex);
} finally {
try {
out.close();
in.close();
} catch (IOException ex) {
_log.error("Error closing streams", ex);
}
synchronized(finishLock) {
finished=true;
finishLock.notifyAll();
// the main thread will close sockets etc. now
}
}
}
}
}

View File

@ -0,0 +1,138 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.*;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.data.Base64;
import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
public class I2PTunnelServer extends I2PTunnelTask
implements Runnable {
private final static Log _log = new Log(I2PTunnelServer.class);
private I2PSocketManager sockMgr;
private I2PServerSocket i2pss;
private Object lock = new Object(), slock = new Object();
private InetAddress remoteHost;
private int remotePort;
private Logging l;
public I2PTunnelServer(InetAddress host, int port,
String privData, Logging l,
EventDispatcher notifyThis) {
super(host+":"+port+" <- "+privData, notifyThis);
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(privData));
init(host, port, bais, privData, l);
}
public I2PTunnelServer(InetAddress host, int port,
File privkey, String privkeyname,
Logging l, EventDispatcher notifyThis) {
super(host+":"+port+" <- "+privkeyname, notifyThis);
try {
init(host, port, new FileInputStream(privkey), privkeyname, l);
} catch (IOException ioe) {
_log.error("Error starting server", ioe);
notifyEvent("openServerResult", "error");
}
}
public I2PTunnelServer(InetAddress host, int port,
InputStream privData, String privkeyname,
Logging l, EventDispatcher notifyThis) {
super(host+":"+port+" <- "+privkeyname, notifyThis);
init(host, port, privData, privkeyname, l);
}
private void init(InetAddress host, int port, InputStream privData,
String privkeyname, Logging l) {
this.l=l;
this.remoteHost=host;
this.remotePort=port;
I2PClient client = I2PClientFactory.createClient();
Properties props = new Properties();
props.putAll(System.getProperties());
synchronized(slock) {
sockMgr = I2PSocketManagerFactory.createManager
(privData, I2PTunnel.host,
Integer.parseInt(I2PTunnel.port), props);
}
l.log("Ready!");
notifyEvent("openServerResult", "ok");
open=true;
Thread t = new Thread(this);
t.setName("Server");
t.start();
}
public boolean close(boolean forced) {
if (!open) return true;
synchronized(lock) {
if (!forced && sockMgr.listSockets().size() != 0) {
l.log("There are still active connections!");
for (Iterator it = sockMgr.listSockets().iterator();
it.hasNext();) {
l.log("->"+it.next());
}
return false;
}
l.log("Shutting down server "+toString());
try {
if (i2pss != null) i2pss.close();
sockMgr.getSession().destroySession();
} catch (I2PException ex) {
_log.error("Error destroying the session", ex);
System.exit(1);
}
l.log("Server shut down.");
open=false;
return true;
}
}
public void run() {
try {
I2PServerSocket i2pss = sockMgr.getServerSocket();
while (true) {
I2PSocket i2ps = i2pss.accept();
//local is fast, so synchronously. Does not need that many
//threads.
try {
Socket s = new Socket(remoteHost, remotePort);
new I2PTunnelRunner(s, i2ps, slock, null);
} catch (SocketException ex) {
i2ps.close();
}
}
} catch (I2PException ex) {
_log.error("Error while waiting for I2PConnections", ex);
} catch (IOException ex) {
_log.error("Error while waiting for I2PConnections", ex);
}
}
}

View File

@ -0,0 +1,75 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.util.Set;
import net.i2p.client.I2PSession;
import net.i2p.util.EventDispatcher;
import net.i2p.util.EventDispatcherImpl;
/**
* Either a Server or a Client.
*/
public abstract class I2PTunnelTask implements EventDispatcher {
private final EventDispatcherImpl _event = new EventDispatcherImpl();
private int id;
private String name;
protected boolean open;
private I2PTunnel tunnel;
//protected I2PTunnelTask(String name) {
// I2PTunnelTask(name, (EventDispatcher)null);
//}
protected I2PTunnelTask(String name, EventDispatcher notifyThis) {
attachEventDispatcher(notifyThis);
this.name=name;
this.id = -1;
}
/** for apps that use multiple I2PTunnel instances */
public void setTunnel(I2PTunnel pTunnel) { tunnel = pTunnel; }
public int getId() {
return this.id;
}
public boolean isOpen() {return open;}
public void setId(int id) {
this.id = id;
}
protected void setName(String name) {
this.name=name;
}
protected void routerDisconnected() { tunnel.routerDisconnected(); }
public abstract boolean close(boolean forced);
public void disconnected(I2PSession session) { routerDisconnected(); }
public void errorOccurred(I2PSession session, String message,
Throwable error) {}
public void reportAbuse(I2PSession session, int severity) {}
public String toString() {
return name;
}
/* Required by the EventDispatcher interface */
public EventDispatcher getEventDispatcher() { return _event; }
public void attachEventDispatcher(EventDispatcher e) { _event.attachEventDispatcher(e.getEventDispatcher()); }
public void detachEventDispatcher(EventDispatcher e) { _event.detachEventDispatcher(e.getEventDispatcher()); }
public void notifyEvent(String e, Object a) { _event.notifyEvent(e,a); }
public Object getEventValue(String n) { return _event.getEventValue(n); }
public Set getEvents() { return _event.getEvents(); }
public void ignoreEvents() { _event.ignoreEvents(); }
public void unIgnoreEvents() { _event.unIgnoreEvents(); }
public Object waitEventValue(String n) { return _event.waitEventValue(n); }
}

View File

@ -0,0 +1,228 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.data.Destination;
import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
public class I2Ping extends I2PTunnelTask implements Runnable {
private final static Log _log = new Log(I2Ping.class);
private static final int PING_COUNT = 3;
private static final int CPING_COUNT = 5;
private static final int PING_TIMEOUT= 5000;
private static final long PING_DISTANCE=1000;
private int MAX_SIMUL_PINGS=10; // not really final...
private boolean countPing=false;
private I2PSocketManager sockMgr;
private Logging l;
private boolean finished=false;
private String command;
private long timeout = PING_TIMEOUT;
private Object simulLock = new Object();
private int simulPings = 0;
private long lastPingTime = 0;
private Object lock = new Object(), slock = new Object();
//public I2Ping(String cmd, Logging l,
// boolean ownDest) {
// I2Ping(cmd, l, (EventDispatcher)null);
//}
public I2Ping(String cmd, Logging l,
boolean ownDest, EventDispatcher notifyThis) {
super("I2Ping ["+cmd+"]", notifyThis);
this.l=l;
command=cmd;
synchronized(slock) {
if (ownDest) {
sockMgr = I2PTunnelClient.buildSocketManager();
} else {
sockMgr = I2PTunnelClient.getSocketManager();
}
}
Thread t = new Thread(this);
t.setName("Client");
t.start();
open=true;
}
public void run() {
l.log("*** I2Ping results:");
try {
runCommand(command);
} catch (InterruptedException ex) {
l.log("*** Interrupted");
_log.error("Pinger interrupted",ex);
} catch (IOException ex) {
_log.error("Pinger exception",ex);
}
l.log("*** Finished.");
synchronized(lock) {
finished=true;
}
close(false);
}
public void runCommand(String cmd) throws InterruptedException,
IOException {
if (cmd.startsWith("-t ")) { // timeout
cmd = cmd.substring(3);
int pos = cmd.indexOf(" ");
if (pos == -1) {
l.log("Syntax error");
return;
} else {
timeout = Long.parseLong(cmd.substring(0, pos));
cmd=cmd.substring(pos+1);
}
}
if (cmd.startsWith("-m ")) { // max simultaneous pings
cmd = cmd.substring(3);
int pos = cmd.indexOf(" ");
if (pos == -1) {
l.log("Syntax error");
return;
} else {
MAX_SIMUL_PINGS = Integer.parseInt(cmd.substring(0, pos));
cmd=cmd.substring(pos+1);
}
}
if (cmd.startsWith("-c ")) { // "count" ping
countPing=true;
cmd=cmd.substring(3);
}
if (cmd.equals("-h")) { // ping all hosts
cmd="-l hosts.txt";
}
if (cmd.startsWith("-l ")) { // ping a list of hosts
BufferedReader br = new BufferedReader
(new FileReader(cmd.substring(3)));
String line;
List pingHandlers = new ArrayList();
while ((line = br.readLine()) != null) {
if (line.startsWith("#")) continue; // comments
if (line.startsWith(";")) continue;
if (line.startsWith("!")) continue;
if (line.indexOf("=") != -1) { // maybe file is hosts.txt?
line=line.substring(0,line.indexOf("="));
}
pingHandlers.add(new PingHandler(line));
}
br.close();
for (Iterator it= pingHandlers.iterator(); it.hasNext(); ) {
Thread t = (Thread) it.next();
t.join();
}
} else {
Thread t = new PingHandler(cmd);
t.join();
}
}
public boolean close(boolean forced) {
if (!open) return true;
synchronized(lock) {
if (!forced && !finished) {
l.log("There are still pings running!");
return false;
}
l.log("Closing pinger "+toString());
l.log("Pinger closed.");
open=false;
return true;
}
}
public boolean ping(Destination dest) throws I2PException {
try {
synchronized(simulLock) {
while (simulPings >= MAX_SIMUL_PINGS) {
simulLock.wait();
}
simulPings++;
while (lastPingTime + PING_DISTANCE >
System.currentTimeMillis()) {
// no wait here, to delay all pingers
Thread.sleep(PING_DISTANCE/2);
}
lastPingTime=System.currentTimeMillis();
}
boolean sent = sockMgr.ping(dest, PING_TIMEOUT);
synchronized(simulLock) {
simulPings--;
simulLock.notifyAll();
}
return sent;
} catch (InterruptedException ex) {
_log.error("Interrupted", ex);
return false;
}
}
public class PingHandler extends Thread {
private String destination;
public PingHandler(String dest) {
this.destination=dest;
setName("PingHandler for " + dest);
start();
}
public void run() {
try {
Destination dest=I2PTunnel.destFromName(destination);
if (dest == null) {
synchronized(lock) { // Logger is not thread safe
l.log("Unresolvable: "+destination+"");
}
return;
}
int cnt = countPing ? CPING_COUNT : PING_COUNT;
StringBuffer pingResults = new StringBuffer
(2*cnt+ destination.length()+3);
for (int i=0;i<cnt; i++) {
boolean sent;
sent = ping(dest);
if (countPing) {
if (!sent) {
pingResults.append(i).append(" ");
break;
} else if (i == cnt - 1) {
pingResults.append("+ ");
}
} else {
pingResults.append(sent?"+ ":"- ");
}
// System.out.println(sent+" -> "+destination);
}
pingResults.append(" ").append(destination);
synchronized(lock) { // Logger is not thread safe
l.log(pingResults.toString());
}
} catch (I2PException ex) {
_log.error("Error pinging " + destination, ex);
}
}
}
}

View File

@ -0,0 +1,9 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
public interface Logging {
public void log(String s);
}

View File

@ -0,0 +1,433 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
import net.i2p.util.Clock;
import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
* Quick and dirty socket listener to control an I2PTunnel.
* Basically run this class as TunnelManager [listenHost] [listenPort] and
* then send it commands on that port. Commands are one shot deals -
* Send a command + newline, get a response plus newline, then get disconnected.
* <p />
* <b>Implemented commands:</b>
* <pre>
* -------------------------------------------------
* lookup &lt;name&gt;\n
* --
* &lt;base64 of the destination&gt;\n
* or
* &lt;error message, usually 'Unknown host'&gt;\n
*
* Lookup the public key of a named destination (i.e. listed in hosts.txt)
* -------------------------------------------------
* genkey\n
* --
* &lt;base64 of the destination&gt;\t&lt;base64 of private data&gt;\n
*
* Generates a new public and private key pair
* -------------------------------------------------
* convertprivate &lt;base64 of privkey&gt;
* --
* &lt;base64 of destination&gt;\n
* or
* &lt;error message&gt;\n
*
* Returns the destination (pubkey) of a given private key.
* -------------------------------------------------
* listen_on &lt;ip&gt;\n
* --
* ok\n
* or
* error\n
*
* Sets the ip address clients will listen on. By default this is the
* localhost (127.0.0.1)
* -------------------------------------------------
* openclient &lt;listenPort&gt; &lt;peer&gt;\n
* --
* ok [&lt;jobId&gt;]\n
* or
* ok &lt;listenPort&gt; [&lt;jobId&gt;]\n
* or
* error\n
*
* Open a tunnel on the given &lt;listenport&gt; to the destination specified
* by &lt;peer&gt;. If &lt;listenPort&gt; is 0 a free port is picked and returned in
* the reply message. Otherwise the short reply message is used.
* Peer can be the base64 of the destination, a file with the public key
* specified as 'file:&lt;filename&gt;' or the name of a destination listed in
* hosts.txt. The &lt;jobId&gt; returned together with "ok" and &lt;listenport&gt; can
* later be used as argument for the "close" command.
* -------------------------------------------------
* openhttpclient &lt;listenPort&gt; [&lt;proxy&gt;]\n
* --
* ok [&lt;jobId&gt;]\n
* or
* ok &lt;listenPort&gt; [&lt;jobId&gt;]\n
* or
* error\n
*
* Open an HTTP proxy through the I2P on the given
* &lt;listenport&gt;. &lt;proxy&gt; (optional) specifies a
* destination to be used as an outbound proxy, to access normal WWW
* sites out of the .i2p domain. If &lt;listenPort&gt; is 0 a free
* port is picked and returned in the reply message. Otherwise the
* short reply message is used. &lt;proxy&gt; can be the base64 of the
* destination, a file with the public key specified as
* 'file:&lt;filename&gt;' or the name of a destination listed in
* hosts.txt. The &lt;jobId&gt; returned together with "ok" and
* &lt;listenport&gt; can later be used as argument for the "close"
* command.
* -------------------------------------------------
* opensockstunnel &lt;listenPort&gt;\n
* --
* ok [&lt;jobId&gt;]\n
* or
* ok &lt;listenPort&gt; [&lt;jobId&gt;]\n
* or
* error\n
*
* Open an SOCKS tunnel through the I2P on the given
* &lt;listenport&gt;. If &lt;listenPort&gt; is 0 a free port is
* picked and returned in the reply message. Otherwise the short
* reply message is used. The &lt;jobId&gt; returned together with
* "ok" and &lt;listenport&gt; can later be used as argument for the
* "close" command.
* -------------------------------------------------
* openserver &lt;serverHost&gt; &lt;serverPort&gt; &lt;serverKeys&gt;\n
* --
* ok [&lt;jobId&gt;]\n
* or
* error\n
*
* Starts receiving traffic for the destination specified by &lt;serverKeys&gt;
* and forwards it to the &lt;serverPort&gt; of &lt;serverHost&gt;.
* &lt;serverKeys&gt; is the base 64 encoded private key set of the local
* destination. The &lt;joId&gt; returned together with "ok" can later be used
* as argument for the "close" command.
* -------------------------------------------------
* close [forced] &lt;jobId&gt;\n
* or
* close [forced] all\n
* --
* ok\n
* or
* error\n
*
* Closes the job specified by &lt;jobId&gt; or all jobs. Use the list command
* for a list of running jobs.
* Normally a connection job is not closed when it still has an active
* connection. Use the optional 'forced' keyword to close connections
* regardless of their use.
* -------------------------------------------------
* list\n
* --
* Example output:
*
* [0] i2p.dnsalias.net/69.55.226.145:5555 &lt;- C:\i2pKeys\squidPriv
* [1] 8767 -&gt; HTTPClient
* [2] 7575 -&gt; file:C:\i2pKeys\squidPub
* [3] 5252 -&gt; sCcSANIO~f4AQtCNI1BvDp3ZBS~9Ag5O0k0Msm7XBWWz5eOnZWL3MQ-2rxlesucb9XnpASGhWzyYNBpWAfaIB3pux1J1xujQLOwscMIhm7T8BP76Ly5jx6BLZCYrrPj0BI0uV90XJyT~4UyQgUlC1jzFQdZ9HDgBPJDf1UI4-YjIwEHuJgdZynYlQ1oUFhgno~HhcDByXO~PDaO~1JDMDbBEfIh~v6MgmHp-Xchod1OfKFrxFrzHgcJbn7E8edTFjZA6JCi~DtFxFelQz1lSBd-QB1qJnA0g-pVL5qngNUojXJCXs4qWcQ7ICLpvIc-Fpfj-0F1gkVlGDSGkb1yLH3~8p4czYgR3W5D7OpwXzezz6clpV8kmbd~x2SotdWsXBPRhqpewO38coU4dJG3OEUbuYmdN~nJMfWbmlcM1lXzz2vBsys4sZzW6dV3hZnbvbfxNTqbdqOh-KXi1iAzXv7CVTun0ubw~CfeGpcAqutC5loRUq7Mq62ngOukyv8Z9AAAA
*
* Lists descriptions of all running jobs. The exact format of the
* description depends on the type of job.
* -------------------------------------------------
* </pre>
*/
public class TunnelManager implements Runnable {
private final static Log _log = new Log(TunnelManager.class);
private I2PTunnel _tunnel;
private ServerSocket _socket;
private boolean _keepAccepting;
public TunnelManager(int listenPort) {
this(null, listenPort);
}
public TunnelManager(String listenHost, int listenPort) {
_tunnel = new I2PTunnel();
_keepAccepting = true;
try {
if (listenHost != null) {
_socket = new ServerSocket(listenPort, 0, InetAddress.getByName(listenHost));
_log.info("Listening for tunnel management clients on " + listenHost + ":" + listenPort);
} else {
_socket = new ServerSocket(listenPort);
_log.info("Listening for tunnel management clients on localhost:" + listenPort);
}
} catch (Exception e) {
_log.error("Error starting up tunnel management listener on " + listenPort, e);
}
}
public static void main(String args[]) {
int port = 7676;
String host = null;
if (args.length == 1) {
try {
port = Integer.parseInt(args[0]);
} catch (NumberFormatException nfe) {
_log.error("Usage: TunnelManager [host] [port]");
return;
}
} else if (args.length == 2) {
host = args[0];
try {
port = Integer.parseInt(args[1]);
} catch (NumberFormatException nfe) {
_log.error("Usage: TunnelManager [host] [port]");
return;
}
}
TunnelManager mgr = new TunnelManager(host, port);
Thread t = new Thread(mgr, "Listener");
t.start();
}
public void run() {
if (_socket == null) {
_log.error("Unable to start listening, since the socket was not bound. Already running?");
return;
}
_log.debug("Running");
try {
while (_keepAccepting) {
Socket socket = _socket.accept();
_log.debug("Client accepted");
if (socket != null) {
Thread t = new I2PThread(new TunnelManagerClientRunner(this, socket));
t.setName("TunnelManager Client");
t.setPriority(I2PThread.MIN_PRIORITY);
t.start();
}
}
} catch (IOException ioe) {
_log.error("Error accepting connections", ioe);
} catch (Exception e) {
_log.error("Other error?!", e);
} finally {
if (_socket != null) try { _socket.close(); } catch (IOException ioe) {}
}
try { Thread.sleep(5000); } catch (InterruptedException ie) {}
}
public void error(String msg, OutputStream out) throws IOException {
out.write(msg.getBytes());
out.write('\n');
}
public void processQuit(OutputStream out) throws IOException {
out.write("Nice try".getBytes());
out.write('\n');
}
public void processList(OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
long startCommand = Clock.getInstance().now();
_tunnel.runCommand("list", buf);
Object obj = _tunnel.waitEventValue("listDone");
long endCommand = Clock.getInstance().now();
String str = buf.getBuffer();
_log.debug("ListDone complete after " + (endCommand-startCommand) + "ms: [" + str + "]");
out.write(str.getBytes());
out.write('\n');
buf.ignoreFurtherActions();
}
public void processListenOn(String ip, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("listen_on " + ip, buf);
String status = (String)_tunnel.waitEventValue("listen_onResult");
out.write((status + "\n").getBytes());
buf.ignoreFurtherActions();
}
/**
* "lookup <name>" returns with the result in base64, else "Unknown host" [or something like that],
* then a newline.
*
*/
public void processLookup(String name, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("lookup " + name, buf);
String rv = (String)_tunnel.waitEventValue("lookupResult");
out.write(rv.getBytes());
out.write('\n');
buf.ignoreFurtherActions();
}
public void processTestDestination(String destKey, OutputStream out) throws IOException {
try {
Destination d = new Destination();
d.fromBase64(destKey);
out.write("valid\n".getBytes());
} catch (DataFormatException dfe) {
out.write("invalid\n".getBytes());
}
out.flush();
}
public void processConvertPrivate(String priv, OutputStream out) throws IOException {
try {
Destination dest = new Destination();
dest.fromBase64(priv);
String str = dest.toBase64();
out.write(str.getBytes());
out.write('\n');
} catch (DataFormatException dfe) {
_log.error("Error converting private data", dfe);
out.write("Error converting private key\n".getBytes());
}
}
public void processClose(String which, boolean forced, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand((forced?"close forced ":"close ") + which, buf);
String str = (String)_tunnel.waitEventValue("closeResult");
out.write((str + "\n").getBytes());
buf.ignoreFurtherActions();
}
/**
* "genkey" returns with the base64 of the destination, followed by a tab, then the base64 of that
* destination's private keys, then a newline.
*
*/
public void processGenKey(OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("gentextkeys", buf);
String priv = (String)_tunnel.waitEventValue("privateKey");
String pub = (String)_tunnel.waitEventValue("publicDestination");
out.write((pub + "\t" + priv).getBytes());
out.write('\n');
buf.ignoreFurtherActions();
}
public void processOpenClient(int listenPort, String peer, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("client " + listenPort + " " + peer, buf);
Integer taskId = (Integer)_tunnel.waitEventValue("clientTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String)_tunnel.waitEventValue("openClientResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
if (listenPort != 0) {
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
return;
}
Integer port = (Integer)_tunnel.waitEventValue("clientLocalPort");
out.write((rv + " " + port.intValue() + " [" + taskId.intValue()
+ "]\n").getBytes());
buf.ignoreFurtherActions();
}
public void processOpenHTTPClient(int listenPort,
String proxy,
OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("httpclient " + listenPort + " " + proxy, buf);
Integer taskId = (Integer)_tunnel.waitEventValue("httpclientTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String)_tunnel.waitEventValue("openHTTPClientResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
if (listenPort != 0) {
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
return;
}
Integer port = (Integer)_tunnel.waitEventValue("clientLocalPort");
out.write((rv + " " + port.intValue() + " [" + taskId.intValue()
+ "]\n").getBytes());
buf.ignoreFurtherActions();
}
public void processOpenSOCKSTunnel(int listenPort,
OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("sockstunnel " + listenPort, buf);
Integer taskId = (Integer)_tunnel.waitEventValue("sockstunnelTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String)_tunnel.waitEventValue("openSOCKSTunnelResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
if (listenPort != 0) {
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
return;
}
Integer port = (Integer)_tunnel.waitEventValue("clientLocalPort");
out.write((rv + " " + port.intValue() + " [" + taskId.intValue()
+ "]\n").getBytes());
buf.ignoreFurtherActions();
}
public void processOpenServer(String serverHost, int serverPort, String privateKeys, OutputStream out) throws IOException {
BufferLogger buf = new BufferLogger();
_tunnel.runCommand("textserver " + serverHost + " " + serverPort + " " + privateKeys, buf);
Integer taskId = (Integer)_tunnel.waitEventValue("serverTaskId");
if (taskId.intValue() < 0) {
out.write("error\n".getBytes());
buf.ignoreFurtherActions();
return;
}
String rv = (String)_tunnel.waitEventValue("openServerResult");
if (rv.equals("error")) {
out.write((rv + "\n").getBytes());
buf.ignoreFurtherActions();
return;
}
out.write((rv + " [" + taskId.intValue() + "]\n").getBytes());
buf.ignoreFurtherActions();
}
/**
* Frisbee.
*
*/
public void unknownCommand(String command, OutputStream out) throws IOException {
out.write("Unknown command: ".getBytes());
out.write(command.getBytes());
out.write("\n".getBytes());
}
}

View File

@ -0,0 +1,193 @@
/* I2PTunnel is GPL'ed (with the exception mentioned in I2PTunnel.java)
* (c) 2003 - 2004 mihi
*/
package net.i2p.i2ptunnel;
import net.i2p.util.Log;
import java.util.StringTokenizer;
import java.net.Socket;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.OutputStream;
/**
* Runner thread that reads commands from the socket and fires off commands to
* the TunnelManager
*
*/
class TunnelManagerClientRunner implements Runnable {
private final static Log _log = new Log(TunnelManagerClientRunner.class);
private TunnelManager _mgr;
private Socket _clientSocket;
public TunnelManagerClientRunner(TunnelManager mgr, Socket socket) {
_clientSocket = socket;
_mgr = mgr;
}
public void run() {
_log.debug("Client running");
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(_clientSocket.getInputStream()));
OutputStream out = _clientSocket.getOutputStream();
String cmd = reader.readLine();
if (cmd != null)
processCommand(cmd, out);
} catch (IOException ioe) {
_log.error("Error processing client commands", ioe);
} finally {
if (_clientSocket != null) try { _clientSocket.close(); } catch (IOException ioe) {}
}
_log.debug("Client closed");
}
/**
* Parse the command string and fire off the appropriate tunnelManager method,
* sending the results to the output stream
*/
private void processCommand(String command, OutputStream out) throws IOException {
_log.debug("Processing [" + command + "]");
StringTokenizer tok = new StringTokenizer(command);
if (!tok.hasMoreTokens()) {
_mgr.unknownCommand(command, out);
} else {
String cmd = tok.nextToken();
if ("quit".equalsIgnoreCase(cmd)) {
_mgr.processQuit(out);
} else if ("lookup".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens())
_mgr.processLookup(tok.nextToken(), out);
else
_mgr.error("Usage: lookup <hostname>", out);
} else if ("testdestination".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens())
_mgr.processTestDestination(tok.nextToken(), out);
else
_mgr.error("Usage: testdestination <publicDestination>", out);
} else if ("convertprivate".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens())
_mgr.processConvertPrivate(tok.nextToken(), out);
else
_mgr.error("Usage: convertprivate <privateData>", out);
} else if ("close".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens()) {
String closeArg;
if ((closeArg = tok.nextToken()).equals("forced")) {
if (tok.hasMoreTokens()) {
_mgr.processClose(tok.nextToken(), true, out);
} else {
_mgr.error("Usage: close [forced] <jobnumber>|all", out);
}
} else {
_mgr.processClose(closeArg, false, out);
}
} else {
_mgr.error("Usage: close [forced] <jobnumber>|all", out);
}
} else if ("genkey".equalsIgnoreCase(cmd)) {
_mgr.processGenKey(out);
} else if ("list".equalsIgnoreCase(cmd)) {
_mgr.processList(out);
} else if ("listen_on".equalsIgnoreCase(cmd)) {
if (tok.hasMoreTokens()) {
_mgr.processListenOn(tok.nextToken(), out);
} else {
_mgr.error("Usage: listen_on <ip>", out);
}
} else if ("openclient".equalsIgnoreCase(cmd)) {
int listenPort = 0;
String peer = null;
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openclient <listenPort> <peer>", out);
return;
}
try {
String portStr = tok.nextToken();
listenPort = Integer.parseInt(portStr);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openclient <listenport> <peer>", out);
return;
}
peer = tok.nextToken();
_mgr.processOpenClient(listenPort, peer, out);
} else if ("openhttpclient".equalsIgnoreCase(cmd)) {
int listenPort = 0;
String proxy = "squid.i2p";
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openhttpclient <listenPort> [<proxy>]",out);
return;
}
try {
String portStr = tok.nextToken();
listenPort = Integer.parseInt(portStr);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
if (tok.hasMoreTokens()) {
proxy = tok.nextToken();
}
if (tok.hasMoreTokens()) {
_mgr.error("Usage: openclient <listenport> [<proxy>]",out);
return;
}
_mgr.processOpenHTTPClient(listenPort, proxy, out);
} else if ("opensockstunnel".equalsIgnoreCase(cmd)) {
int listenPort = 0;
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: opensockstunnel <listenPort>",out);
return;
}
try {
String portStr = tok.nextToken();
listenPort = Integer.parseInt(portStr);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
if (tok.hasMoreTokens()) {
_mgr.error("Usage: opensockstunnel <listenport>",out);
return;
}
_mgr.processOpenSOCKSTunnel(listenPort, out);
} else if ("openserver".equalsIgnoreCase(cmd)) {
int listenPort = 0;
String serverHost = null;
String serverKeys = null;
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
return;
}
serverHost = tok.nextToken();
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
return;
}
try {
String portStr = tok.nextToken();
listenPort = Integer.parseInt(portStr);
} catch (NumberFormatException nfe) {
_mgr.error("Bad listen port", out);
return;
}
if (!tok.hasMoreTokens()) {
_mgr.error("Usage: openserver <serverHost> <serverPort> <serverKeys>", out);
return;
}
serverKeys = tok.nextToken();
_mgr.processOpenServer(serverHost, listenPort, serverKeys, out);
} else {
_mgr.unknownCommand(command, out);
}
}
}
}

View File

@ -0,0 +1,56 @@
/* I2PSOCKSTunnel is released under the terms of the GNU GPL,
* with an additional exception. For further details, see the
* licensing terms in I2PTunnel.java.
*
* Copyright (c) 2004 by human
*/
package net.i2p.i2ptunnel.socks;
import java.net.Socket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.Destination;
import net.i2p.i2ptunnel.I2PTunnelClientBase;
import net.i2p.i2ptunnel.I2PTunnelRunner;
import net.i2p.i2ptunnel.Logging;
import net.i2p.util.EventDispatcher;
import net.i2p.util.Log;
public class I2PSOCKSTunnel extends I2PTunnelClientBase {
private static final Log _log = new Log(I2PSOCKSTunnel.class);
protected Destination outProxyDest = null;
//public I2PSOCKSTunnel(int localPort, Logging l, boolean ownDest) {
// I2PSOCKSTunnel(localPort, l, ownDest, (EventDispatcher)null);
//}
public I2PSOCKSTunnel(int localPort, Logging l, boolean ownDest,
EventDispatcher notifyThis) {
super(localPort, ownDest, l, notifyThis, "SOCKSHandler");
if (waitEventValue("openBaseClientResult").equals("error")) {
notifyEvent("openSOCKSTunnelResult", "error");
return;
}
setName(getLocalPort() + " -> SOCKSTunnel");
startRunning();
notifyEvent("openSOCKSTunnelResult", "ok");
}
protected void clientConnectionRun(Socket s) {
try {
SOCKSServer serv = SOCKSServerFactory.createSOCKSServer(s);
Socket clientSock = serv.getClientSocket();
I2PSocket destSock = serv.getDestinationI2PSocket();
new I2PTunnelRunner (clientSock, destSock, sockLock, null);
} catch (SOCKSException e) {
_log.error("Error from SOCKS connection: " + e.getMessage());
closeSocket(s);
}
}
}

View File

@ -0,0 +1,309 @@
/* I2PSOCKSTunnel is released under the terms of the GNU GPL,
* with an additional exception. For further details, see the
* licensing terms in I2PTunnel.java.
*
* Copyright (c) 2004 by human
*/
package net.i2p.i2ptunnel.socks;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import net.i2p.util.HexDump;
import net.i2p.util.Log;
/*
* Class that manages SOCKS5 connections, and forwards them to
* destination hosts or (eventually) some outproxy.
*
* @author human
*/
public class SOCKS5Server extends SOCKSServer {
private static final Log _log = new Log(SOCKS5Server.class);
private static final int SOCKS_VERSION_5 = 0x05;
private Socket clientSock = null;
private boolean setupCompleted = false;
/**
* Create a SOCKS5 server that communicates with the client using
* the specified socket. This method should not be invoked
* directly: new SOCKS5Server objects should be created by using
* SOCKSServerFactory.createSOCSKServer(). It is assumed that the
* SOCKS VER field has been stripped from the input stream of the
* client socket.
*
* @param clientSock client socket
*/
public SOCKS5Server(Socket clientSock) {
this.clientSock = clientSock;
}
public Socket getClientSocket() throws SOCKSException {
setupServer();
return clientSock;
}
protected void setupServer() throws SOCKSException {
if (setupCompleted) {
return;
}
DataInputStream in;
DataOutputStream out;
try {
in = new DataInputStream(clientSock.getInputStream());
out = new DataOutputStream(clientSock.getOutputStream());
init(in, out);
manageRequest(in, out);
} catch (IOException e) {
throw new SOCKSException("Connection error ("
+ e.getMessage() + ")");
}
setupCompleted = true;
}
/**
* SOCKS5 connection initialization. This method assumes that
* SOCKS "VER" field has been stripped from the input stream.
*/
private void init (DataInputStream in,
DataOutputStream out) throws IOException, SOCKSException {
int nMethods = in.readByte() & 0xff;
boolean methodOk = false;
int method = Method.NO_ACCEPTABLE_METHODS;
for (int i = 0; i < nMethods; ++i) {
method = in.readByte() & 0xff;
if (method == Method.NO_AUTH_REQUIRED) {
// That's fine, we do support this method
break;
}
}
boolean canContinue = false;
switch (method) {
case Method.NO_AUTH_REQUIRED:
_log.debug("no authentication required");
sendInitReply(Method.NO_AUTH_REQUIRED, out);
return;
default:
_log.debug("no suitable authentication methods found ("
+ Integer.toHexString(method)+ ")");
sendInitReply(Method.NO_ACCEPTABLE_METHODS, out);
throw new SOCKSException("Unsupported authentication method");
}
}
/**
* SOCKS5 request management. This method assumes that all the
* stuff preceding or enveloping the actual request (e.g. protocol
* initialization, integrity/confidentiality encapsulations, etc)
* has been stripped out of the input/output streams.
*/
private void manageRequest(DataInputStream in,
DataOutputStream out) throws IOException, SOCKSException {
int socksVer = in.readByte() & 0xff;
if (socksVer != SOCKS_VERSION_5) {
_log.debug("error in SOCKS5 request (protocol != 5? wtf?)");
throw new SOCKSException("Invalid protocol version in request");
}
int command = in.readByte() & 0xff;
switch (command) {
case Command.CONNECT:
break;
case Command.BIND:
_log.debug("BIND command is not supported!");
sendRequestReply(Reply.COMMAND_NOT_SUPPORTED,
AddressType.DOMAINNAME, null,
"0.0.0.0", 0, out);
throw new SOCKSException("BIND command not supported");
case Command.UDP_ASSOCIATE:
_log.debug("UDP ASSOCIATE command is not supported!");
sendRequestReply(Reply.COMMAND_NOT_SUPPORTED,
AddressType.DOMAINNAME, null,
"0.0.0.0", 0, out);
throw new SOCKSException("UDP ASSOCIATE command not supported");
default:
_log.debug("unknown command in request ("
+ Integer.toHexString(command) + ")");
throw new SOCKSException("Invalid command in request");
}
{
// Reserved byte, should be 0x00
byte rsv = in.readByte();
}
int addressType = in.readByte() & 0xff;
switch (addressType) {
case AddressType.IPV4:
connHostName = new String("");
for (int i = 0; i < 4; ++i) {
int octet = in.readByte() & 0xff;
connHostName += Integer.toString(octet);
if (i != 3) {
connHostName += ".";
}
}
_log.warn("IPV4 address type in request: " + connHostName
+ ". Is your client secure?");
break;
case AddressType.DOMAINNAME:
{
int addrLen = in.readByte() & 0xff;
if (addrLen == 0) {
_log.debug("0-sized address length? wtf?");
throw new SOCKSException("Illegal DOMAINNAME length");
}
byte addr[] = new byte[addrLen];
in.readFully(addr);
connHostName = new String(addr);
}
_log.debug("DOMAINNAME address type in request: " + connHostName);
break;
case AddressType.IPV6:
_log.warn("IP V6 address type in request! Is your client secure?"
+ " (IPv6 is not supported, anyway :-)");
sendRequestReply(Reply.ADDRESS_TYPE_NOT_SUPPORTED,
AddressType.DOMAINNAME, null,
"0.0.0.0", 0, out);
throw new SOCKSException("IPV6 addresses not supported");
default:
_log.debug("unknown address type in request ("
+ Integer.toHexString(command) + ")");
throw new SOCKSException("Invalid addresses type in request");
}
connPort = in.readUnsignedShort();
if (connPort == 0) {
_log.debug("trying to connect to TCP port 0? Dropping!");
throw new SOCKSException("Invalid port number in request");
}
}
protected void confirmConnection() throws SOCKSException {
DataInputStream in;
DataOutputStream out;
try {
out = new DataOutputStream(clientSock.getOutputStream());
sendRequestReply(Reply.SUCCEEDED,
AddressType.IPV4,
InetAddress.getByName("127.0.0.1"),
null, 1, out);
} catch (IOException e) {
throw new SOCKSException("Connection error ("
+ e.getMessage() + ")");
}
}
/**
* Send the specified reply during SOCKS5 initialization
*/
private void sendInitReply(int replyCode,
DataOutputStream out) throws IOException {
ByteArrayOutputStream reps = new ByteArrayOutputStream();
reps.write(SOCKS_VERSION_5);
reps.write(replyCode);
byte[] reply = reps.toByteArray();
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Sending init reply:\n" + HexDump.dump(reply));
}
out.write(reply);
}
/**
* Send the specified reply to a request of the client. Either
* one of inetAddr or domainName can be null, depending on
* addressType.
*/
private void sendRequestReply(int replyCode,
int addressType,
InetAddress inetAddr,
String domainName,
int bindPort,
DataOutputStream out) throws IOException {
ByteArrayOutputStream reps = new ByteArrayOutputStream();
DataOutputStream dreps = new DataOutputStream(reps);
dreps.write(SOCKS_VERSION_5);
dreps.write(replyCode);
// Reserved byte, should be 0x00
dreps.write(0x00);
dreps.write(addressType);
switch (addressType) {
case AddressType.IPV4:
dreps.write(inetAddr.getAddress());
break;
case AddressType.DOMAINNAME:
dreps.writeByte(domainName.length());
dreps.writeBytes(domainName);
break;
default:
_log.error("unknown address type passed to sendReply() ("
+ Integer.toHexString(addressType) + ")! wtf?");
return;
}
dreps.writeShort(bindPort);
byte[] reply = reps.toByteArray();
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Sending request reply:\n" + HexDump.dump(reply));
}
out.write(reply);
}
/*
* Some namespaces to enclose SOCKS protocol codes
*/
private class Method {
private static final int NO_AUTH_REQUIRED = 0x00;
private static final int NO_ACCEPTABLE_METHODS = 0xff;
}
private class AddressType {
private static final int IPV4 = 0x01;
private static final int DOMAINNAME = 0x03;
private static final int IPV6 = 0x04;
}
private class Command {
private static final int CONNECT = 0x01;
private static final int BIND = 0x02;
private static final int UDP_ASSOCIATE = 0x03;
}
private class Reply {
private static final int SUCCEEDED = 0x00;
private static final int GENERAL_SOCKS_SERVER_FAILURE = 0x01;
private static final int CONNECTION_NOT_ALLOWED_BY_RULESET = 0x02;
private static final int NETWORK_UNREACHABLE = 0x03;
private static final int HOST_UNREACHABLE = 0x04;
private static final int CONNECTION_REFUSED = 0x05;
private static final int TTL_EXPIRED = 0x06;
private static final int COMMAND_NOT_SUPPORTED = 0x07;
private static final int ADDRESS_TYPE_NOT_SUPPORTED = 0x08;
}
}

View File

@ -0,0 +1,23 @@
/* I2PSOCKSTunnel is released under the terms of the GNU GPL,
* with an additional exception. For further details, see the
* licensing terms in I2PTunnel.java.
*
* Copyright (c) 2004 by human
*/
package net.i2p.i2ptunnel.socks;
/**
* Exception thrown by socket methods
*
* @author human
*/
public class SOCKSException extends Exception {
public SOCKSException() {
super();
}
public SOCKSException(String s) {
super(s);
}
}

View File

@ -0,0 +1,100 @@
/* I2PSOCKSTunnel is released under the terms of the GNU GPL,
* with an additional exception. For further details, see the
* licensing terms in I2PTunnel.java.
*
* Copyright (c) 2004 by human
*/
package net.i2p.i2ptunnel.socks;
import java.net.Socket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.data.DataFormatException;
import net.i2p.I2PException;
import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.util.Log;
/**
* Abstract base class used by all SOCKS servers.
*
* @author human
*/
public abstract class SOCKSServer {
private static final Log _log = new Log(SOCKSServer.class);
/* Details about the connection requested by client */
protected String connHostName = null;
protected int connPort = 0;
I2PSocket destSocket = null;
Object FIXME = new Object();
/**
* Perform server initialization (expecially regarding protected
* variables).
*/
protected abstract void setupServer() throws SOCKSException;
/**
* Get a socket that can be used to send/receive 8-bit clean data
* to/from the client.
*
* @return a Socket connected with the client
*/
public abstract Socket getClientSocket() throws SOCKSException;
/**
* Confirm to the client that the connection has succeeded
*/
protected abstract void confirmConnection() throws SOCKSException;
/**
* Get an I2PSocket that can be used to send/receive 8-bit clean data
* to/from the destination of the SOCKS connection.
*
* @return an I2PSocket connected with the destination
*/
public I2PSocket getDestinationI2PSocket() throws SOCKSException {
setupServer();
if (connHostName == null) {
_log.error("BUG: destination host name has not been initialized!");
throw new SOCKSException("BUG! See the logs!");
}
if (connPort == 0) {
_log.error("BUG: destination port has not been initialized!");
throw new SOCKSException("BUG! See the logs!");
}
// FIXME: here we should read our config file, select an
// outproxy, and instantiate the proper socket class that
// handles the outproxy itself (SOCKS4a, SOCKS5, HTTP CONNECT...).
I2PSocket destSock;
try {
if (connHostName.toLowerCase().endsWith(".i2p")) {
_log.debug("connecting to " + connHostName + "...");
I2PSocketManager sm = I2PSocketManagerFactory.createManager();
destSock = sm.connect(I2PTunnel.destFromName(connHostName),
new I2PSocketOptions());
confirmConnection();
_log.debug("connection confirmed - exchanging data...");
} else {
_log.error("We don't support outproxies (yet)");
throw new SOCKSException("Ouproxies not supported (yet)");
}
} catch (DataFormatException e) {
throw new SOCKSException("Error in destination format");
} catch (I2PException e) {
throw new SOCKSException("I2P error (" + e.getMessage() + ")");
}
return destSock;
}
}

View File

@ -0,0 +1,53 @@
/* I2PSOCKSTunnel is released under the terms of the GNU GPL,
* with an additional exception. For further details, see the
* licensing terms in I2PTunnel.java.
*
* Copyright (c) 2004 by human
*/
package net.i2p.i2ptunnel.socks;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
import net.i2p.util.Log;
/**
* Factory class for creating SOCKS forwarders through I2P
*/
public class SOCKSServerFactory {
private final static Log _log = new Log(SOCKSServerFactory.class);
/**
* Create a new SOCKS server, using the provided socket (that must
* be connected to a client) to select the proper SOCKS protocol
* version. This method wil strip the SOCKS VER field from the
* provided sockets's input stream.
*
* @param s a Socket used to choose the SOCKS server type
*/
public static SOCKSServer createSOCKSServer(Socket s) throws SOCKSException {
SOCKSServer serv;
try {
DataInputStream in = new DataInputStream(s.getInputStream());
int socksVer = in.readByte();
switch (socksVer) {
case 0x05: // SOCKS version 5
serv = new SOCKS5Server(s);
break;
default:
_log.debug("SOCKS protocol version not supported ("
+ Integer.toHexString(socksVer) + ")");
return null;
}
} catch (IOException e) {
_log.debug("error reading SOCKS protocol version");
throw new SOCKSException("Connection error ("
+ e.getMessage() + ")");
}
return serv;
}
}

View File

@ -0,0 +1,83 @@
ministreaming protocol:
*******************
Each message looks like the following
1 byte type
3 byte id
x byte payload.
messages from client to server have type 0xA0 to 0xAF,
messages from server to client have type 0x50 to 0x5F.
Each "connections" has 2 IDs, a client ID and a server ID.
These IDs may be any 3-byte values except 00 00 00, which is reserved.
All connections are created as PROP_RELIABILITY_GUARANTEED.
"actions" are the things a proper tunnel implementation SHOULD do
when it receives such a message.
Client->Server:
===============
0xA0 Send data
id: the server id
payload: the data to send
actions: send the data to the TCP connection
0xA1 SYN
id: the client id
payload: the public key dynamically created by the client
actions: create a server ID and create a TCP connection. When successful,
send an ACK back, otherwise a close.
0xA2 Close
id: the server id
payload: nothing
actions: close the connection
Server->Client
==============
0x50 Send data
id: the client id
payload: the data to send
actions: send the data to the TCP connection
0x51 ACK
id: the client id
payload: the server id
actions: nothing
0x52 Close
id: the client id
payload: nothing
actions: close the connection
Sample conversations:
=====================
a) Service not available (e.g. the server on the TCP port is not running)
C->S A1 12 34 56 key... (SYN, client ID = 12 34 56)
S->C 52 12 34 56 (Close)
b) Service available, server sends data, client closes
C->S A1 23 45 67 key... (SYN)
S->C 51 23 45 67 98 76 54 (ACK, server ID = 98 76 54)
S->C 50 23 45 67 data (Send data)
C->S A2 98 76 54 (Close)
c) Service available, client sends data first, server closes after answer (HTTP)
C->S A1 11 11 11 key... (SYN)
S->C 51 11 11 11 FF FF FF (ACK)
C->S A0 FF FF FF data (send data)
S->C 50 11 11 11 data (answer with data)
S->C 50 11 11 11 data (more data)
S->C 51 11 11 11 (Close)

View File

@ -0,0 +1,10 @@
$Id$
the i2p/apps/ministreaming module is the root of the
ministreaming library, and everything within it
is released according to the terms of the I2P
license policy. That means everything contained
within the i2p/apps/ministreaming module is released
under a BSD license unless otherwise marked.
Alternate licenses that may be used include Cryptix,
MIT, as well as code granted into the public domain.

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="ministreaming">
<target name="all" depends="clean, build" />
<target name="build" depends="builddep, jar" />
<target name="builddep">
<ant dir="../../../core/java/" target="build" />
</target>
<target name="compile">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac srcdir="./src" debug="true" destdir="./build/obj" classpath="../../../core/java/build/i2p.jar" />
</target>
<target name="jar" depends="compile">
<jar destfile="./build/mstreaming.jar" basedir="./build/obj" includes="**/*.class" />
</target>
<target name="javadoc">
<mkdir dir="./build" />
<mkdir dir="./build/javadoc" />
<javadoc
sourcepath="./src:../../../core/java/src:../../../core/java/test" destdir="./build/javadoc"
packagenames="*"
use="true"
splitindex="true"
windowtitle="I2P ministreaming library" />
</target>
<target name="clean">
<delete dir="./build" />
</target>
<target name="cleandep" depends="clean">
<ant dir="../../../core/java/" target="cleandep" />
</target>
<target name="distclean" depends="clean">
<ant dir="../../../core/java/" target="distclean" />
</target>
</project>

View File

@ -0,0 +1,159 @@
package net.i2p.client.streaming;
/** Like a StringBuffer, but for bytes */
public class ByteCollector {
byte[] contents;
int size;
public ByteCollector() {
contents=new byte[80];
size=0;
}
public ByteCollector(byte[] b) {
this();
append(b);
}
public ByteCollector(byte b) {
this();
append(b);
}
public ByteCollector append (byte b) {
ensureCapacity(size+1);
contents[size++]=b;
return this;
}
public ByteCollector append (byte[] b) {
ensureCapacity(size+b.length);
System.arraycopy(b,0,contents,size,b.length);
size+=b.length;
return this;
}
public ByteCollector append(byte[] b, int len) {
return append(b,0,len);
}
public ByteCollector append(byte[] b, int off, int len) {
ensureCapacity(size+len);
System.arraycopy(b,off,contents,size,len);
size+=len;
return this;
}
public ByteCollector append(ByteCollector bc) {
// optimieren?
return append(bc.toByteArray());
}
public byte[] toByteArray() {
byte[] result=new byte[size];
System.arraycopy(contents,0,result,0,size);
return result;
}
public byte[] startToByteArray(int maxlen) {
if (size < maxlen) {
byte[] res = toByteArray();
clear();
return res;
} else {
byte[] result = new byte[maxlen];
System.arraycopy(contents,0,result,0,maxlen);
System.arraycopy(contents,maxlen,contents,0,size-maxlen);
size-=maxlen;
return result;
}
}
public int getCurrentSize() {
return size;
}
public boolean ensureCapacity(int cap) {
if (contents.length<cap) {
int l=contents.length;
while (l<cap) {
l=(l*3)/2+1;
}
byte[] newcont=new byte[l];
System.arraycopy(contents,0,newcont,0,size);
contents=newcont;
return true;
}
return false;
}
public boolean isEmpty() {
return size==0;
}
public int indexOf(ByteCollector bc) {
// optimieren?
return indexOf(bc.toByteArray());
}
public int indexOf(byte b) {
// optimieren?
return indexOf(new byte[] {b});
}
public int indexOf(byte[] ba) {
loop:
for (int i=0;i<size-ba.length+1;i++) {
for (int j=0;j<ba.length;j++) {
if (contents[i+j]!=ba[j]) continue loop;
}
return i;
}
return -1;
}
public void clear() {
size=0;
}
public void clearAndShorten() {
size=0;
contents=new byte[80];
}
public String toString() {
return new String(toByteArray());
}
public int hashCode() {
int h =0;
for (int i=0;i<size;i++) {
h+=contents[i]*contents[i];
}
return h;
}
public boolean equals(Object o) {
if (o instanceof ByteCollector) {
ByteCollector by=(ByteCollector)o;
if (size!=by.size) return false;
for (int i=0;i<size;i++) {
if (contents[i]!=by.contents[i]) return false;
}
return true;
} else {
return false;
}
}
public byte removeFirst() {
byte bb=contents[0];
if (size==0)
throw new IllegalArgumentException("ByteCollector is empty");
if(size>1)
System.arraycopy(contents,1,contents,0,--size);
else
size=0;
return bb;
}
}

View File

@ -0,0 +1,29 @@
package net.i2p.client.streaming;
import net.i2p.I2PException;
/**
* Defines how to listen for streaming peer connections
*
*/
public interface I2PServerSocket {
/**
* Closes the socket.
*/
public void close() throws I2PException;
/**
* Waits for the next socket connecting. If a remote user tried to make a
* connection and the local application wasn't .accept()ing new connections,
* they should get refused (if .accept() doesnt occur in some small period)
*
* @throws I2PException if there is a problem with reading a new socket
* from the data available (aka the I2PSession closed, etc)
*/
public I2PSocket accept() throws I2PException;
/**
* Access the manager which is coordinating the server socket
*/
public I2PSocketManager getManager();
}

View File

@ -0,0 +1,50 @@
package net.i2p.client.streaming;
import net.i2p.I2PException;
import net.i2p.util.Log;
/**
* Initial stub implementation for the server socket
*
*/
class I2PServerSocketImpl implements I2PServerSocket {
private final static Log _log = new Log(I2PServerSocketImpl.class);
private I2PSocketManager mgr;
private I2PSocket cached=null; // buffer one socket here
public I2PServerSocketImpl(I2PSocketManager mgr) {
this.mgr = mgr;
}
public synchronized I2PSocket accept() throws I2PException {
while(cached == null) {
myWait();
}
I2PSocket ret=cached;
cached=null;
notifyAll();
_log.debug("TIMING: handed out accept result "+ret.hashCode());
return ret;
}
public synchronized boolean getNewSocket(I2PSocket s){
while(cached != null) {
myWait();
}
cached=s;
notifyAll();
return true;
}
public void close() throws I2PException {
//noop
}
private void myWait() {
try{
wait();
} catch (InterruptedException ex) {}
}
public I2PSocketManager getManager() { return mgr; }
}

View File

@ -0,0 +1,39 @@
package net.i2p.client.streaming;
import net.i2p.data.Destination;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
/**
* Minimalistic adapter between the socket api and I2PTunnel's way.
* Note that this interface is a "subinterface" of the interface
* defined in the "official" streaming api.
*/
public interface I2PSocket {
/**
* Return the Destination of this side of the socket.
*/
public Destination getThisDestination();
/**
* Return the destination of the peer.
*/
public Destination getPeerDestination();
/**
* Return an InputStream to read from the socket.
*/
public InputStream getInputStream() throws IOException;
/**
* Return an OutputStream to write into the socket.
*/
public OutputStream getOutputStream() throws IOException;
/**
* Closes the socket if not closed yet
*/
public void close() throws IOException;
}

View File

@ -0,0 +1,335 @@
package net.i2p.client.streaming;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import net.i2p.I2PException;
import net.i2p.client.I2PSessionException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/**
* Initial stub implementation for the socket
*
*/
class I2PSocketImpl implements I2PSocket {
private final static Log _log = new Log(I2PSocketImpl.class);
public static final int MAX_PACKET_SIZE = 1024*32;
public static final int PACKET_DELAY=100;
private I2PSocketManager manager;
private Destination local;
private Destination remote;
private String localID;
private String remoteID;
private Object remoteIDWaiter = new Object();
private I2PInputStream in;
private I2POutputStream out;
private boolean outgoing;
private Object flagLock = new Object();
private boolean closed = false, sendClose=true, closed2=false;
public I2PSocketImpl(Destination peer, I2PSocketManager mgr,
boolean outgoing, String localID) {
this.outgoing=outgoing;
manager = mgr;
remote = peer;
local = mgr.getSession().getMyDestination();
in = new I2PInputStream();
I2PInputStream pin = new I2PInputStream();
out = new I2POutputStream(pin);
new I2PSocketRunner(pin);
this.localID = localID;
}
public String getLocalID() {
return localID;
}
public void setRemoteID(String id) {
synchronized(remoteIDWaiter) {
remoteID=id;
remoteIDWaiter.notifyAll();
}
}
public String getRemoteID(boolean wait) throws InterruptedIOException {
return getRemoteID(wait, -1);
}
public String getRemoteID(boolean wait, long maxWait) throws InterruptedIOException {
long dieAfter = System.currentTimeMillis() + maxWait;
synchronized(remoteIDWaiter) {
while (wait && remoteID==null) {
try {
if (maxWait > 0)
remoteIDWaiter.wait(maxWait);
else
remoteIDWaiter.wait();
} catch (InterruptedException ex) {}
if ( (maxWait > 0) && (System.currentTimeMillis() > dieAfter) )
throw new InterruptedIOException("Timed out waiting for remote ID");
}
if (wait) {
_log.debug("TIMING: RemoteID set to " + I2PSocketManager.getReadableForm(remoteID) +" for "+this.hashCode());
}
return remoteID;
}
}
public String getRemoteID() throws InterruptedIOException {
return getRemoteID(false);
}
public void queueData(byte[] data) {
in.queueData(data);
}
/**
* Return the Destination of this side of the socket.
*/
public Destination getThisDestination() { return local; }
/**
* Return the destination of the peer.
*/
public Destination getPeerDestination() { return remote; }
/**
* Return an InputStream to read from the socket.
*/
public InputStream getInputStream() throws IOException {
if ( (in == null) )
throw new IOException("Not connected");
return in;
}
/**
* Return an OutputStream to write into the socket.
*/
public OutputStream getOutputStream() throws IOException {
if ( (out == null) )
throw new IOException("Not connected");
return out;
}
/**
* Closes the socket if not closed yet
*/
public void close() throws IOException {
synchronized(flagLock) {
_log.debug("Closing connection");
closed=true;
}
out.close();
in.notifyClosed();
}
public void internalClose() {
synchronized(flagLock) {
closed=true;
closed2=true;
sendClose=false;
}
out.close();
in.notifyClosed();
}
private byte getMask(int add) {
return (byte)((outgoing?(byte)0xA0:(byte)0x50)+(byte)add);
}
//--------------------------------------------------
public class I2PInputStream extends InputStream {
private ByteCollector bc = new ByteCollector();
public int read() throws IOException {
byte[] b = new byte[1];
int res = read(b);
if (res == 1) return b[0] & 0xff;
if (res == -1) return -1;
throw new RuntimeException("Incorrect read() result");
}
public synchronized int read(byte[] b, int off, int len) throws IOException {
_log.debug("Read called: "+this.hashCode());
if (len==0) return 0;
byte[] read = bc.startToByteArray(len);
while (read.length==0) {
synchronized(flagLock) {
if (closed){
_log.debug("Closed is set, so closing stream: "+this.hashCode());
return -1;
}
}
try {
wait();
} catch (InterruptedException ex) {}
read = bc.startToByteArray(len);
}
if (read.length>len) throw new RuntimeException("BUG");
System.arraycopy(read,0,b,off,read.length);
if (_log.shouldLog(Log.DEBUG)) {
_log.debug("Read from I2PInputStream " + this.hashCode()
+ " returned "+read.length+" bytes");
}
//if (_log.shouldLog(Log.DEBUG)) {
// _log.debug("Read from I2PInputStream " + this.hashCode()
// + " returned "+read.length+" bytes:\n"
// + HexDump.dump(read));
//}
return read.length;
}
public int available() {
return bc.getCurrentSize();
}
public void queueData(byte[] data) {
queueData(data,0,data.length);
}
public synchronized void queueData(byte[] data, int off, int len) {
_log.debug("Insert "+len+" bytes into queue: "+this.hashCode());
bc.append(data, off, len);
notifyAll();
}
public synchronized void notifyClosed() {
notifyAll();
}
}
public class I2POutputStream extends OutputStream {
public I2PInputStream sendTo;
public I2POutputStream(I2PInputStream sendTo) {
this.sendTo=sendTo;
}
public void write(int b) throws IOException {
write(new byte[] {(byte)b});
}
public void write (byte[] b, int off, int len) throws IOException {
sendTo.queueData(b,off,len);
}
public void close() {
sendTo.notifyClosed();
}
}
public class I2PSocketRunner extends Thread {
public InputStream in;
public I2PSocketRunner(InputStream in) {
_log.debug("Runner's input stream is: "+in.hashCode());
this.in=in;
start();
}
public void run() {
byte[] buffer = new byte[MAX_PACKET_SIZE];
ByteCollector bc = new ByteCollector();
boolean sent = true;
try {
int len, bcsize;
// try {
while (true) {
len = in.read(buffer);
bcsize = bc.getCurrentSize();
if (len != -1) {
bc.append(buffer,len);
} else if (bcsize == 0) {
break;
}
if ((bcsize < MAX_PACKET_SIZE)
&& (in.available()==0)) {
_log.debug("Runner Point d: "+this.hashCode());
try {
Thread.sleep(PACKET_DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if ((bcsize >= MAX_PACKET_SIZE)
|| (in.available()==0) ) {
byte[] data = bc.startToByteArray(MAX_PACKET_SIZE);
if (data.length > 0) {
_log.debug("Message size is: "+data.length);
sent = sendBlock(data);
if (!sent) {
_log.error("Error sending message to peer. Killing socket runner");
break;
}
}
}
}
if ((bc.getCurrentSize() > 0) && sent) {
_log.error("A SCARY MONSTER HAS EATEN SOME DATA! "
+ "(input stream: " + in.hashCode() + "; "
+ "queue size: " + bc.getCurrentSize() + ")");
}
synchronized(flagLock) {
closed2=true;
}
// } catch (IOException ex) {
// if (_log.shouldLog(Log.INFO))
// _log.info("Error reading and writing", ex);
// }
boolean sc;
synchronized(flagLock) {
sc=sendClose;
} // FIXME: Race here?
if (sc) {
_log.info("Sending close packet: "+outgoing);
byte[] packet = I2PSocketManager.makePacket
((byte)(getMask(0x02)),remoteID, new byte[0]);
synchronized(manager.getSession()) {
sent = manager.getSession().sendMessage(remote, packet);
}
if (!sent) {
_log.error("Error sending close packet to peer");
}
}
manager.removeSocket(I2PSocketImpl.this);
} catch (IOException ex) {
// WHOEVER removes this event on inconsistent
// state before fixing the inconsistent state (a
// reference on the socket in the socket manager
// etc.) will get hanged by me personally -- mihi
_log.error("Error running - **INCONSISTENT STATE!!!**", ex);
} catch (I2PException ex) {
_log.error("Error running - **INCONSISTENT STATE!!!**" , ex);
}
}
private boolean sendBlock(byte data[]) throws I2PSessionException {
_log.debug("TIMING: Block to send for "+I2PSocketImpl.this.hashCode());
if (remoteID==null) {
_log.error("NULL REMOTEID");
return false;
}
byte[] packet = I2PSocketManager.makePacket(getMask(0x00), remoteID,
data);
boolean sent;
synchronized(flagLock) {
if (closed2) return false;
}
synchronized(manager.getSession()) {
sent = manager.getSession().sendMessage(remote, packet);
}
return sent;
}
}
}

View File

@ -0,0 +1,386 @@
/*
* licensed under BSD license...
* (if you know the proper clause for that, add it ...)
*/
package net.i2p.client.streaming;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import net.i2p.I2PException;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionListener;
import net.i2p.data.Base64;
import net.i2p.data.Destination;
import net.i2p.util.Log;
/**
* Centralize the coordination and multiplexing of the local client's streaming.
* There should be one I2PSocketManager for each I2PSession, and if an application
* is sending and receiving data through the streaming library using an
* I2PSocketManager, it should not attempt to call I2PSession's setSessionListener
* or receive any messages with its .receiveMessage
*
*/
public class I2PSocketManager implements I2PSessionListener {
private final static Log _log = new Log(I2PSocketManager.class);
private I2PSession _session;
private I2PServerSocketImpl _serverSocket;
private Object lock = new Object(); // for locking socket lists
private HashMap _outSockets;
private HashMap _inSockets;
private I2PSocketOptions _defaultOptions;
public static final int PUBKEY_LENGTH=387;
public I2PSocketManager() {
_session=null;
_serverSocket = new I2PServerSocketImpl(this);
_inSockets = new HashMap(16);
_outSockets = new HashMap(16);
}
public I2PSession getSession() {
return _session;
}
public void setSession(I2PSession session) {
_session = session;
if (session != null)
session.setSessionListener(this);
}
public void disconnected(I2PSession session) {
_log.error("Disconnected from the session");
}
public void errorOccurred(I2PSession session, String message, Throwable error) {
_log.error("Error occurred: [" + message + "]", error);
}
public void messageAvailable(I2PSession session, int msgId, long size) {
try {
I2PSocketImpl s;
byte msg[] = session.receiveMessage(msgId);
if (msg.length == 1 && msg[0] == -1) {
_log.debug("Ping received");
return;
}
if (msg.length <4) {
_log.error("==== packet too short ====");
return;
}
int type = msg[0] & 0xff;
String id = new String(new byte[] {msg[1], msg[2], msg[3]},
"ISO-8859-1");
byte[] payload = new byte[msg.length-4];
System.arraycopy(msg, 4, payload, 0, payload.length);
_log.debug("Message read: type = [" + Integer.toHexString(type) +
"] id = [" + getReadableForm(id)+
"] payload length: " + payload.length + "]");
synchronized(lock) {
switch(type) {
case 0x51: // ACK outgoing
s = (I2PSocketImpl) _outSockets.get(id);
if (s == null) {
_log.warn("No socket responsible for ACK packet");
return;
}
if (payload.length==3 && s.getRemoteID(false)==null) {
String newID = new String(payload,
"ISO-8859-1");
s.setRemoteID(newID);
return;
} else {
if (payload.length != 3)
_log.warn("Ack packet had " + payload.length + " bytes");
else
_log.warn("Remote ID already exists? " + s.getRemoteID());
return;
}
case 0x52: // disconnect outgoing
_log.debug("*Disconnect outgoing!");
try {
s = (I2PSocketImpl) _outSockets.get(id);
if (payload.length==0 && s != null) {
s.internalClose();
_outSockets.remove(id);
return;
} else {
if (payload.length > 0)
_log.warn("Disconnect packet had " + payload.length + " bytes");
return;
}
} catch (Exception t) {
_log.error("Ignoring error on disconnect", t);
}
case 0x50: // packet send outgoing
_log.debug("*Packet send outgoing [" + payload.length + "]");
s = (I2PSocketImpl) _outSockets.get(id);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.error("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
case 0xA1: // SYN incoming
_log.debug("*Syn!");
if (payload.length==PUBKEY_LENGTH) {
String newLocalID = makeID(_inSockets);
Destination d = new Destination();
d.readBytes(new ByteArrayInputStream(payload));
s = new I2PSocketImpl(d, this, false,
newLocalID);
s.setRemoteID(id);
if (_serverSocket.getNewSocket(s)) {
_inSockets.put(newLocalID, s);
byte[] packet = makePacket
((byte)0x51, id,
newLocalID.getBytes("ISO-8859-1"));
boolean replySentOk = false;
synchronized(_session) {
replySentOk = _session.sendMessage(d, packet);
}
if (!replySentOk) {
_log.error("Error sending reply to " +
d.calculateHash().toBase64() +
" in response to a new con message",
new Exception("Failed creation"));
s.internalClose();
}
} else {
byte[] packet =
(" "+id).getBytes("ISO-8859-1");
packet[0]=0x52;
boolean nackSent = session.sendMessage(d, packet);
if (!nackSent) {
_log.error("Error sending NACK for session creation");
}
s.internalClose();
}
return;
} else {
_log.error("Syn packet that has a payload not equal to the pubkey length (" + payload.length + " != " + PUBKEY_LENGTH + ")");
return;
}
case 0xA2: // disconnect incoming
_log.debug("*Disconnect incoming!");
try {
s = (I2PSocketImpl) _inSockets.get(id);
if (payload.length==0 && s != null) {
s.internalClose();
_inSockets.remove(id);
return;
} else {
if (payload.length > 0)
_log.warn("Disconnect packet had " + payload.length + " bytes");
return;
}
} catch (Exception t) {
_log.error("Ignoring error on disconnect", t);
return;
}
case 0xA0: // packet send incoming
_log.debug("*Packet send incoming [" + payload.length + "]");
s = (I2PSocketImpl) _inSockets.get(id);
if (s != null) {
s.queueData(payload);
return;
} else {
_log.error("Null socket with data available");
throw new IllegalStateException("Null socket with data available");
}
case 0xFF: // ignore
return;
}
_log.error("\n\n=============== Unknown packet! "+
"============"+
"\nType: "+(int)type+
"\nID: " + getReadableForm(id)+
"\nBase64'ed Data: "+Base64.encode(payload)+
"\n\n\n");
if (id != null) {
_inSockets.remove(id);
_outSockets.remove(id);
}
}
} catch (I2PException ise) {
_log.error("Error processing", ise);
} catch (IOException ioe) {
_log.error("Error processing", ioe);
} catch (IllegalStateException ise) {
_log.debug("Error processing", ise);
}
}
public void reportAbuse(I2PSession session, int severity) {
_log.error("Abuse reported [" + severity + "]");
}
public void setDefaultOptions(I2PSocketOptions options) { _defaultOptions = options; }
public I2PSocketOptions getDefaultOptions() { return _defaultOptions ; }
public I2PServerSocket getServerSocket() { return _serverSocket; }
/**
* Create a new connected socket (block until the socket is created)
*
* @throws I2PException if there is a problem connecting
*/
public I2PSocket connect(Destination peer, I2PSocketOptions options) throws I2PException {
String localID, lcID;
I2PSocketImpl s;
synchronized(lock) {
localID=makeID(_outSockets);
lcID=getReadableForm(localID);
s = new I2PSocketImpl(peer, this, true, localID);
_outSockets.put(s.getLocalID(),s);
}
try {
ByteArrayOutputStream pubkey = new ByteArrayOutputStream();
_session.getMyDestination().writeBytes(pubkey);
String remoteID;
byte[] packet = makePacket((byte)0xA1, localID,
pubkey.toByteArray());
boolean sent = false;
synchronized(_session) {
sent = _session.sendMessage(peer, packet);
}
if (!sent) {
_log.info("Unable to send & receive ack for SYN packet");
synchronized(lock) {
_outSockets.remove(s.getLocalID());
}
throw new I2PException("Unable to reach peer");
}
remoteID = s.getRemoteID(true, options.getConnectTimeout());
if ("".equals(remoteID)) {
throw new I2PException("Unable to reach peer");
}
_log.debug("TIMING: s given out for remoteID "+getReadableForm(remoteID));
return s;
} catch (InterruptedIOException ioe) {
_log.error("Timeout waiting for ack from syn for id " + getReadableForm(lcID), ioe);
synchronized(lock) {
_outSockets.remove(s.getLocalID());
}
throw new I2PException("Timeout waiting for ack");
} catch (IOException ex) {
_log.error("Error sending syn on id " + getReadableForm(lcID), ex);
synchronized(lock) {
_outSockets.remove(s.getLocalID());
}
throw new I2PException("IOException occurred");
} catch (I2PException ex) {
_log.info("Error sending syn on id " + getReadableForm(lcID), ex);
synchronized(lock) {
_outSockets.remove(s.getLocalID());
}
throw ex;
}
}
public I2PSocket connect(Destination peer) throws I2PException {
return connect(peer, null);
}
/**
* Retrieve a set of currently connected I2PSockets, either initiated locally or remotely.
*
*/
public Set listSockets() {
Set sockets = new HashSet(8);
synchronized (lock) {
sockets.addAll(_inSockets.values());
sockets.addAll(_outSockets.values());
}
return sockets;
}
/**
* Ping the specified peer, returning true if they replied to the ping within
* the timeout specified, false otherwise. This call blocks.
*
*/
public boolean ping(Destination peer, long timeoutMs) {
try {
return _session.sendMessage(peer, new byte[] {(byte)0xFF});
} catch (I2PException ex) {
_log.error("I2PException:",ex);
return false;
}
}
public void removeSocket(I2PSocketImpl sock) {
synchronized(lock) {
_inSockets.remove(sock.getLocalID());
_outSockets.remove(sock.getLocalID());
}
}
public static String getReadableForm(String id) {
try {
if (id.length() != 3) return "Bogus";
return Base64.encode(id.getBytes("ISO-8859-1"));
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
return null;
}
}
/**
* Create a new part the connection ID that is locally unique
*
* @param uniqueIn map of already known local IDs so we don't collide. WARNING - NOT THREADSAFE!
*/
public static String makeID(HashMap uniqueIn) {
String newID;
try {
do {
int id = (int)(Math.random()*16777215+1);
byte[] nid = new byte[3];
nid[0]=(byte)(id / 65536);
nid[1] = (byte)((id/256) % 256);
nid[2]= (byte)(id %256);
newID = new String(nid, "ISO-8859-1");
} while (uniqueIn.get(newID) != null);
return newID;
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
return null;
}
}
/**
* Create a new packet of the given type for the specified connection containing
* the given payload
*/
public static byte[] makePacket(byte type, String id, byte[] payload) {
try {
byte[] packet = new byte[payload.length+4];
packet[0]=type;
byte[] temp = id.getBytes("ISO-8859-1");
if (temp.length != 3)
throw new RuntimeException("Incorrect ID length: "+
temp.length);
System.arraycopy(temp,0,packet,1,3);
System.arraycopy(payload,0,packet,4,payload.length);
return packet;
} catch (UnsupportedEncodingException ex) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error building the packet", ex);
return new byte[0];
}
}
}

View File

@ -0,0 +1,86 @@
package net.i2p.client.streaming;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PClientFactory;
import net.i2p.client.I2PSessionException;
import net.i2p.I2PException;
import net.i2p.data.Destination;
import net.i2p.util.Log;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;
/**
* Simplify the creation of I2PSession and transient I2P Destination objects if
* necessary to create a socket manager. This class is most likely how classes
* will begin their use of the socket library
*
*/
public class I2PSocketManagerFactory {
private final static Log _log = new Log(I2PSocketManagerFactory.class);
/**
* Create a socket manager using a brand new destination connected to the
* I2CP router on the local machine on the default port (7654).
*
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager() {
return createManager("localhost", 7654, new Properties());
}
/**
* Create a socket manager using a brand new destination connected to the
* I2CP router on the given machine reachable through the given port.
*
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(String i2cpHost, int i2cpPort, Properties opts) {
I2PClient client = I2PClientFactory.createClient();
ByteArrayOutputStream keyStream = new ByteArrayOutputStream(512);
try {
Destination dest = client.createDestination(keyStream);
ByteArrayInputStream in = new ByteArrayInputStream(keyStream.toByteArray());
return createManager(in, i2cpHost, i2cpPort, opts);
} catch (IOException ioe) {
_log.error("Error creating the destination for socket manager", ioe);
return null;
} catch (I2PException ie) {
_log.error("Error creating the destination for socket manager", ie);
return null;
}
}
/**
* Create a socket manager using the destination loaded from the given private key
* stream and connected to the I2CP router on the specified machine on the given
* port
*
* @return the newly created socket manager, or null if there were errors
*/
public static I2PSocketManager createManager(InputStream myPrivateKeyStream, String i2cpHost, int i2cpPort, Properties opts) {
I2PClient client = I2PClientFactory.createClient();
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
opts.setProperty(I2PClient.PROP_TCP_PORT, ""+i2cpPort);
try {
I2PSession session = client.createSession(myPrivateKeyStream, opts);
session.connect();
return createManager(session);
} catch (I2PSessionException ise) {
_log.error("Error creating session for socket manager", ise);
return null;
}
}
private static I2PSocketManager createManager(I2PSession session) {
I2PSocketManager mgr = new I2PSocketManager();
mgr.setSession(session);
return mgr;
}
}

View File

@ -0,0 +1,21 @@
package net.i2p.client.streaming;
/**
* Define the configuration for streaming and verifying data on the socket.
* No options available...
*
*/
public class I2PSocketOptions {
private long _connectTimeout;
public I2PSocketOptions() {
_connectTimeout = -1;
}
/**
* How long we will wait for the ACK from a SYN, in milliseconds.
*
* @return milliseconds to wait, or -1 if we will wait indefinitely
*/
public long getConnectTimeout() { return _connectTimeout; }
public void setConnectTimeout(long ms) { _connectTimeout = ms; }
}

View File

@ -0,0 +1,10 @@
$Id$
the i2p/apps/phttprelay module is the root of the
PHTTPRelay application, and everything within it
is released according to the terms of the I2P
license policy. That means everything contained
within the i2p/apps/phttprelay module is released
into the public domain unless otherwise marked.
Alternate licenses that may be used include BSD,
Cryptix, and MIT.

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="phttprelay">
<target name="all" depends="clean, build" />
<target name="build" depends="builddep, jar" />
<target name="builddep">
<ant dir="../../../core/java/" target="build" />
</target>
<target name="compile">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac srcdir="./src" debug="true" destdir="./build/obj" classpath="../../../core/java/build/i2p.jar:lib/javax.servlet.jar" />
</target>
<target name="jar" depends="compile">
<war destfile="./build/phttprelay.war" webxml="web.xml">
<classes dir="./build/obj/">
<include name="**/*.class" />
</classes>
<lib dir="../../../core/java/build/">
<include name="i2p.jar" />
</lib>
</war>
</target>
<target name="javadoc">
<mkdir dir="./build" />
<mkdir dir="./build/javadoc" />
<javadoc
sourcepath="./src:../../../core/java/src" destdir="./build/javadoc"
classpath="./lib/javax.servlet.jar"
packagenames="*"
use="true"
splitindex="true"
windowtitle="I2P phttp relay" />
</target>
<target name="clean">
<delete dir="./build" />
</target>
<target name="cleandep" depends="clean">
<ant dir="../../../core/java/" target="cleandep" />
</target>
<target name="distclean" depends="clean">
<ant dir="../../../core/java/" target="distclean" />
</target>
</project>

View File

@ -0,0 +1,159 @@
<HTML>
<HEAD>
<TITLE>Jetty License</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<FONT FACE=ARIAL,HELVETICA>
<CENTER><FONT SIZE=+3><B>Jetty License</B></FONT></CENTER>
<CENTER><FONT SIZE=-1><B>$Revision: 1.1 $</B></FONT></CENTER>
<B>Preamble:</B><p>
The intent of this document is to state the conditions under which the
Jetty Package may be copied, such that the Copyright Holder maintains some
semblance of control over the development of the package, while giving the
users of the package the right to use, distribute and make reasonable
modifications to the Package in accordance with the goals and ideals of
the Open Source concept as described at
<A HREF="http://www.opensource.org">http://www.opensource.org</A>.
<P>
It is the intent of this license to allow commercial usage of the Jetty
package, so long as the source code is distributed or suitable visible
credit given or other arrangements made with the copyright holders.
<P><B>Definitions:</B><P>
<UL>
<LI> "Jetty" refers to the collection of Java classes that are
distributed as a HTTP server with servlet capabilities and
associated utilities.<p>
<LI> "Package" refers to the collection of files distributed by the
Copyright Holder, and derivatives of that collection of files
created through textual modification.<P>
<LI> "Standard Version" refers to such a Package if it has not been
modified, or has been modified in accordance with the wishes
of the Copyright Holder.<P>
<LI> "Copyright Holder" is whoever is named in the copyright or
copyrights for the package. <BR>
Mort Bay Consulting Pty. Ltd. (Australia) is the "Copyright
Holder" for the Jetty package.<P>
<LI> "You" is you, if you're thinking about copying or distributing
this Package.<P>
<LI> "Reasonable copying fee" is whatever you can justify on the
basis of media cost, duplication charges, time of people involved,
and so on. (You will not be required to justify it to the
Copyright Holder, but only to the computing community at large
as a market that must bear the fee.)<P>
<LI> "Freely Available" means that no fee is charged for the item
itself, though there may be fees involved in handling the item.
It also means that recipients of the item may redistribute it
under the same conditions they received it.<P>
</UL>
0. The Jetty Package is Copyright (c) Mort Bay Consulting Pty. Ltd.
(Australia) and others. Individual files in this package may contain
additional copyright notices. The javax.servlet packages are copyright
Sun Microsystems Inc. <P>
1. The Standard Version of the Jetty package is
available from <A HREF=http://jetty.mortbay.org>http://jetty.mortbay.org</A>.<P>
2. You may make and distribute verbatim copies of the source form
of the Standard Version of this Package without restriction, provided that
you include this license and all of the original copyright notices
and associated disclaimers.<P>
3. You may make and distribute verbatim copies of the compiled form of the
Standard Version of this Package without restriction, provided that you
include this license.<P>
4. You may apply bug fixes, portability fixes and other modifications
derived from the Public Domain or from the Copyright Holder. A Package
modified in such a way shall still be considered the Standard Version.<P>
5. You may otherwise modify your copy of this Package in any way, provided
that you insert a prominent notice in each changed file stating how and
when you changed that file, and provided that you do at least ONE of the
following:<P>
<BLOCKQUOTE>
a) Place your modifications in the Public Domain or otherwise make them
Freely Available, such as by posting said modifications to Usenet or
an equivalent medium, or placing the modifications on a major archive
site such as ftp.uu.net, or by allowing the Copyright Holder to include
your modifications in the Standard Version of the Package.<P>
b) Use the modified Package only within your corporation or organization.<P>
c) Rename any non-standard classes so the names do not conflict
with standard classes, which must also be provided, and provide
a separate manual page for each non-standard class that clearly
documents how it differs from the Standard Version.<P>
d) Make other arrangements with the Copyright Holder.<P>
</BLOCKQUOTE>
6. You may distribute modifications or subsets of this Package in source
code or compiled form, provided that you do at least ONE of the following:<P>
<BLOCKQUOTE>
a) Distribute this license and all original copyright messages, together
with instructions (in the about dialog, manual page or equivalent) on where
to get the complete Standard Version.<P>
b) Accompany the distribution with the machine-readable source of
the Package with your modifications. The modified package must include
this license and all of the original copyright notices and associated
disclaimers, together with instructions on where to get the complete
Standard Version.<P>
c) Make other arrangements with the Copyright Holder.<P>
</BLOCKQUOTE>
7. You may charge a reasonable copying fee for any distribution of this
Package. You may charge any fee you choose for support of this Package.
You may not charge a fee for this Package itself. However,
you may distribute this Package in aggregate with other (possibly
commercial) programs as part of a larger (possibly commercial) software
distribution provided that you meet the other distribution requirements
of this license.<P>
8. Input to or the output produced from the programs of this Package
do not automatically fall under the copyright of this Package, but
belong to whomever generated them, and may be sold commercially, and
may be aggregated with this Package.<P>
9. Any program subroutines supplied by you and linked into this Package
shall not be considered part of this Package.<P>
10. The name of the Copyright Holder may not be used to endorse or promote
products derived from this software without specific prior written
permission.<P>
11. This license may change with each release of a Standard Version of
the Package. You may choose to use the license associated with version
you are using or the license of the latest Standard Version.<P>
12. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.<P>
13. If any superior law implies a warranty, the sole remedy under such shall
be , at the Copyright Holders option either a) return of any price paid or
b) use or reasonable endeavours to repair or replace the software.<P>
14. This license shall be read under the laws of Australia. <P>
<center>The End</center>
<center><FONT size=-1>This license was derived from the <I>Artistic</I> license published
on <a href=http://www.opensource.org>http://www.opensource.com</a></font></center>
</FONT>

View File

@ -0,0 +1,6 @@
The file javax.servlet.jar is distributed under the terms of LICENSE.html,
which is the implementation of the java servlet classes as retrieved from
http://jetty.mortbay.org/jetty/
It is only included to assist in building the phttprelay.war file on hosts
that do not have a servlet container / implementation.

View File

@ -0,0 +1,113 @@
package net.i2p.phttprelay;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Check the status of previous message delivery, returning either pending or
* unknown, where pending means that particular message ID for that particular
* target is still on the server, and unknown means it has either not been created
* or it has been sent successfully. It does this by sending HTTP 204 (NO CONTENT)
* for pending, and HTTP 404 (NOT FOUND) for unknown. <p />
*
* This servlet should be set up in web.xml as follows:
*
* <servlet>
* <servlet-name>CheckSendStatus</servlet-name>
* <servlet-class>net.i2p.phttprelay.CheckSendStatusServlet</servlet-class>
* <init-param>
* <param-name>baseDir</param-name>
* <param-value>/usr/local/jetty/phttprelayDir</param-value>
* </init-param>
* </servlet>
*
* <servlet-mapping>
* <servlet-name>CheckSendStatus</servlet-name>
* <url-pattern>/phttpCheckSendStatus</url-pattern>
* </servlet-mapping>
*
* baseDir is the directory under which registrants and their pending messages are stored
*
*/
public class CheckSendStatusServlet extends PHTTPRelayServlet {
/* URL parameters on the check */
/** H(routerIdent).toBase64() of the target to receive the message */
public final static String PARAM_SEND_TARGET = "target";
/** msgId parameter */
public final static String PARAM_MSG_ID = "msgId";
public final static String PROP_STATUS = "status";
public final static String STATUS_PENDING = "pending";
public final static String STATUS_UNKNOWN = "unknown";
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String target = req.getParameter(PARAM_SEND_TARGET);
String msgIdStr = req.getParameter(PARAM_MSG_ID);
log("Checking status of [" + target + "] message [" + msgIdStr + "]");
if (!isKnownMessage(target, msgIdStr)) {
log("Not known - its not pending");
notPending(req, resp);
return;
} else {
log("Known - its still pending");
pending(req, resp);
return;
}
}
private boolean isKnownMessage(String target, String msgId) throws IOException {
if ( (target == null) || (target.trim().length() <= 0) ) return false;
if ( (msgId == null) || (msgId.trim().length() <= 0) ) return false;
File identDir = getIdentDir(target);
if (identDir.exists()) {
File identFile = new File(identDir, "identity.dat");
if (identFile.exists()) {
// known and valid (maybe we need to check the file format... naw, fuck it
File msgFile = new File(identDir, "msg" + msgId + ".dat");
if (msgFile.exists())
return true;
else
return false;
} else {
return false;
}
} else {
return false;
}
}
private void pending(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream out = resp.getOutputStream();
StringBuffer buf = new StringBuffer();
buf.append(PROP_STATUS).append('=').append(STATUS_PENDING).append('\n');
out.write(buf.toString().getBytes());
out.flush();
out.close();
}
private void notPending(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream out = resp.getOutputStream();
StringBuffer buf = new StringBuffer();
buf.append(PROP_STATUS).append('=').append(STATUS_UNKNOWN).append('\n');
out.write(buf.toString().getBytes());
out.flush();
out.close();
}
}

View File

@ -0,0 +1,40 @@
package net.i2p.phttprelay;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.util.HashSet;
import java.util.Set;
/**
* Lock identities for updating messages (so that they aren't read / deleted
* while being written)
*
*/
class LockManager {
private volatile static Set _locks = new HashSet(); // target
public static void lockIdent(String target) {
while (true) {
synchronized (_locks) {
if (!_locks.contains(target)) {
_locks.add(target);
return;
}
try { _locks.wait(1000); } catch (InterruptedException ie) {}
}
}
}
public static void unlockIdent(String target) {
synchronized (_locks) {
_locks.remove(target);
_locks.notifyAll();
}
}
}

View File

@ -0,0 +1,73 @@
package net.i2p.phttprelay;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import net.i2p.util.Log;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import java.io.File;
import java.io.IOException;
abstract class PHTTPRelayServlet extends HttpServlet {
private Log _log = new Log(getClass());
protected String _baseDir;
/* config params */
/*public final static String PARAM_BASEDIR = "baseDir";*/
public final static String ENV_BASEDIR = "phttpRelay.baseDir";
/** match the clock fudge factor on the router, rather than importing the entire router cvs module */
public final static long CLOCK_FUDGE_FACTOR = 1*60*1000;
protected String buildURL(HttpServletRequest req, String path) {
StringBuffer buf = new StringBuffer();
buf.append(req.getScheme()).append("://");
buf.append(req.getServerName()).append(":").append(req.getServerPort());
buf.append(req.getContextPath());
buf.append(path);
log("URL built: " + buf.toString());
return buf.toString();
}
protected File getIdentDir(String target) throws IOException {
if ( (_baseDir == null) || (target == null) ) throw new IOException("dir not specified to deal with");
File baseDir = new File(_baseDir);
if (!baseDir.exists()) {
boolean created = baseDir.mkdirs();
log("Creating PHTTP Relay Base Directory: " + baseDir.getAbsolutePath() + " - ok? " + created);
}
File identDir = new File(baseDir, target);
log("Ident dir: " + identDir.getAbsolutePath());
return identDir;
}
public void init(ServletConfig config) throws ServletException {
super.init(config);
String dir = System.getProperty(ENV_BASEDIR);
if (dir == null) {
_log.warn("Base directory for the polling http relay system not in the environment [" + ENV_BASEDIR +"]");
_log.warn("Setting the base directory to ./relayDir for " + getServletName());
_baseDir = ".relayDir";
} else {
_baseDir = dir;
log("Loaded up " + getServletName() + " with base directory " + _baseDir);
}
}
public void log(String msg) {
_log.debug(msg);
}
public void log(String msg, Throwable t) {
_log.debug(msg, t);
}
}

View File

@ -0,0 +1,263 @@
package net.i2p.phttprelay;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.i2p.crypto.DSAEngine;
import net.i2p.data.Base64;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.RouterIdentity;
import net.i2p.data.Signature;
import net.i2p.util.Clock;
/**
* Handle poll requests for new messages - checking the poll request for a valid signature,
* sending back all of the messages found, and after all messages are written out, delete
* them from the local store. If the signature fails, it sends back an HTTP 403 (UNAUTHORIZED).
* If the target is not registered, it sends back an HTTP 404 (NOT FOUND) <p />
*
* This servlet should be set up in web.xml as follows:
*
* <servlet>
* <servlet-name>Poll</servlet-name>
* <servlet-class>net.i2p.phttprelay.PollServlet</servlet-class>
* <init-param>
* <param-name>baseDir</param-name>
* <param-value>/usr/local/jetty/phttprelayDir</param-value>
* </init-param>
* </servlet>
*
* <servlet-mapping>
* <servlet-name>Poll</servlet-name>
* <url-pattern>/phttpPoll</url-pattern>
* </servlet-mapping>
*
* baseDir is the directory under which registrants and their pending messages are stored
*
*/
public class PollServlet extends PHTTPRelayServlet {
/* URL parameters on the check */
/** H(routerIdent).toBase64() of the target to receive the message */
public final static String PARAM_SEND_TARGET = "target";
/** HTTP error code if the target is not known*/
public final static int CODE_UNKNOWN = HttpServletResponse.SC_NOT_FOUND;
/** HTTP error code if the signature failed */
public final static int CODE_UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED;
/** HTTP error code if everything is ok */
public final static int CODE_OK = HttpServletResponse.SC_OK;
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
byte data[] = getData(req);
if (data == null) return;
ByteArrayInputStream bais = new ByteArrayInputStream(data);
String target = getTarget(bais);
if (target == null) {
log("Target not specified");
resp.sendError(CODE_UNKNOWN);
return;
}
if (!isKnown(target)) {
resp.sendError(CODE_UNKNOWN);
return;
}
if (!isAuthorized(target, bais)) {
resp.sendError(CODE_UNAUTHORIZED);
return;
} else {
log("Authorized access for target " + target);
}
sendMessages(resp, target);
}
private byte[] getData(HttpServletRequest req) throws ServletException, IOException {
ServletInputStream in = req.getInputStream();
int len = req.getContentLength();
byte data[] = new byte[len];
int cur = 0;
int read = DataHelper.read(in, data);
if (read != len) {
log("Size read is incorrect [" + read + " instead of expected " + len + "]");
return null;
} else {
log("Read data length: " + data.length + " in base64: " + Base64.encode(data));
return data;
}
}
private String getTarget(InputStream in) throws IOException {
StringBuffer buf = new StringBuffer(64);
int numBytes = 0;
int c = 0;
while ( (c = in.read()) != -1) {
if (c == (int)'&') break;
buf.append((char)c);
numBytes++;
if (numBytes > 128) {
log("Target didn't find the & after 128 bytes [" + buf.toString() + "]");
return null;
}
}
if (buf.toString().indexOf("target=") != 0) {
log("Did not start with target= [" + buf.toString() + "]");
return null;
}
return buf.substring("target=".length());
}
private void sendMessages(HttpServletResponse resp, String target) throws IOException {
log("Before lock " + target);
LockManager.lockIdent(target);
log("Locked " + target);
try {
File identDir = getIdentDir(target);
expire(identDir);
File messageFiles[] = identDir.listFiles();
resp.setStatus(CODE_OK);
log("Sending back " + (messageFiles.length -1) + " messages");
ServletOutputStream out = resp.getOutputStream();
DataHelper.writeDate(out, new Date(Clock.getInstance().now()));
DataHelper.writeLong(out, 2, messageFiles.length -1);
for (int i = 0; i < messageFiles.length; i++) {
if ("identity.dat".equals(messageFiles[i].getName())) {
// skip
} else {
log("Message file " + messageFiles[i].getName() + " is " + messageFiles[i].length() + " bytes");
DataHelper.writeLong(out, 4, messageFiles[i].length());
writeFile(out, messageFiles[i]);
boolean deleted = messageFiles[i].delete();
if (!deleted) {
log("!!!Error removing message file " + messageFiles[i].getAbsolutePath() + " - please delete!");
}
}
}
out.flush();
out.close();
} catch (DataFormatException dfe) {
log("Error sending message", dfe);
} finally {
LockManager.unlockIdent(target);
log("Unlocked " + target);
}
}
private final static long EXPIRE_DELAY = 60*1000; // expire messages every minute
private void expire(File identDir) throws IOException {
File files[] = identDir.listFiles();
long now = System.currentTimeMillis();
for (int i = 0 ; i < files.length; i++) {
if ("identity.dat".equals(files[i].getName())) {
continue;
}
if (files[i].lastModified() + EXPIRE_DELAY < now) {
log("Expiring " + files[i].getAbsolutePath());
files[i].delete();
}
}
}
private void writeFile(ServletOutputStream out, File file) throws IOException {
FileInputStream fis = new FileInputStream(file);
try {
byte buf[] = new byte[4096];
while (true) {
int read = DataHelper.read(fis, buf);
if (read > 0)
out.write(buf, 0, read);
else
break;
}
} finally {
fis.close();
}
}
private boolean isKnown(String target) throws IOException {
File identDir = getIdentDir(target);
if (identDir.exists()) {
File identFile = new File(identDir, "identity.dat");
if (identFile.exists()) {
// known and valid (maybe we need to check the file format... naw, fuck it
return true;
} else {
return false;
}
} else {
return false;
}
}
private boolean isAuthorized(String target, InputStream in) throws IOException {
RouterIdentity ident = null;
try {
ident = getRouterIdentity(target);
} catch (DataFormatException dfe) {
log("Identity was not valid", dfe);
}
if (ident == null) {
log("Identity not registered");
return false;
}
try {
long val = DataHelper.readLong(in, 4);
Signature sig = new Signature();
sig.readBytes(in);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataHelper.writeLong(baos, 4, val);
if (DSAEngine.getInstance().verifySignature(sig, baos.toByteArray(), ident.getSigningPublicKey())) {
return true;
} else {
log("Signature does NOT match");
return false;
}
} catch (DataFormatException dfe) {
log("Format error reading the nonce and signature", dfe);
return false;
}
}
private RouterIdentity getRouterIdentity(String target) throws IOException, DataFormatException {
File identDir = getIdentDir(target);
if (identDir.exists()) {
File identFile = new File(identDir, "identity.dat");
if (identFile.exists()) {
// known and valid (maybe we need to check the file format... naw, fuck it
RouterIdentity ident = new RouterIdentity();
ident.readBytes(new FileInputStream(identFile));
return ident;
} else {
return null;
}
} else {
return null;
}
}
}

View File

@ -0,0 +1,154 @@
package net.i2p.phttprelay;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.RouterIdentity;
import net.i2p.util.Clock;
/**
* Accept registrations for PHTTP relaying, allowing the Polling HTTP (PHTTP)
* transport for I2P to bridge past firewalls, NATs, and proxy servers. <p />
*
* This servlet should be set up in web.xml as follows:
*
* <servlet>
* <servlet-name>Register</servlet-name>
* <servlet-class>net.i2p.phttprelay.RegisterServlet</servlet-class>
* <init-param>
* <param-name>baseDir</param-name>
* <param-value>/usr/local/jetty/phttprelayDir</param-value>
* </init-param>
* <init-param>
* <param-name>pollPath</param-name>
* <param-value>phttpPoll</param-value>
* </init-param>
* <init-param>
* <param-name>sendPath</param-name>
* <param-value>phttpSend</param-value>
* </init-param>
* </servlet>
*
* <servlet-mapping>
* <servlet-name>Register</servlet-name>
* <url-pattern>/phttpRegister</url-pattern>
* </servlet-mapping>
*
* baseDir is the directory under which registrants and their pending messages are stored
* pollPath is the path under the current host that requests polling for messages should be sent
* sendPath is the path under the current host that requests submitting messages should be sent
*
* The pollPath and sendPath must not start with / as they are translated ala http://host:port/[path]
*/
public class RegisterServlet extends PHTTPRelayServlet {
private String _pollPath;
private String _sendPath;
/* config params */
public final static String PARAM_POLL_PATH = "pollPath";
public final static String PARAM_SEND_PATH = "sendPath";
/* key=val keys sent back on registration */
public final static String PROP_STATUS = "status";
public final static String PROP_POLL_URL = "pollURL";
public final static String PROP_SEND_URL = "sendURL";
public final static String PROP_TIME_OFFSET = "timeOffset"; // ms (local-remote)
/* values for the PROP_STATUS */
public final static String STATUS_FAILED = "failed";
public final static String STATUS_REGISTERED = "registered";
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream in = req.getInputStream();
RouterIdentity ident = new RouterIdentity();
try {
Date remoteTime = DataHelper.readDate(in);
long skew = getSkew(remoteTime);
ident.readBytes(in);
boolean ok = registerIdent(ident);
sendURLs(req, resp, skew, ok);
} catch (DataFormatException dfe) {
log("Invalid format for router identity posted", dfe);
} finally {
in.close();
}
}
private long getSkew(Date remoteDate) {
if (remoteDate == null) {
log("*ERROR: remote date was null");
return Long.MAX_VALUE;
} else {
long diff = Clock.getInstance().now() - remoteDate.getTime();
return diff;
}
}
private boolean registerIdent(RouterIdentity ident) throws DataFormatException, IOException {
File identDir = getIdentDir(ident.getHash().toBase64());
boolean created = identDir.mkdirs();
File identFile = new File(identDir, "identity.dat");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(identFile);
ident.writeBytes(fos);
} finally {
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
}
log("Identity registered into " + identFile.getAbsolutePath());
return true;
}
private void sendURLs(HttpServletRequest req, HttpServletResponse resp, long skew, boolean ok) throws IOException {
ServletOutputStream out = resp.getOutputStream();
log("*Debug: clock skew of " + skew + "ms (local-remote)");
StringBuffer buf = new StringBuffer();
if (ok) {
buf.append(PROP_POLL_URL).append("=").append(buildURL(req, _pollPath)).append("\n");
buf.append(PROP_SEND_URL).append("=").append(buildURL(req, _sendPath)).append("\n");
buf.append(PROP_TIME_OFFSET).append("=").append(skew).append("\n");
buf.append(PROP_STATUS).append("=").append(STATUS_REGISTERED).append("\n");
} else {
buf.append(PROP_TIME_OFFSET).append("=").append(skew).append("\n");
buf.append(PROP_STATUS).append("=").append(STATUS_FAILED).append("\n");
}
out.write(buf.toString().getBytes());
out.close();
}
public void init(ServletConfig config) throws ServletException {
super.init(config);
String pollPath = config.getInitParameter(PARAM_POLL_PATH);
if (pollPath == null)
throw new ServletException("Polling path for the registration servlet required [" + PARAM_POLL_PATH + "]");
else
_pollPath = pollPath;
String sendPath = config.getInitParameter(PARAM_SEND_PATH);
if (sendPath == null)
throw new ServletException("Sending path for the registration servlet required [" + PARAM_SEND_PATH + "]");
else
_sendPath = sendPath;
}
}

View File

@ -0,0 +1,318 @@
package net.i2p.phttprelay;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Accept messages for PHTTP relaying, allowing the Polling HTTP (PHTTP)
* transport for I2P to bridge past firewalls, NATs, and proxy servers. This
* delivers them into the queue, returning HTTP 201 (created) if the queue is
* known, as well as the URL at which requests can be made to check the delivery
* status of the message. If the queue is not known, HTTP 410 (resource gone) is
* sent back. <p />
*
* This servlet should be set up in web.xml as follows:
*
* <servlet>
* <servlet-name>Send</servlet-name>
* <servlet-class>net.i2p.phttprelay.SendServlet</servlet-class>
* <init-param>
* <param-name>baseDir</param-name>
* <param-value>/usr/local/jetty/phttprelayDir</param-value>
* </init-param>
* <init-param>
* <param-name>checkPath</param-name>
* <param-value>phttpCheckStatus</param-value>
* </init-param>
* <init-param>
* <param-name>maxMessagesPerIdent</param-name>
* <param-value>100</param-value>
* </init-param>
* </servlet>
*
* <servlet-mapping>
* <servlet-name>Send</servlet-name>
* <url-pattern>/phttpSend</url-pattern>
* </servlet-mapping>
*
* baseDir is the directory under which registrants and their pending messages are stored
* checkPath is the path under the current host that requests for the status of delivery should be sent
* maxMessagesPerIdent is the maximum number of outstanding messages per peer being relayed
*
* The checkPath must not start with / as they are translated ala http://host:port/[path]
*/
public class SendServlet extends PHTTPRelayServlet {
private String _checkPath;
private int _maxMessagesPerIdent;
/* config params */
public final static String PARAM_CHECK_PATH = "checkPath";
public final static String PARAM_MAX_MESSAGES_PER_IDENT = "maxMessagesPerIdent";
/* URL parameters on the send */
/** H(routerIdent).toBase64() of the target to receive the message */
public final static String PARAM_SEND_TARGET = "target";
/** # ms to wait for the message to be delivered before failing it */
public final static String PARAM_SEND_TIMEOUTMS = "timeoutMs";
/** # bytes to be sent in the message */
public final static String PARAM_SEND_DATA_LENGTH = "dataLength";
/** sending router's time in ms */
public final static String PARAM_SEND_TIME = "localTime";
/** msgId parameter to access the check path servlet with (along side PARAM_SEND_TARGET) */
public final static String PARAM_MSG_ID = "msgId";
/* key=val keys sent back on registration */
public final static String PROP_CHECK_URL = "statusCheckURL";
public final static String PROP_STATUS = "status";
public final static String STATUS_OK = "accepted";
public final static String STATUS_UNKNOWN = "unknown";
private final static String STATUS_CLOCKSKEW = "clockSkew_"; /** prefix for (local-remote) */
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream in = req.getInputStream();
try {
int contentLen = req.getContentLength();
String firstLine = getFirstLine(in, contentLen);
if (firstLine == null) {
return;
}
Map params = getParameters(firstLine);
String target = (String)params.get(PARAM_SEND_TARGET);
String timeoutStr = (String)params.get(PARAM_SEND_TIMEOUTMS);
String lenStr = (String)params.get(PARAM_SEND_DATA_LENGTH);
String remoteTimeStr = (String)params.get(PARAM_SEND_TIME);
long skew = 0;
try {
long remTime = Long.parseLong(remoteTimeStr);
skew = System.currentTimeMillis() - remTime;
} catch (Throwable t) {
skew = Long.MAX_VALUE;
log("*ERROR could not parse the remote time from [" + remoteTimeStr + "]");
}
log("Target [" + target + "] timeout [" + timeoutStr + "] length [" + lenStr + "] skew [" + skew + "]");
if ( (skew > CLOCK_FUDGE_FACTOR) || (skew < 0 - CLOCK_FUDGE_FACTOR) ) {
log("Attempt to send by a skewed router: skew = " + skew + "ms (local-remote)");
failSkewed(req, resp, skew);
}
if (!isValidTarget(target)) {
log("Attempt to send to an invalid target [" + target + "]");
fail(req, resp, "Unknown or invalid target");
return;
}
long len = -1;
try {
len = Long.parseLong(lenStr);
} catch (Throwable t) {
log("Unable to parse length parameter [" + PARAM_SEND_DATA_LENGTH + "] (" + lenStr + ")");
fail(req, resp, "Invalid length parameter");
return;
}
int msgId = saveFile(in, resp, target, len);
if (msgId >= 0) {
sendSuccess(req, resp, target, msgId);
} else {
fail(req, resp, "Unable to queue up the message for delivery");
}
} finally {
try { in.close(); } catch (IOException ioe) {}
}
}
private String getFirstLine(ServletInputStream in, int len) throws ServletException, IOException {
StringBuffer buf = new StringBuffer(128);
int numBytes = 0;
int c = 0;
while ( (c = in.read()) != -1) {
if (c == (int)'\n') break;
buf.append((char)c);
numBytes++;
if (numBytes > 512) {
log("First line is > 512 bytes [" + buf.toString() + "]");
return null;
}
}
log("First line: " + buf.toString());
return buf.toString();
}
private static Map getParameters(String line) {
//StringTokenizer tok = new StringTokenizer(line, "&=", true);
Map params = new HashMap();
while (line != null) {
String key = null;
String val = null;
int firstAmp = line.indexOf('&');
int firstEq = line.indexOf('=');
if (firstAmp > 0) {
key = line.substring(0, firstEq);
val = line.substring(firstEq+1, firstAmp);
line = line.substring(firstAmp+1);
params.put(key, val);
} else {
line = null;
}
}
return params;
}
private boolean isValidTarget(String target) throws IOException {
File identDir = getIdentDir(target);
if (identDir.exists()) {
File identFile = new File(identDir, "identity.dat");
if (identFile.exists()) {
// known and valid (maybe we need to check the file format... naw, fuck it
String files[] = identDir.list();
// we skip 1 because of identity.dat
if (files.length -1 > _maxMessagesPerIdent) {
log("Too many messages pending for " + target + ": " + (files.length-1));
return false;
} else {
return true;
}
} else {
log("Ident directory exists, but identity does not... corrupt for " + target);
return false;
}
} else {
log("Unknown ident " + target);
return false;
}
}
private int saveFile(InputStream in, HttpServletResponse resp, String target, long len) throws IOException {
File identDir = getIdentDir(target);
if (!identDir.exists()) return -1;
try {
LockManager.lockIdent(target);
int i = 0;
while (true) {
File curFile = new File(identDir, "msg" + i + ".dat");
if (!curFile.exists()) {
boolean ok = writeFile(curFile, in, len);
if (ok)
return i;
else
return -1;
}
i++;
continue;
}
} finally {
LockManager.unlockIdent(target);
}
}
private boolean writeFile(File file, InputStream in, long len) throws IOException {
long remaining = len;
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
byte buf[] = new byte[4096];
while (remaining > 0) {
int read = in.read(buf);
if (read == -1)
break;
remaining -= read;
if (read > 0)
fos.write(buf, 0, read);
}
} finally {
if (fos != null) {
try { fos.close(); } catch (IOException ioe) {}
}
if (remaining != 0) {
log("Invalid remaining bytes [" + remaining + " out of " + len + "] - perhaps message was cancelled partway through delivery? deleting " + file.getAbsolutePath());
boolean deleted = file.delete();
if (!deleted)
log("!!!Error deleting temporary file " + file.getAbsolutePath());
return false;
}
}
return true;
}
private void sendSuccess(HttpServletRequest req, HttpServletResponse resp, String target, int msgId) throws IOException {
ServletOutputStream out = resp.getOutputStream();
StringBuffer buf = new StringBuffer();
buf.append(PROP_STATUS).append('=').append(STATUS_OK).append('\n');
buf.append(PROP_CHECK_URL).append('=').append(buildURL(req, _checkPath));
buf.append('?');
buf.append(PARAM_SEND_TARGET).append('=').append(target).append("&");
buf.append(PARAM_MSG_ID).append('=').append(msgId).append("\n");
out.write(buf.toString().getBytes());
out.flush();
}
private void fail(HttpServletRequest req, HttpServletResponse resp, String err) throws IOException {
ServletOutputStream out = resp.getOutputStream();
StringBuffer buf = new StringBuffer();
buf.append(PROP_STATUS).append('=').append(STATUS_UNKNOWN).append('\n');
out.write(buf.toString().getBytes());
out.flush();
}
private void failSkewed(HttpServletRequest req, HttpServletResponse resp, long skew) throws IOException {
ServletOutputStream out = resp.getOutputStream();
StringBuffer buf = new StringBuffer();
buf.append(PROP_STATUS).append('=').append(STATUS_CLOCKSKEW).append(skew).append('\n');
out.write(buf.toString().getBytes());
out.flush();
}
public void init(ServletConfig config) throws ServletException {
super.init(config);
String checkPath = config.getInitParameter(PARAM_CHECK_PATH);
if (checkPath == null)
throw new ServletException("Check status path for the sending servlet required [" + PARAM_CHECK_PATH + "]");
else
_checkPath = checkPath;
String maxMessagesPerIdentStr = config.getInitParameter(PARAM_MAX_MESSAGES_PER_IDENT);
if (maxMessagesPerIdentStr == null)
throw new ServletException("Max messages per ident for the sending servlet required [" + PARAM_MAX_MESSAGES_PER_IDENT + "]");
try {
_maxMessagesPerIdent = Integer.parseInt(maxMessagesPerIdentStr);
} catch (Throwable t) {
throw new ServletException("Valid max messages per ident for the sending servlet required [" + PARAM_MAX_MESSAGES_PER_IDENT + "]");
}
}
public static void main(String args[]) {
String line = "target=pp0ARjQiB~IKC-0FsMUsPEMrwR3gxVBZGRYfEr1IzHI=&timeoutMs=52068&dataLength=2691&";
Map props = getParameters(line);
for (java.util.Iterator iter = props.keySet().iterator(); iter.hasNext(); ) {
String key = (String)iter.next();
String val = (String)props.get(key);
System.out.println("[" + key + "] = [" + val + "]");
}
}
}

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>I2P Polling HTTP Relay</display-name>
<servlet>
<servlet-name>Register</servlet-name>
<servlet-class>net.i2p.phttprelay.RegisterServlet</servlet-class>
<init-param>
<param-name>pollPath</param-name>
<param-value>/phttpPoll</param-value>
</init-param>
<init-param>
<param-name>sendPath</param-name>
<param-value>/phttpSend</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Send</servlet-name>
<servlet-class>net.i2p.phttprelay.SendServlet</servlet-class>
<init-param>
<param-name>checkPath</param-name>
<param-value>/phttpCheckSendStatus</param-value>
</init-param>
<init-param>
<param-name>maxMessagesPerIdent</param-name>
<param-value>100</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>CheckSendStatus</servlet-name>
<servlet-class>net.i2p.phttprelay.CheckSendStatusServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Poll</servlet-name>
<servlet-class>net.i2p.phttprelay.PollServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Register</servlet-name>
<url-pattern>/phttpRegister</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Send</servlet-name>
<url-pattern>/phttpSend</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>CheckSendStatus</servlet-name>
<url-pattern>/phttpCheckSendStatus</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Poll</servlet-name>
<url-pattern>/phttpPoll</url-pattern>
</servlet-mapping>
</web-app>

278
apps/tests/COPYING Normal file
View File

@ -0,0 +1,278 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

View File

@ -0,0 +1,44 @@
/*
* A Minimal echo server.
*
* Copyright (c) 2004 Michael Schierl
*
* Licensed unter GNU General Public License.
*/
import java.io.*;
import java.net.*;
public class EchoServer extends Thread {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(Integer.parseInt(args[0]));
while (true) {
Socket s = ss.accept();
new EchoServer(s);
}
}
private Socket s;
public EchoServer(Socket s) {
this.s=s;
start();
}
public void run() {
try {
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
byte[] b = new byte[4096];
int len;
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
} catch (SocketException ex) {
// nothing
} catch (IOException ex) {
ex.printStackTrace();
}
}
}

View File

@ -0,0 +1,106 @@
// compile & run this file against i2p.jar
import java.io.*;
import java.util.*;
import net.i2p.*;
import net.i2p.client.*;
import net.i2p.data.*;
public class GuaranteedBug {
public void reproduce() {
try {
Destination d1 = null;
// first client (receiver)
if (true) { // smaller scope for variables ...
I2PClient client = I2PClientFactory.createClient();
ByteArrayOutputStream keyStream =
new ByteArrayOutputStream(512);
d1 = client.createDestination(keyStream);
ByteArrayInputStream in =
new ByteArrayInputStream(keyStream.toByteArray());
Properties opts = new Properties();
opts.setProperty(I2PClient.PROP_RELIABILITY,
I2PClient.PROP_RELIABILITY_GUARANTEED);
opts.setProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
opts.setProperty(I2PClient.PROP_TCP_PORT, "7654");
I2PSession session = client.createSession(in, opts);
session.connect();
session.setSessionListener(new PacketCounter());
}
// second client (sender)
I2PClient client = I2PClientFactory.createClient();
ByteArrayOutputStream keyStream = new ByteArrayOutputStream(512);
Destination d2 = client.createDestination(keyStream);
ByteArrayInputStream in =
new ByteArrayInputStream(keyStream.toByteArray());
Properties opts = new Properties();
opts.setProperty(I2PClient.PROP_RELIABILITY,
I2PClient.PROP_RELIABILITY_GUARANTEED);
opts.setProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
opts.setProperty(I2PClient.PROP_TCP_PORT, "7654");
I2PSession session = client.createSession(in, opts);
session.connect();
session.setSessionListener(new DummyListener());
for (int i=0;i<1000; i++) {
byte[] msg = (""+i).getBytes("ISO-8859-1");
session.sendMessage(d1,msg);
System.out.println(">>"+i);
}
} catch (IOException ex) {
ex.printStackTrace();
} catch (I2PException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
new GuaranteedBug().reproduce();
}
// -------------------------------------------------------
public class DummyListener implements I2PSessionListener {
public void disconnected(I2PSession session) {
System.err.println("Disconnected: "+session);
}
public void errorOccurred(I2PSession session, String message,
Throwable error) {
System.err.println("Error: "+session+"/"+message);
error.printStackTrace();
}
public void messageAvailable(I2PSession session, int msgId,
long size) {
System.err.println("Message here? "+session);
}
public void reportAbuse(I2PSession session, int severity) {
System.err.println("Abuse: "+severity+"/"+session);
}
}
public class PacketCounter extends DummyListener {
private int lastPacket = -1;
public void messageAvailable(I2PSession session, int msgId,
long size) {
try {
byte msg[] = session.receiveMessage(msgId);
String m = new String(msg, "ISO-8859-1");
int no = Integer.parseInt(m);
if (no != ++lastPacket) {
System.out.println("ERROR: <<"+no);
} else {
System.out.println("<<"+no);
}
} catch (NumberFormatException ex) {
ex.printStackTrace();
} catch (I2PException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}

6
apps/tests/README Normal file
View File

@ -0,0 +1,6 @@
This directory is intended for tests which are useful for testing
I2P, but don't test any of the I2P components directly. Instead,
tests are run on "application" level (TCP, IRC, HTTP etc.).
IOW: These tests may be useful for any other project that allows
tunneling of "normal" protocols, not only I2P.

View File

@ -0,0 +1,94 @@
/**
* A basic implementation for the EchoTestAnalyzer.
*/
public class BasicEchoTestAnalyzer implements EchoTestAnalyzer {
/**
* How many events must appear until a detailed report is
* printed. Default is every 20 events.
*/
private static int REPORT_DELAY = 20;
private static int SUMMARY_SIZE = 100;
public BasicEchoTestAnalyzer() {
this(20, 100);
}
public BasicEchoTestAnalyzer(int reportDelay, int summarySize) {
REPORT_DELAY = reportDelay;
SUMMARY_SIZE = summarySize;
}
private int events = 0,
packetLosses = 0,
packetLossesDisconnect=0,
disconnects = 0,
disconnectsRefused = 0,
delayCount=0,
lastDelayPtr = 0;
private long minDelay=Long.MAX_VALUE, maxDelay = 0, delaySum=0;
private long[] lastDelays = new long[SUMMARY_SIZE];
public synchronized void packetLossOccurred(boolean beforeDisconnect) {
System.out.println("1: Packet lost"+
(beforeDisconnect?" before disconnect":"")+
".");
packetLosses++;
if (beforeDisconnect) packetLossesDisconnect++;
countEvent();
}
public synchronized void successOccurred(long delay) {
System.out.println("0: Delay = "+delay);
if (delay > maxDelay) maxDelay=delay;
if (delay < minDelay) minDelay=delay;
delaySum+=delay;
delayCount++;
lastDelays[lastDelayPtr++]=delay;
lastDelayPtr%=SUMMARY_SIZE;
countEvent();
}
public synchronized void disconnected(boolean refused) {
System.out.println("2: Disconnected"+
(refused?" (connection refused)":"")+
".");
disconnects++;
if (refused) disconnectsRefused++;
countEvent();
}
private void countEvent() {
events++;
if (events % REPORT_DELAY == 0) {
int packets = packetLosses+delayCount;
long delaySummary=0;
for (int i=0;i<SUMMARY_SIZE;i++) {
delaySummary+=lastDelays[i];
}
System.out.println
("++++++++++++++++ ECHO STATISTICS +++++++++++++++++++++++++"+
"\n++ Number of total echo messages: "+packets+
"\n++ No response for "+packetLosses+
"\n++ (of which "+ packetLossesDisconnect+
" due to a disconnect)"+
"\n++ Disconnects: "+disconnects+
"\n++ (of which "+disconnectsRefused+
" due to 'connection refused')"+
(disconnects>0 || true
?"\n++ Average lost packets per disconnect: "+
(packetLossesDisconnect/(float)disconnects)
:"")+
"\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++"+
"\n++ Minimal delay: "+minDelay+
"\n++ Average delay: "+(delaySum/(float)delayCount)+
"\n++ Maximal delay: "+maxDelay+
(delayCount >=SUMMARY_SIZE
?"\n++ Average delay over last " + SUMMARY_SIZE + ": "+(delaySummary/(float)SUMMARY_SIZE)
:"")+
"\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
}
}
}

View File

@ -0,0 +1,19 @@
/**
* A class that wants to analyze tests implements this interface. This
* allows to "mix" several test values (from different echo servers)
* as well as different algorithms for analyzing the data (for
* jrandom: Strategy Pattern *g*).
*/
public interface EchoTestAnalyzer {
public void packetLossOccurred(boolean beforeDisconnect);
public void successOccurred(long delay);
public void disconnected(boolean refused);
}

View File

@ -0,0 +1,170 @@
/*
* Test for an echo server. This test is intended to be used via an
* I2PTunnel, but should work as well on other networks that provide
* TCP tunneling and an echo server.
*
* Copyright (c) 2004 Michael Schierl
*
* Licensed unter GNU General Public License.
*/
import java.io.*;
import java.net.*;
/**
* The main engine for the EchoTester.
*/
public class EchoTester extends Thread {
/**
* How long to wait between packets. Default is 6 seconds.
*/
private static long PACKET_DELAY= 6000;
/**
* How many packets may be on the way before the connection is
* seen as "broken" and disconnected.
*/
private static final long MAX_PACKETS_QUEUED=50; // unused
private EchoTestAnalyzer eta;
private String host;
private int port;
// the following vars are synchronized via the lock.
private Object lock = new Object();
private long nextPacket=0;
private long nextUnreceived=0;
private boolean readerRunning=false;
public static void main(String[] args) {
if (args.length == 3)
PACKET_DELAY = Long.parseLong(args[2]);
new EchoTester(args[0], Integer.parseInt(args[1]),
new BasicEchoTestAnalyzer());
}
public EchoTester(String host, int port, EchoTestAnalyzer eta) {
this.eta=eta;
this.host=host;
this.port=port;
start();
}
public void run() {
try {
while (true) {
Socket s;
try {
s = new Socket(host, port);
} catch (ConnectException ex) {
eta.disconnected(true);
Thread.sleep(PACKET_DELAY);
continue;
}
System.out.println("41: Connected to "+host+":"+port);
synchronized(lock) {
nextUnreceived=nextPacket;
}
Thread t = new ResponseReaderThread(s);
Writer w = new BufferedWriter(new OutputStreamWriter
(s.getOutputStream()));
while (true) {
long no;
synchronized(lock) {
no = nextPacket++;
}
try {
w.write(no+" "+System.currentTimeMillis()+"\n");
w.flush();
} catch (SocketException ex) {
break;
}
Thread.sleep(PACKET_DELAY);
}
s.close();
t.join();
synchronized(lock) {
if (readerRunning) {
System.out.println("*** WHY IS THIS THREAD STILL"+
" RUNNING?");
}
while (nextUnreceived < nextPacket) {
nextUnreceived++;
eta.packetLossOccurred(true);
}
if (nextUnreceived > nextPacket) {
System.out.println("*** WTF? "+nextUnreceived+" > "+
nextPacket);
}
}
eta.disconnected(false);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
System.exit(1); // treat these errors as fatal
} catch (IOException ex) {
ex.printStackTrace();
System.exit(1); // treat these errors as fatal
}
}
private class ResponseReaderThread extends Thread {
private Socket s;
public ResponseReaderThread(Socket s) {
this.s=s;
synchronized(lock) {
readerRunning=true;
}
start();
}
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader
(s.getInputStream()));
String line;
int index;
while ((line=br.readLine()) != null) {
if ((index=line.indexOf(" ")) == -1)
continue;
long now, packetNumber, packetTime;
now = System.currentTimeMillis();
try {
packetNumber = Long.parseLong
(line.substring(0,index));
packetTime = Long.parseLong
(line.substring(index+1));
} catch (NumberFormatException ex) {
System.out.println(ex.toString());
continue;
}
synchronized (lock) {
while (packetNumber > nextUnreceived) {
nextUnreceived++;
eta.packetLossOccurred(false);
}
if (nextUnreceived > packetNumber) {
System.out.println("*** DOUBLE PACKET!");
} else {
nextUnreceived++;
}
}
eta.successOccurred(now-packetTime);
}
} catch (SocketException ex) {
// ignore
} catch (IOException ex) {
ex.printStackTrace();
System.exit(0);
}
synchronized(lock) {
readerRunning=false;
}
}
}
}

View File

@ -0,0 +1,10 @@
$Id$
the i2p/apps/tests module is the root of application
level tests, and everything within it is released
according to the terms of the I2P license policy.
That means everything contained within the
i2p/apps/tests module is released into the public
domain unless otherwise marked. Alternate licenses
that may be used include GPL, GPL + java exception,
BSD, Cryptix, and MIT.

57
build.xml Normal file
View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="i2p">
<target name="all" >
<echo message="Useful targets: " />
<echo message=" build: build existing code into ./build/, but dont clean" />
<echo message=" javadoc: generate javadoc for the entire project into ./build/javadoc" />
<echo message=" clean: clean up the ./build/ dir" />
<echo message=" distclean: clean up all derived files" />
<echo message=" dist: distclean, then build and javadoc" />
</target>
<target name="dist" depends="distclean, build, javadoc" />
<target name="build" depends="builddep, jar" />
<target name="builddep">
<ant dir="core/java/" target="build" />
<ant dir="router/java/" target="build" />
<ant dir="apps/ministreaming/java/" target="build" />
<ant dir="apps/i2ptunnel/java/" target="build" />
<ant dir="apps/httptunnel/java/" target="build" />
<ant dir="apps/phttprelay/java/" target="build" />
<ant dir="installer/java/" target="build" />
</target>
<target name="compile" />
<target name="jar" depends="compile" >
<copy file="core/java/build/i2p.jar" todir="build/" />
<copy file="router/java/build/router.jar" todir="build/" />
<copy file="apps/ministreaming/java/build/mstreaming.jar" todir="build/" />
<copy file="apps/i2ptunnel/java/build/i2ptunnel.jar" todir="build/" />
<copy file="apps/httptunnel/java/build/httptunnel.jar" todir="build/" />
<copy file="apps/phttprelay/java/build/phttprelay.war" todir="build/" />
<copy file="installer/java/build/install.jar" todir="build/" />
<copy file="installer/java/build/guiinstall.jar" todir="build/" />
<copy file="installer/java/build/fetchseeds.jar" todir="build/" />
</target>
<target name="javadoc">
<mkdir dir="./build" />
<mkdir dir="./build/javadoc" />
<javadoc
sourcepath="core/java/src:core/java/test:router/java/src:router/java/test:apps/ministreaming/java/src:apps/i2ptunnel/java/src:apps/httptunnel/java/src"
destdir="./build/javadoc"
packagenames="*"
use="true"
splitindex="true"
windowtitle="I2P" />
</target>
<target name="clean">
<delete dir="./build" />
</target>
<target name="distclean" depends="clean">
<ant dir="core/java/" target="distclean" />
<ant dir="router/java/" target="distclean" />
<ant dir="apps/ministreaming/java/" target="distclean" />
<ant dir="apps/i2ptunnel/java/" target="distclean" />
<ant dir="apps/httptunnel/java/" target="distclean" />
<ant dir="apps/phttprelay/java/" target="distclean" />
<ant dir="installer/java/" target="distclean" />
</target>
</project>

82
hosts.txt Normal file
View File

@ -0,0 +1,82 @@
; TC's hosts.txt guaranteed freshness
; $Id: hosts.txt,v 1.28 2004/03/19 21:58:44 jrandom Exp $
; changelog:
; (1.27) added jar.i2p
; (1.26) added tor-www-proxy.i2p (WWW through tor by human - won't be up 24/7!)
; (1.25) added ugha.i2p
; (1.24) added reefer.i2p
; (1.23) added nic.i2p
; (1.22) added www.janonymous.i2p
; (1.21) added bluebeam.i2p
; (1.20) added echo.baffled.i2p (which runs mihi's echo server - telnet and get replies)
; (1.19) added irc.baffled.i2p (which is hooked up with irc.duck.i2p over i2p)
; (1.18) added www.aum.i2p, nntp.baffled.i2p, www.baffled.i2p, and ardvark.i2p
; (1.17) added gernika's eepsite
; (1.16) added duck's scp and hosting server
; (1.15) added eco's beta i2psnark server
; (1.14) added duck's pgp keyserver
; (1.13) added human's eepsite, the mail servers, and luckypunk's eepsite
; (1.12) added fillament's chessd server (FICS based clients or telnet to it)
; (1.11) added mp3.tc.i2p and mesh.firerabbit.i2p
; (1.10) added aum's mp3 and ogg streams
; (1.9) added the FCP and HTTP keys for a private entropy node, as well as nm's eepsite
; (1.8) updated nightblade's eepsite (after confirming ident w/ trent on iip)
; (1.7) added aum's eepsite
; (1.6) added fillament's flog/plog and eco's eepsite
; (1.5) added duck's irc
; (1.4) added eco's JAP tunnel
; (1.3) added duck's eepsite
; (1.2) fixed duck's subdomain names
; (1.1) added nntp, jabber, squid, and i2pcvs (and imported into i2p's cvs for distribution)
; (1.0) added dyad.i2p and nightblade.i2p
; (0.5) added bozo.i2p
; (0.0) added tc.i2p
;
tc.i2p=3RPLOkQGlq8anNyNWhjbMyHxpAvUyUJKbiUejI80DnPR59T3blc7-XrBhQ2iPbf-BRAR~v1j34Kpba1eDyhPk2gevsE6ULO1irarJ3~C9WcQH2wAbNiVwfWqbh6onQ~YmkSpGNwGHD6ytwbvTyXeBJcS8e6gmfNN-sYLn1aQu8UqWB3D6BmTfLtyS3eqWVk66Nrzmwy8E1Hvq5z~1lukYb~cyiDO1oZHAOLyUQtd9eN16yJY~2SRG8LiscpPMl9nSJUr6fmXMUubW-M7QGFH82Om-735PJUk6WMy1Hi9Vgh4Pxhdl7gfqGRWioFABdhcypb7p1Ca77p73uabLDFK-SjIYmdj7TwSdbNa6PCmzEvCEW~IZeZmnZC5B6pK30AdmD9vc641wUGce9xTJVfNRupf5L7pSsVIISix6FkKQk-FTW2RsZKLbuMCYMaPzLEx5gzODEqtI6Jf2teMd5xCz51RPayDJl~lJ-W0IWYfosnjM~KxYaqc4agviBuF5ZWeAAAA
dyad.i2p=W~JFpqSH8uopylox2V5hMbpcHSsb-dJkSKvdJ1vj~KQcUFJWXFyfbetBAukcGH5S559aK9oslU0qbVoMDlJITVC4OXfXSnVbJBP1IhsK8SvjSYicjmIi2fA~k4HvSh9Wxu~bg8yo~jgfHA8tjYppK9QKc56BpkJb~hx0nNGy4Ny9eW~6A5AwAmHvwdt5NqcREYRMjRd63dMGm8BcEe-6FbOyMo3dnIFcETWAe8TCeoMxm~S1n~6Jlinw3ETxv-L6lQkhFFWnC5zyzQ~4JhVxxT3taTMYXg8td4CBGmrS078jcjW63rlSiQgZBlYfN3iEYmurhuIEV9NXRcmnMrBOQUAoXPpVuRIxJbaQNDL71FO2iv424n4YjKs84suAho34GGQKq7WoL5V5KQgihfcl0f~xne-qP3FtpoPFeyA9x-sA2JWDAsxoZlfvgkiP5eyOn23prT9TJK47HCVilHSV11uTVaC4Jc5YsjoBCZadWbgQnMCKlZ4jk-bLE1PSWLg7AAAA
nightblade.i2p=nyErwSseXbsojcWtNkDyUul0YULtqr6qyWSzIp639Ygpe8juCdgPMLURVXcmlCvo~QPoHg6zt53KpgpGvB1-Wv2SGvc2Mvs~o8USw3ius8fP1URphqcBbulK8Ci0bgknt0kD0AfxqfMz-p~xk1QEMxq2kZEoB3oyIIFnQlpb2ByS74Lx8iKzXTrwWk19I3Dvu4nIq8CBDDwu3lYoCD2kC-jT5pjgglverGPEGN4o55LYVTtfSg4gAJFZeaE4KjBR5P1z7cca6UDjGMWfR0iCa8P3qpkY2ODYpk~8w2xgBbgDq-8Hzik~uraHc598ccS8QpwB0f0Jw~2PZcTjOPdZ-239U6p3tESXa7FXzRBCujv4Bx6CVFRhCmBHpyFnCD-MugZ~vR6XFSS2XBsCT~duXKq94HH2n1iAWslG4Vu44ut1JVhDPFzp~Dk7wujB0tCo2HXH2icRQxOWe37foU4LZSJ4oMpFDACBzwSfcZdIPsVRxGttKQx4yzgffR1Q~Jl7AAAA
bozo.i2p=ubMPUwY0op6B7Jr8SAjY2bQXze8m1sT6xF2N0cv43dIHwLTO0gUqn7FCP9jXZDodE9DR3fu8fG8x1Yz1SpXFk4WtFmuDuhdN7uaHuLIQ71PATC2GRhDS7NXqn7GsVZgQxhHKenaE5BKjIKt2amZ2~8CM0qBKTqwievUO-Y6zG~-8l~RpnAxDZUMOjKKy5R3~jEN9DFZCaKvXSNcOVFjZRGaD6d8NvkAJjndHdE3bFSJUDNv0qhhp09-mm~Se9C~FzjrAbhgappdNRiwQepXTWqRbjjt6lUPT2eJISPDxYxoeZkBGZa9XmfO9QH3hoMo0g~RbwLeBqtgeRGhVgFiC4pN8lFt3z7j8L-12575SUeOnJPIm3hQWXdTjKX1hqf4LopYBG84N95IeydPJegsmkIkAMzEb0d~-UZfVSP9yFgs37j~Fds5yxBsu-NFc6qmZihpXEd7jrfX1-HuJVmXFmwaZgyumRSRDbj714wxr7RP4Hb-liA3JrU-FbqNQFoTMAAAA
duck.i2p=eFJdRYFmtjcpx9-Mw3JBdF6fwtb9cHRBR103Q8IGc91Jdfn0iYzK6Xx0oIzPvpbD4yOlPQm-C-7eTahrAkHa2FMCRTiiVq2a0nBp37W1uTvAToV-MKYPKTdFMxrXxvjS7qaSUXdJRcPaPexolfx-Gcjh~rN2tKCh0mz9beueiQ18~8qWGh6hUMb0yyA09ipL9vIkmHmooLwT9AZyzHXEzdLXZe1P~CG3L46QaXp9aTD7EkAwG6VBMjQrGiSJ-9FFhx4QcYAZWM-dfrtzbbVYfHxqQRBwzB27zLlaKVaqu4enC0N1cW1yy-cjnv0Wxokqe62B2uPzFYtloxQpBPfTLQZUfUzjskY-3Yg2AdSbEu37jYsnAJA95AlLz4t1W1vPTNiXzCaRqkkMX342SUkJK-HZE3wTjAgGPqJ9vMaC2YexPFViTxEO2Q0jDSjdPHG0D769qehkN5Bfb8MEI-yr3g9zLaY~w4r6T38ap33qfyISWlIJ5qCPcRVkeK8OqZ4vAAAA
irc.duck.i2p=Bxqr5E7-56oJeya1lsDLBN6L1gKme-FUS6Bh~TQS3HswomK9rpjrYNeqBTBoE8TCFl161~FI3soWqbnmFhIdhskausZsO0ez5-4IXMJW8NTilWqXQ3OJxA20M9grohx3RjkZgXU1ooTx7wviSHtXQYiiqnzGnIzmmEZo5-Xx6VjXakctebWwbi2PrsE6XLxrxXBzB34l4KlVsyX504BJiOT6KXNaVZxvG61GfGVfNHdeXljMDE5d25UdFC6RJdDnJ3Z7Yb7EjAww78aowbR0VCfJDH~cB868-VOKIxmor3Rs7giaLXmUyW~GRtFX10COJj5V6BrhKs61XOXxfbyQKGVXZ0mM2A8cdZE1ftr96SZgGy~V8uUHKvoa1HpjMNrPL5Tr6EGfJxOxAy6PHwotn6a8UMnCZgEdTbQ6U3BTywU~x3SCAQvfOT~dl3sZ-5ujYWNFRp7RhdY-WHn1Kj59MfU-VpczGYdV3bRkwT5lpIjST~vopLfkYUeB6gcVSr49AAAA
jabber.duck.i2p=AzR8Zrms1sLXflvaZZ5CI2TS1cRvlO99D5Jh641-KS0lMSQPUnSApTfy9R4aPxZ3SWwpZPfSSGN-e7~kw8ucGQVB9EOHnht7WCz5UKC423vX4fFZqpTyqemDBx0CYXfhqRf-~7mjslXigR2nbQwh8NMl2bgLPknGe55wMlZPfsacS2WKNQYbdOvrgG6Zb6DK8RzTDrsum5h2jtIorv3CuMrNKLdfziXBDICep2Zp9jKDPBHgSsRgl6dW8A-5~WbkIPLpQwqeTzw90r8uJ9EIln~GlFulblJprCfLzxJ2LAFpxNcTkqzOJPElFfhjVrJOYbm6IfllBlHMVNbJjYTOsiJLqODxMiVt3pQx~AGFrx5mVtMUcz9cu9hVPnz2ZVmIDB~a4UGkexf~FCHhaQ6Emnr4nnZBiFSP3f4nASOMHLOs4SE3UKiYUB3ntmJssVdD1w-ZGrovHynkvXMjrJ9GhIeldFm0M5cOAhra0OtJagZ-bgphR0v99ADGPl7X5DoGAAAA
home.duck.i2p=dQmTRCAgcKlbt3VKdW-iFI4zd3WnDaM8ULsfIZb8oEdDuTq5Sl~1JguWH2Wl8PMRuxkhHpfOZ6IUJZtxo-rr3tfaDsYPl8SZSIbvGBs5QooLl2NNTPA~gQMJOHZkuM4KWMWYnFRWoaqAUt9KAmdyl5vgF20xv0rmSgRmm8t6c3QSTfNWSeSDYkRgEumaUKP-kRJnSKNeIOez1N2HQSlaDu~cH27E-VqEuAu8HppI87dV~v6nVKXR9-KgHzCdfHXxy2pxV9trJ8vmTEZO53ab11-BdDMoZjOv5sjMUADNDnhIW1RJ3c-ZcF9OotYsUrf7E1fRo3BwGg27LKhFnyBDmzI0a5uQJ9HCDlzSQ7yfHNMqNCtMfHgZE6Bw6F3tJfr5ISP1j5ibfj4k9tODyfWVqTionQfgrP5szHdyGu14Y4AtDW01-d9BNmBTHA53jCl~Jq6Qm5~CKcGZGS-05iyhmg9x1FtR-rW5Vf6o8uitSjtE5uPt~UHXacaGxQMJSy9vAAAA
nntp.duck.i2p=5SIb-1I6SVwFlW~XlYTNJU1NCpbyHJ3co8KdrpDxBosnHtdsZzq2qzYzGDfvkKK3WRoKmRGYCE77uXAvOQjEyWoTkkGeY2xl3B1t4t8K4j8dFvYtDJkRGePxGaY3MW-9ANGUQsGOhh6qq4lcQ7rv-AWyDftfZ3pGaHc79VukSo7-6OSh4WDw~0l~DjdFjQsZOVNTbKIfDxzSjKnkTpNc73nrLhRE5nmnMj7bljTzNtAHiVf~hFMndPxF80JZt6erLqy62-~XbevTWpn2KCTjYzhYUkwYW95-SW27ph5rIVZneizLEanSPtdUkDGhMEjjmy39Qr5rD5i9H-ioIP3NppRkjPwrtI9VJpsJtzv75uANIXy48RCBVXXA18ng8o7FVevdyjVb~C90IXpJF~mT7DI94rTT5QnzlpJVCid65kOoNzXFC80lP-iiwXMMBEcys8RGA9hdOvagkFDJ64l0GwrVpFGO2AVMsMenR0WHEvJOjNv62sT6rlntO3ZywhNXAAAA
pgp.duck.i2p=kRm2KRa2EiWO~XQFYxSg6UM9lXYHZ93IB80j3ShFhJOOZ4AN05BrTGjMeT9nhn5n1LMEUhy9HJuN-Lhkd89Mbhufk7di6M40wqySns2g0T7XUCFUgQ8kl4~BoY8M9U0pHpM3RJcCw8WJEFGEk~fX8tgC4XQB43UIXfrdKTlNfKhxZODE4UdvlFfFdOYMgH53x8UgMZUprn~URTRNg1uwcaXr2luMwts6HnDt1bDd8elitsWViOJiw42yAFMqBnf-7mhiTCsoYg-nsOiq0Jirt58cBjAGUL5ujZo~vfXkLslDKjHOP32Y7HIi5ANEsbbRr~8QrVUojnVoJwFXs8BQBTevRpGLkCSLnKa31jNSt3msOnEP-n729Jabwj8o3pdRk9e-3~~y9gfj4bcmpSH9sOJoqmipXDiqOvv0tL9twERqse3tAwjE1NgXTvl5e2Zc~F0xJ-L2aG~6BX175ihjjEiYGWRYaoEisHMZdMtivsAK92dKl1JVEkuDF3W8KjL8AAAA
scp.duck.i2p=AE2Ff7x-tJMI901UKEEXkcwb9~5KZKs--VBXEoZnnpC~mlgnqGsZr8MBvRvs6xAzP7xXLeL~cHQ~gvPFT60obEBitwH~JcKMahhJDGb0p23Z8B81QajXijypDpVfMDfFbMiqQGctXhnBidNKWe7HQEcJGZer-SCu3m8CiftcZ0t0g5Y67B2AtqLhza5xfepq6FQ-Tl6fpgS-UDcGUAqMpLUYfrBFB11oM09TRVsfNp~NIAvMdrvXiX8dTDUHJM4FRdpV2OsJiyDdgf5-W6s6ssxohviT5MdijUSYQw5sWj8~9Xkv~~aa11Dvty0E7K9IhXAeJlwBe3VuDizsAJGnFIU7PwZXV7-9-28Zgldarg1P-rh2QDvCqF1vjdyZe99BBcTiCqvE3zx2N-9eT~FeOURrgE~2rFBKVBZfuAASSORqiXIeAANKklCW2pGQrM3DkX8ybi93Weg~eBjwGQugO1FRZ-ISq7npRWruMiC7f~fWLqkmRUy9HahPNp8E41s9AAAA
squid.i2p=8fiWZbRjOEzrj5n4jSqjN9UN54wTrsgEjqn7GRUQpLx1svf8lwckXPV5buP2VEYGo~83ftkIcDKyMLXkxSr8jqbb4yAEgPe2~w7OT~8LNvmVPz0xZhIO6fiw0WU4xD9x5PG1spYjWPnLFv7pynEvBpWFXaUlCacjWL2KkfViiGPXvveQqQIZs7VkxVD2HK-oT4yIjdqHpc7Y8nEV9xwds3-LX6to5p70jFe~kZJA2fjWHsSCm92TtPvoR3aTlL1VS3JUKpcH6BL5irsh-SKODEtDRCErPQI~j2SHzhcD6dMUsI7bm3AxivjFpSQHqyXLmLVdxECYsMET~nIHmuv8NYTHQQ0jM0XTQzwnQwEHjHRBd1~spR9uS2~LSnX4Pw~X1WTknJpPK1f4Cu1O44X4RYcLRCsxpEzytUBXA4BQizrbYgOJVGQa9-PNGxJeZsnNUZ3PxUi23Oh-c4jUaB0ZjyKTWJSpzj1GI6vc-gW-0ixGJ358TCSbKgqdBv~g~f7yAAAA
i2pcvs.i2p=okyFe1gfa8R5CRoUAiZA105EaZVuLV4V82bGvd~lRhVhDWGgGMzLycW6XTjtlSpAJOQ1sTox-91EjyFcX4Z0lQKzFGNJrcEvsGQ99qtBJefkOnHEUNlsfo096MZwzwuywB3x3wkEQ3R1~kKjazw6YBek0I4~UkFd1J8TlSMdcM5K0WQtjeJyMEN5NQ-YZupJk7gth1khdtK2HXphFYDQkkZaKPY9ZLMxDtHUgwEcDS5KXfnpLEssakAd0zRPS03xQ9hzlbRkjNAnc8FE6Vkp7zflnOFo4qglNAhJb1IBl53r~Gp91QeD4WY0mZgkVb5fbsF4b3F3SerutnrjhLqO7y~TUM0Ww1pYXD2E~aIqdSz46cZ7AEETc7KZl1BtAQmKQFeJiPHn4xqLlRsMv-hxB2pupthpZ5nLhaOr738AuCJVdrCAMZM9jw3TtUZnckq2tPmA6IofFNIHAdQCQTxee5EXvakTC0Mw6-MHVq-OLuGDP9anEK2IFeZolDIU5pUAAAAA
jap.eco.i2p=R5rLvDIz8JAHgR7tf6iC8z7-ugctL23ipkNr~3SNCTxzdggCndToz~kmVACG5mGaoNf--KZsIKM2pbgSci45cJ1xAipx8CAaC3bbP3paDAY01hnxBPXcMN-Q1bisJPQHjkCphLRZqIh7gb1oXxiTtXU7erqkdAPXg6kvscFiblv5JxRg4B3Y5HHJ91UEb~Bc2oIsESQX3pCK~zmhWlReYROnoenaa9DDP1b6Na3SOAhgECR3PjoneMco1qDqlndb3C5wJ7X9YDGcZaIE~N6c0erNzWX-mGkEGARVLjyhtsOHMC72CROehAB3tsjwneKFojUpGQ16xcHGX4hEKFLNPztAbvnT7fg01dfcQb01YzIuBxbbjsP8nnoLJbEpjOaRrdIfk0cXm3aE6AQt7LoKYA-bW2sxgBxEAKsjcj4rQ57SDAi97FjnvzH2Xt363eHt0Jf0CypyEO9ajQYChEeMj6ISyVGe2a7wNJutq7aAx0ilbYB3Tiq0ExrbEsmXVvsOAAAA
fillament.i2p=Pa50z7pU~ni5nWwUdaDZ5CJxG0fYjoarm9wlxnkgX~wHMX9RPgQAXz~r0Rr1Nadt2OA~dr9RMHswrMok0hutK3JZuFD707D7FjmWW2w979Ee9I3zxKyx9W5A2eE49PPT131NLa3uINXLXOYVA5frfDOmM75Dmvm533r8e2kloemJyj22HpvRiSXiQYgqYJGDMH3Hlnwk884eRkQu7P8DJL~hcuKpyY0FzLZtfxTNsdSavGjl7rKPMzJeP02-9TS5TkdHokZrstVM5Cn9ay1c8DQrMds7SPXJy13Ut34QRjb65JxRV0mrnY3teXewW6QFvFMXJCsf5C3i46t-9Fufy5D1H8cSd2Tx~Xl71MC5-1AJCcIS01Od23E9tFY3dU7IOSRhKC~FiAslyk3x-BnBSpKxbgl1w~LArBm5plNiCiUemJU88xYdn1UyukLer~yNrEHAWspckCRkXFwmUtPkaGNTvfwBSYns-skNHSd7MAUUoS-ewStBdmtnDgRkwSG9AAAA
eco.i2p=KhRG6BGxVPh-BUDDfIgy0570cppTdighytcaGVR0HzQo46tgRMBp9Shlpax5FQX4nLHn6qHQbdFFpFbAwe7CiDhURCVF9-CxYmPurGadxlMPHMjz9O3jHX0CQiv2iULsk4XPrYXF3PqBc4t1J6vVyBVO7uTUhDi0gF6sN1Ro-1GLcWcsoR8Kx-hb~Z4WqGD0QAROOBPHnSRSb236qVBkhFvSkfigfBq3jFgEsttadYJA9ZLSUj1XrFFRBjz~xkRra8kJQSZl5dbfg-eZMlL49h61U6Uta5n1~tL6sarmnl9CaTl2Qo27SKB1OmMLeZEteA5G0-~LiOjN0nxaKpwrCjKIOyvwbQy2QqE-GEb9m8SN8nC2bwYK9fH15pTMHY8GvPYGcUukbF6RhefwzkEJLZ~PaAECrZYuLsn9KE5C35uRnlWJiuJlJ25hG7da5tFMyDB95efzq5IMxPeI0pMigRfuVfRSaGDpNos6JxjfEIX8umk3jIJUPhz1d8gP4QgrAAAA
aum.i2p=09hSo56PTtkFLUEt1iUTO7zYTnO-B~ogsIsyyPWif6q1Iz4wz4JoBflAWZtedPmwGmH0nly4HYUS0gAADoUmBUnXwemmO6dxT-hPQkfEW-A7b3uEvYQWIN~kyFyg0Pa~FN6TaD9kGFttBN-GE4wxiHhXmWdzWNDVb0q5PVGnxMm6Jleik8xkd2Lgeexze8rIv8LCocAWx074USVkbCVQwoFi2P7EnjLq8odSz1cJAntbuCFeUZcjbslE3qmlcTFMCNCZXWKVzn7d5m4oszCQ83NidgekwxJ-S~iS6mBwIS0XKI--4iXiKXzzCFf0KtYfEWpvKCuqNJOcU8vQWAA2-i7~K28aLPzccDQn7acXWLKRTXF3tf0i6e-lSx-X6WTSWK-fuNitjAtKu~jqO10d~bCk7y~UPL-XwdH1XSTbk-Phhk7UoBTDiHY6zQHdD~iAzXER~9JXsJ4UoIrGFVabg7frzSt82CN7Ek5Li4AMz5gg3wq9H9HUa7xM5QfGIJpXAAAA
mp3.aum.i2p=vBOu1caCAajaL5WRMuwk4LwfXrlcn0WzA6iHUKV5ULhaBpJb9pR3SZpnQms2Ot2c5Fvu5I6Rp7WF6QRcyasAhUmC915ap~2~VG8KCDP0z3Quh1-eqGcmzErsIfXdh09j3CWuxN~fH84hd~KswqGudFkWtFTM9RcuQUGSC1efG7uF03uaDI-DKu7eb4VUV-hmpXb3Lqntgo5qSMBMmjyUND-f6RBoXnqM005mUZJpMoYfsBhnUEq37GG8u6P9T94nlMmtz9R3gNURpBJJKPlnEqCBN4mlE5rwspQ0ovxAlogVMhSCpQ4jr6cyWIbNx-nMzKGDj~hMQ0ndbVnWw3EDC3KsPnRnDv0yVz8Fc1YpoPwerHej7VnTupDKxc4T-j8XNA1dN8SfPmaKYEPfavlmy7HFAGcsbmeRZOq-PVvlDdrKNflug8Ysodd5XkDbh7y2k1pRDjwNBQ1EgDVAtL05-i9jerqekHkbFKJ6DlT76f06vj1R2v9qlQzAYjpcKbI3AAAA
ogg.aum.i2p=wR2ETKWn-mxsTurWwmSujvjpOiIjLg5TsldFUa4YFTgiRZdFIB-bXuK59shfnchlEgAZR0IR3~hH-O8bZ~j6wVBdZWq7bGTmyTxQ3MeYPdqK7wH7Jp147YUabFlqJkyI~DluwBDylJrIUyc2qw~ogJ67x-KyzIF7JLnoCC4E-T8Z0vmTAFWSa3XC-ncghrdZQCqEXaCMlG9PN~a7dcDq~qdWoNoyFcgLd0IQfE8JuJ1wSvmWUNEd9vkB2Zuu3EoSoDv4C53Fc0YhVACNug~VEEL-ZBGcCBcpVNud8dOMq-CbavkD5yKqHlvq~uzRi6BY5ajHI77uepJygkHcsm-8T0PXWXdc5ib4TtUI03tPkTar4Y2iVocY~oLk2jh7pQKZNioHJT4StWv9Pj8EWaVX4-emQB5kZOBwZItjo~EAGEoBT14NSM7CmKClgc6sg7fpvWF~-cNHkZsurBndni~~FKmUeWoO0FRQRF9Ao~C1DOt2V9oBbEW1~n6anjL5V~IyAAAA
fcp.entropy.i2p=jy0D13oJVmxSa1MltstV3FOfA5e2WAEEZJiYOJIZSUOcNnAkaR3ghE-AX4vuqyQPyOEUydpauD6cS8vfx4iZkb2U3ddlLcOU3YrFKdLrySpGtD~V126VO-9nOJFwDQOOaKAsiVGRKtMPLC64GkpU6TWSIhiVYWb7WmeAHXLLlR71DtgamAxEIlP3VhytxlS3vuvAoEH9ItsBwkv4N~7jec60WMQINl~c7uDDsuzKFY8wQlkHnLFQJCQ0VExfNYqK9nZ3x8TXNPmNKTMMQ1CUCowgwR783U7UAYqsxNrpkuWvTleadn7QcR9i2v4~L9zOeHd4nHBy8PAjO29g6nf6DIsYhg4c2HYnPYzktQ1NIElytmW83BhbXJXLgNBs1eI9gDaQmOiXi74FMgfg63IcXCYWeqCdwEzSouSphaXEHDcZZVTx7DE9R-1Bi4Dt~KvPOFsAoOqsjHCpHq1gS0u5HiL0hkSm1I1EMk4JBY0j4rM1nAt7e0ix~WiOz5jXlTVSAAAA
http.entropy.i2p=ON2Ud-B0-pJKbTR0Obpjp9wEG8grUpu55gEn5Mz3-dkVkPhHvHK6iLasr~P~Rf4kPPZvn-eK7z6rAVfsAytAJ9pcTH3lXERTjkd9FzVJJ0twbZSQ~XzX5d-24IPIMf00KegjnDkRJ82cRMKa-u4H-ayei~Y7xsSx64zC1eHv6qFxavtql3zRrS~du41~EHtpjqOtOo9Ea3lfFjhm2jUIJpYyVHqve3WbTfMBlguVALwGZIfenph7oQ1Hx~OnEtaviWuOEpupjm11LS9xqCNsccaEpJGvGt6ijxd9hrEuQZ5Ja~C0fNxf3xNtgRaUhakA8Xoo~jz8rCkV2vYQo58kj0E5xYrUQczomj8y-eDBZyq29BP8pfe2G1u3hpHA1z470LUeMPk8qVx8Cx8ZKmSK9XCvOl7WCnFS2~UUfzxbxSxPn9LfzxDZp05AVi9t~hJg-zkrL2n1wfEnScuUFapxarwK90rlAxNSnau-K61WfcXqyVMwDxl3leJOeVdHqhpbAAAA
www.mail.i2p=Y~V8YK2M-my6-Gw0lkrkJouxeqPuB03idp-4uT9pkIXCA5nki9m4YFfPObSPv0E7c2shBxwlUo-6beaRQ-7tCawJssDRc0C0PhRj12QUYYdtZP7JS8SQXy68gZIylY-wfyEXleIC4mYY5mSthhdUUfyo1lqzrdHc1NpjPBxRJcyMBFBGUeM7Of9E9M518jXpVl0bAmxSnr5dy7sgKAVNufzfqIBfEHnmL2ZYH78FoGnPybsV0F9~154emkmt89ZUbx0BuYvH3kT1zin8pSxKw1NqxvqYt7p8CElq1--U38rO9U5Y~kLB9f6F3RYJdkl28ANkvdgJUgqiHLVI5oPWATrJLAOokyGKhK4Xl4Bjp4SCuemxHwTOGyd-4Kl8cO41u3w1LksndX9stkV6U1X0gL9BeSIoa1997IgMLVbUiDMyCz7-cA0y2tc0EdQdlpc2y77nTdo7z23dMSJzWDXsrfmLhX7M24D70htLLc1dpwZ1BUEvM1uPqGfsBSrHdl-sAAAA
smtp.mail.i2p=gfSAYcvEsuU3oNGqeMpq1wZqH-whE1i~YCXEDwYzp8LrmukWsndvPER1~gF5QFrIp2RMXiietF3zEPtAJgevSG4ULxRU0s9MSAMXXlCACVhlf0m493J6kIYnkypOPC-Z8sulyF2kXM8BURLfSH57SS2uxLbx0hkc8j0kR9iys3ksxm5dgW-Rs7clAvLmmfASJkXZiU6DRhWbW84GbpAi9McE3ORhNLrWV5t1W4DXqzT0tzF2W0i8BEGns8XdOBQei9RAewzo5NRGPBmUl6ZKjEJ-2UtX189HPs7FcLknfsRxXhRcPQ1RombPezYCgcNhOgWY9owHq64mwGaDCnnpDSM01sdAuMlFfs1JJMoYNrILckjiHUNzV2XS8A0PIWdO4W0cT1EUs-V2a7Ocvg397HpR6Z4k-7fOrjs9yvpFsCPIEKYUD0mjr44N5pJIc61GGuNE~2ihQZGA3ju0OnUKTRZel3nK0rxl-qfFXsBsEB5vt-MTvKS73ZJdxUKWWzbWAAAA
pop.mail.i2p=aG6owmzirq7QZKYovpSVa4-WBLfI1uJ38cNmb6kkSkcS8A~JdoLWPj6eXieN8r3m7YJLxxyZhf3urmK9qJbiIPBp53M8bOSSkldpFz0NkQPWUWmYXiOrEsOBlugbJ8nNelDcOebqoieKBOTaF-WPJQil5C6RYdUy~PL50O6Qp-Hog1868zP26leYBBFiyzzWI3bOpOsgV~4bNXnqKQZeHXz1Ua2DkV-vDBpeamPzvNWQI6cNodf04PBKXK~TlduLZDK7v1yTt2LhPSBM5nE7ZRtS8KQIdh4o-nyYRmHjA~OQ70gowGpmsRqXHQxOpPro~C7w3gSe2N0zhqSHKstwoFJD-NmsBQ5opyOiKccATpWEQdAwmoICD6rw7TGG4XYXCtyTD2xxLffER0SEsJ-BJesKKhdm-qyEMAOQq00jatoEs9jBYoujFLFQMUgaDejRJdEHWkiGT~x4auosHGYavmrcm-0mdX0CWYgfjwVb~PORhBqHJ1G5IgRPjoZLuxiJAAAA
nm.i2p=UhUVbM972VwQgqS28SkGPtghCb~IpdpeMW7O9E7I3HtlB2I3XGbMeUAoya5RHsoG3TYxf~P6lA5IM5Z~mDZlcbZ~AG7255FE6Z3Jl9kfMArneou6AYaCfNBqNTRS-P7yX9s0Kss-vM63yBulxhS7CqmBKTZsXR27PNjJS0PMYsWBzciuy8UqUkE0YEhSLWSUYAXLP9FKs065CNjxsLumkkoF~MNUaBNEmbCrjpv5Ih9vrwz4XAJSE~S61qSMj6O-nvEPDVhTJJ5ymeoZnYMpIRt4r7FkCTH8vYSkXZkhqXUkLC11WPC33lw2wzh-irmIb5GQeab9o0-DuNQcvUnbK13Jxkq5XiilfK6kgKiPcEniqxMb-4paZAl8dj9Zp01LvhfjlS0c459Jv-gr7ZkjkX7hhTaEVvqwPyFgoVKnxQCitoZrK98WRKJwu7EQb-Kin2vzUsYfpGGI0aT68~gdr23oom2FsoZ~owVuur1h0bJr9mnCaMf6jaioQE7wezxgAAAA
mp3.tc.i2p=LRCWTiJouI6ohqtaVBNHWZe2ymjhtyj3z9KdeI2G2D9l0cYFsG0CRUVT5VPYOg~WykALRVBiL-2U24fbiQ28hhPdQgBMBDl9aQiZJM2hv~di0uVOdARhRSgCDgRQAWioAfpXeg6pyklzXU3TNLY4c2CRLe~9Y7wuLbK3lONsAApcxxKeHLrfGNkZZwJTKd7PcG78KAHU07E-TVNf4tQrOh8tSrHaMB1r7cQQv1Jl8mNUZWz4fGeNYEZ1wr04w74Em2~Z5K0VZ4mH5DhFGXc9ALYzZf6uOVzZKiUC0eOcdfGNdVUbIog2CYJGH69TgAX69d5vF~kEzvHSzX7RxUTt0y25Rlbi6rHSDF36xOfBrOUVnSPn5X~TdKLygz~zusYpRdGZwlsyOTKVTzJHKlU6Vp5Dofijj1bUwXDQ59XCpFUqEDA17nETiOO0H5jfieYBPS6Ke2cFTAhutSvaw~albCd1eV7RPqeGdw-vFfKoucDIEVUT40B5qalyFRKIxx3lAAAA
mesh.firerabbit.i2p=BLt3pciNQcVIU-IGnTfSsrputh~b6drZpc1vH8qeA745XoE~nMCGtw8S7HGYX4uEbwk876nQRPV4qRwGtWWkWBs8BX9qX8NABoXFk4G-6NifB4TxEizC~ZAnXZ2uFs~nrqodhyCR8bBHJL0tzBYK3E36zc~SA-DKqQ9XSHWp7ScW7Z3cdQnYKma~x4u5eZmcS23uie3OIfOCk6pJOabtaE-YWRa1eUizhucI-ysm789GumjTo858vHR1mTQfrsPTqNri3yz0aIe~w06ifciq~UlNjVfx89lLEso1vmg8rfTQ-hwxS-qz-u3K5x5vtqdGp673vCvmEnQpU6GEycmkqoLCho9pNQzGbka-OVHg8fZqlFMeBfj2iLz~zlv170jvX6HTlMCNfBYnFqavs2RQJj7--dJ0g7JHReGMKL~TciQjxljrV5AoN-0afRzTZqtDg13PL4tltJm5U1~f-GcxlsjKLZAlv26LlZXsvTDU5plldsernv3fDcBev9UaKYCwAAAA
chess.fillament.i2p=8xvXLwcBYu2MxqMAoG6DIahvkNAwBQs43kdZTg9SlouzC3JSQ25RHvspbrxWoGIVAlB~XCxVmBB6EolYEHoSZv2YCziOj4UVxEbVP7nHkh32-7Uc5T7xlcjk8-rsmZzdgz9NhxKVn2Uhp3xtcdVAiyG4mpXisvK-7NgHY-mXPNvj6goaH58roeDUZ5otJN7nCFmcRAUpaUk-nlQs8YfgSCCEsWKOWhsVnAaHwtqtDlpdTo1YKooACMRSa-DcV5W75Il80JEWBD79qpSAeONGAOMMPT~VEMwNNg001VG-UZbvyspZdxHaETw2yd7N9l3-mwI-sYcveTTnNXLWO8IjdgDLdUIP5qrIW6WS9XZIHRKqT2kpwEw7xsEgCA~qSNiZWeai8n6Zs0rLmdyeZeafVEEW9vr6CKcLZ5W7i1SMDqCKnzSbZdd2Hvpb9aWFf5foEjLt33n8w2KSaCUe4zmN~xuQMq2yiB-vQ9~5fgPmphlMxo3ca5MTCfbhshw5137YAAAA
kaji.i2p=i-nivH~36AgabgzvS06oKHR~MLoy6NA0oSv8JuNJDLZB8FXEDzIiyzWISmw9XJm8I7QqZ1yFH8~xe84STCHHvMMIQQrfBmOUODLWbKZor~~KhiHdNLtfVZC5BpnXkBCJkklj~fMYSpWa0C~pVRrZl75RoGtBjDVP9P8hioTv5B6RC86g2ypBH5r093rY0wnzxSL8-ZuV3F~H48VYbqro8cRlbMmjx2oEsSHkDpQyjCMVkIYKaCijkSArqZTG~zX6op6Ng9CJwdrkjKSsbzjV6MLnE4aNv-jm2WaGGD5pR24h7e3ImDOGAr17tXRtmNX5ZEQ1udQp8qIhd8UMUumrnm962r8KJWK~9WNzcVeqDrIxaaxC7vcQmXxoPeEW2efbH0yKhVZ7OFu~I9cAapSe~aNWp9UK4URSpuJvOedt0axp3ORaaM-a5U7noW3Ao-HB83qfFEPU-6uUu16HNiPqCFMJiA0qODTOwHiyyx4HKQvbhjujh4mmknSbsuapdgR1AAAA
human.i2p=ji02vZzrp51aAsi~NZ8hwMLbr1rzMtdPUSiWAU94H89kO-~9Oc8Vucpf2vc6NOvStXpeTOqcRz-WhF01W8gj-YLP3WFskbjCcUwz0yF8dHonBeC4A5l4CjupAaztBSMbhu4vyN9FJkqZUFN01eZbQ9UqgXgLWMp4DtbUwf78y8VrzdAfmUOrVn6Iu89B~HUfOAKnpIlQXyGsQk1fnLw3PzDo2PVi8Q3C1Ntn0ybovD1xDKPrrHliTK4or2YujTcEOhSBLK4tQGvouN-tWqcVoF9O814yNGtze~uot62ACGJj9nvEU3J7QPgOl~fgBJ5Hvom0Qu-yPAGJuAZa29LSHnvRhih~z~6lWZYHREBYXQ58IzKktk90xJWcTwlwRRhyO-Sz3A5JYR3jM97h4SsoYBVrjK9TWnvGKj~fc8wYRDzt1oFVfubLlT-17LUzNc59H-2Vhxx8yaey8J~dqdWO0YdowqekxxlZf2~IVSGuLvIZYsr7~f--mLAxCgQBCjOjAAAA
lp.i2p=i~VkqY6fes7yCR6Yn4Nowuo3h621nKC9yvMVOEGW6nC61kLRKS5xcr7JdsFohF02Z7neR7Nv3GLB1qTvqguU2SekCPpfzNYNSDCZZVPMy1IXegZupmMMtDXnY7dAwcy~d5hdjwtODidfiG37~C-AE2g8cogJSAG5-sgOTOcA288fu0n9qYn9lejK6T8vQYMJIgqW73K5ErLg8F9C8yPfCNwRlqOZ8xSWowpvlyzWy5OFMmhk00S4JA5TAKlw-ILQDT20qFOHNcCWc8biXjNDozMxcw~h3rq-TQtWyW0-J8ERf2tmjPSoRr7zxKTFXlP0ibQz8HqAhlt9xDKlpd2LZyI9cKvMNfmGBIrFZHckO~rHtCTNtA7dN-UheC~k2bX0hDlQ8A6QWKovTnt3yktOJjwxBQU3iL7UGXbS8M~8t9LmiEKdTZgMUnAcBLkrPOfTAKpkCLE-~yAYBDj~U8ZKJKEtD-gLDY9~slVuo2AgNLYF6bipXxSMsoCzevJvcWnqAAAA
bt1.eco.i2p=SUHjD4QvbuY5VVTGTm49U8B~DtvTS2bwO1lREMNfJtZU6rxa4tgdqaIpUjRRrbZYLxZcgxjUYx4Bq7gcjriETIRaMy7lQtMx9zFk~XLE5baTmZeXc1~xuQCCTelnYv0yUswWSCZx7ll2a-Y6d88jvTydfxcCTNAYclT~gHnA0kU2kRHD5vgEteidiXFBVer2Ps68tnARUqURDKxTRyRnWtrxwQocmP5MJG29e4dIptcecxX8bKhgqgONzPAmYzAR7F694wEOXJZQO67ro0YQEuQmDXICBI8IwkbAt8qM~BG~JF1H26iWblWWs8mGJonwl34-CpVjMSXWVk~SC~60-xr97CsWSGAeZqrD8pLJyDGFsJ0jNFV0L6Q97ryUViakVwsHaAMlxZ3Hh7KAc6TUcJEGvuygRJ2DKeKA1wyuUDFc08m40HrRVwydn0hwzs4Qfb8hXfGHQ9-yauK6Gk-HvYFUL9qcIrOfrZGgq4xLVdgVh2wB1mIOTkcBMMti4941AAAA
gernika.i2p=1D9ee5J04ZI7pvCwQ3hXQMpeMvbNw9cz3V2pioQ9LakwRQzfMEb9CVAeiFt-wE4HyTFWKeof5rz8F5vmIqFMaH~oMJSkCyNtwPfqiRAENeNeALHbY2plMPfqCfEFR4GkTqXnalAkJGqDjo55CokUfalEVTGMvNiv6i6dmNvwS8~0X56sXIaXgLKuGIK~UNOg9hT8A~uEGQWXwTKD3EDmECsJL40iYcT1UR9rffzuyxOvDhL12HbJ8bIlUGarzEscH-jolj5ShvZAbEyw-MnVzR9LiEqy7DaniKpPtC0oXRZuz7PpcQTqzN-zgQaLq8bHTx7NHIfTuA4P~hhz-STO4SjPot7h~Gbdglc193OmGlL5QwbfjXfdOIccBDh6~jtFaa28GxHrTMoi9GafjnllLfWpvynN1y5H7Jh7Uw3E7KDtBGVsDg9-btyvyQLP3kkqPfIAn7Oj6ePHr9u-TN9ZwDbWj~QBmXXutsE~lLu7aT7kv5Jx6PFmLEeWPib82UuGAAAA
www.aum.i2p=8x3TYbh9aq6EVo8rSuPWBVSrwD~yS1al6z0RZYvRaQFXL9hFUUJYJNL2n3oR3tBg7Zr00MjqntIuBMd20LUKasnklTp4hDlDCE0KfeftYh~bFGykMRf0yTYEWaMHYpIRBY-IJEvSVlgHe8E4AWLMv-b6VKCDZ0~0AdUrsHQ0Qb4NQ-igBPZfU6c~UU0tUVUl1Efsuz1CdAp5pw5RdPviFtPH4tMvUca2t54Rwa-6v6QCqFZ7S2awyhAa73Zb9YlcqT4hP1JHF0wR0rL-OEoJV0gG47Co4Zr903SNW6cgKDj3lW1tIpzcVMoH3BE22SMEVjYyEHgAORdoYwaj19CUg1slDGmvUCoq4dPsnCIrvV7N0LeoUkZekt2pvr~yCH47ENV3oQYpFVLcMLN82tzI0ZFz5IyBHWGr22vlDlT1C-QVhAYQKN92XubDXSEgrhWv5IHPB0h~EgZi-rDcsJG2zb6ZqjtKFHp4CnNvTUxE1cCJh0aR1MDzM~o0iSMiMqh0AAAA
madman2003.i2p=Uilou~5268x~UI5Cjg3XYKfBCeKVEMuD0g9Ea7~j2aNXbczbYXCdRQEetYMk0919Dyyj5Bfpiz5m4~xHMUoXs1aJedteFjvwwOHcXE1IuhORhILv8kKyW4zBYrK7LUnvn~xFsnl6~xaThgwxneXLGhKz5UOitIzDUJmxCAwfcARHgx9PIUuJ5LTninB3Luyev11TIqweOfe~X0dzVYSmemBgaw2T6dxCz7qz0mN5fJe18~qnTqJibKT~7teX~hQhRQolk38P3tLeBRq3wZGTRsJ6biGEydTbE-rqKrNvIMNYvUbhhRHEMdShAiUo4gPJwbyCRRScI~DbluYuy6iXXURnrCIEv2DKtfpHSS~JccPu25HKcpNBfCLzQye7L8hWgjAyaQoTi3idW2JJNEIBBBS4yfGk7wq9swlzBK2aanI-JzFcDrVDXd~fpN0JYN-phF3YJnKspJe2i83BW8J9b-y2JJVbTWBwU2mIEE~gXKggsCvqESfuKK5RhgFaS4dTAAAA
ardvark.i2p=lHNIrjt2OnxJG~p1ZVJ1UzdwoCNLSAGbXq81bSKFQsMNCXvVtYA8-uJPTk~F4uYyaiH7x9SGg5B5dK0RmLdacBNCpwPlCPPxEpHbWizUahUQMo8ifLsyOS02ev4Bxf~i50RHmjU1zPUDe6LNxlFmA60zehKAkSR~pp2SJdbs-3nyBhvG6SX5CUHY5r3g7PpcGvGDnPOuGGJtR0xIqi-~89TsKK74qS6JiKv4BzqbWUn9eZ3TrSaC1-LAjoIiowC6e2Ske9r2HEl3T4fJ3qmOTu6vZWLQCBkzprAVOFoTwou8MJU97n3l9~1UrgghU939jC~IrAye~VolxqCdbsSz~m4Ol9fWdGmAfs~l4ZNgA89Q0O9Z5ooCm5LBlR6SjtOLOO2tQJstSb0V7-XpOxafp-a4EmXMEzg6f9ttBZNDW86hxLUPUzT9O90~27ss6j9txgx7lmFrDL8EjoQIFis6MNZRhqK4O7elL55UltDQeSel2E11iogHa4oZs8S7BsReAAAA
nntp.baffled.i2p=FpTF-krVDnZvQrY3JPZ4A44P5ewaVMSppoNrfH60g~4O3AnU6Uz9V-izgw~Hrn9u10MexFZSYYDTznxCOc6MdSEME6X~hfWYP-g0zCt~OutUAyhyUiWV~g2PAD6u-SMAzxw8F9gesheQKU7drjplDXiTNMXqD9ILuAVaW4VABmci1k0mqSEOKPyZkRyJZJNbU6vx~ELytgQJqwK~LOqDiiG9~6-e3MpMJ0xHy2yUibd6xv3GRLMaJ9kWAruMM5otZdxJiOUOf7n7wWvSVDgmwUx6eEkS8bVlsPxD~HW7i~DNPa5Aa7QD0o46abx77YF2jaComxTzpFx6dwj-0AqTMRhVwtfPgiiXbzx6g28Eggyc93QFJ1wUX5Wgix3RjndsHE9bVOpxNjjDlRwpRP6ytu7zYDkyjJUXDiC-8SRIGDvcSQZi0Op7KsvrxgGt6hGeJyN4JhxE08vDHN~1S8DwqNzc5sQKkpmdJHywOBhzeeNucB3EcSvh6ZaJfJUywZ8FAAAA
www.baffled.i2p=XchfxmcxW-Ex4IJ9Udd8b3O4eKgjJJx0FKM6fUbqqqXUQmOOoXKhzKwlKYobN3JIOE5zYXuWoPiW13a6befjYhZ4x6lky5--GwZe9IgLsmBqwsqGRHRaCqGiDcsHLdIWUJFWmHz38U0GYW6ggcgODQgxs00Kc2u2zfhOfVbFb8ckjvPOFah5iyfh4UQn8uaOl7WOCQmg0U47vSd6glHRq50NdRryGJv722U9TZlnrara-5f7oQn2qm1ctwnnm3sUjRMsNCJbCwuvsVY04axL5VXggwrZ6nDaZOIQHvzoNVnQtbT82GQvcBEda4Jr~IX7gLYiDS7BGNbX1NsOWBTr8GwaozPzFnaA1HMHYH0n-m2Tf~8O~7oqWzlQSP4mqcPd4ePbaXdV1uNXAr4-kziO7J3ID4vhG-biS4fYAUZqEs2rwUgFU1jPocl~Aoi~8alcgWK98mN680ZrT6th~w1BC~rf1q8Zg3MkE5RwW5jRpkOpH5a7vU8Lp6x1xQlLx6YXAAAA
irc.baffled.i2p=AjR0lkZfdzmQqLofq5En-yeUYZ9XOugrcpqgA6giY1v880AnhB~xokcAdV76Sx~D3MLkXKjCYF-AMIrvfV8cRZ0XkY8FPFb-MnsnACGirQ4~WN6kEjQ18iEH-OStEeoATiEzCzhQlIaJkCLnRo~lAbsnmqMrV3tJIjdQOL~uxbZQB5rQl~50w01XWIzsjwo0ZqzPjsJ6715HMRp6hag0NYNgf8ZE7vFKkvRNubl60lI3LWduoA7BjwuqCvHP8QddYmmL1s-L0rjDB0JGxRJ7~YZlUA0BECTIxdv2Kv-QEwr2UmuSLFQiQ2jQ5U5z0tzwObaIcAX4aAaKY16mC~cQE5cltSS3ElnLrI0qhpIeTM6217uigbAO6iigYFK5sazru9YEt0jDk2gzxlTF6m5BAn70tnu5IAikzRe2NI8VnkRa7K473r~mS30CzwOHXuxlSEPQUK3AkkrQN1Nfw5dCBLTpIF6aMPqR5tracYPOEOlro76rzChnNu-QDZmOQGkxAAAA
echo.baffled.i2p=RbdhJKxX35YHvCFR8wNklf~4uJISsjbCy4ur4hsD~0nZ44UVbjbYXHG3ng-PG-rcU8WIhnS2bWvyEWToQNRW8tDreXzwL3uqWmD3ceDVWgaL96oi8dcaiuNu7ualetsP8PilEOWifgAY21whF3NpFtrGPqt4-jXsQvoWiUtdncezbJu1BbcCc-18QrSrJqUjp3MejFYIJwBRZEu6zLRcN1Canky1957WZeIZUKvEyaEY~nVWZXbjcLGZKqHWIhBOc28ldLBI0xGzcrlUv4VAz2AMH0E1hFCu1sb-GrHqOuyRK1pbDKVL9dg-l7pNl6MzVruBAuDyusp9nJKN4qCjk01dy5UgQrYIFfBKGg7TIiyBcQQJHVbZPLMLbJf52G0EaVsJMTAg9cwNsJihz8-Xrp-WIio9odX9VNT6VwADcn8NTVNuf8wA1j389Ns~e4HWz1oQK89eN29GV2SD5E3PZGJ-0iBlk-Zz6hnca455tf9VfFTxKUX-7~93tRjwWdoOAAAA
bluebeam.i2p=JE00TEW1khGvC~IB27LCrXcsZuNtzE6LE6rXL1Vy0D-hr-FkfZwh8AeQ6uocbsmGNagUY9w3C-y9XfZnCK3THev5QL3gh5SNk7iGMjUKTmR4HhKomYDXhIAOnkxeOJ7UrcuPc2H1n1SeKVXqmv7-ygRvVWY00QH7ucOAwjq3hRkuMn6r7bjxbi26Gd8xAQfjCBVvCyQWCAiBHKpMJZWSlOjWrUC~-t0uCQNWGxcskvz~todIdv3ES2XZhPVrYpYmR2mP2K9ihH1L~GpwQYM7G9-LfqfmeHA9L2i4c1ovqxPxfHwMFIevjtIBUPYZ8d7t~jkfBDjSNvA-pwIFvLmqkEOYfs8G1n6ER26PUEtp6JRfa6RbOFPcAK7WRb1lV1cVvGYr6Sm1~GkoIw5SJcFspKl~9UV-f9H5737W4mwXLvvBJWwFz9n~x0QEnEO16rnX4YVchNryTkSqcSingSQZdPQvYnHp9esCi~Mp4qvj4InRX~pfiHYGzJ1a3CPiNb2JAAAA
www.janonymous.i2p=ovdbTie3mEme3kkvCsXjfU2BzOB0oPv0UvQLoUDSUjmPHipugKXZFYdBu0ySbl0sbJEyEgUQxDqywDq53vGtJCjqb7wDa4~hicc9bncQGbR~jhppFi2LneXRW5qawilTNP17zpyISSleRPXPBzFO65n-Z2BYz8pBm9gx0gB81UanojtE0jvAap-aYySMR6-qka44YOnEgs61fuo-~VLTgcDae-4tS8fB1PPgoLH0eqR6SQV6j0hNB9oXY4SndNtgTgCuJFTA6rkZyKdf5r9BuWybiFMvLJa~pXqSAbLefTYQldDb7LH5KQBAS2DBM7~Ri1yc1JPf-8MqWRDfi1povUICgF9kYOF7qYNaxi2xPPpqUHMiB96nUu5Lrmrecjb13qBWLDpOhiuuWconuggLo0~nFafn~Wh1yj7vCbeFfAwYaN12MleG1pzjcb1kX2rhZ-I2mVItv1WRsIVrnGiXUxoN5ZJoymkWGB7P7KdA4iCZ~CHqNLKLGQxSfblK-3XKAAAA
nic.i2p=IV9bI7aaeN5cHSqgXe9df43lh5ZgWegT50j9FbZSc3xKFQYKKrsKYH8f7LCvzrkMGrRAjSw0yRQtRUvnTn-2lpLG4DMs9ElWHVzuIgQKL6hqRoMawcbQxth6KFLs-pjGAFfLqFRqOeY1kCGOryhuhQ7iJ7iYQicRVbfTjSYDE5Fx4oi5LbvIXL6UfcuN3SkF75EPUHZ910dKukWst1ahwaOYj4U40bGuebzDUd9Aha7tgvE~WJQ2lMLhEm6~wOg9zsDzaTL6FI7XgfREEU3XO~WOgpMJBRJT7pqAqYBK-yCI5DhfyETWZMVOuoKUhlKt78squ8B8XHlXFgpaiOE9Q1vxQWYNn~UaAjUNbeeyC1iKjj5wIlzT56G5kGAOlK-6hIPs31I02TlKABTDD-BzlyyjyBS32Lkx-5P~Z1r~okn4PFiWDJuY0idmrBNtd4Yrqc4JkXifD1evgJ5042cV231WNBbjo95jv4HICV8WaiT7LxYlkHSWqzt~SI5017MeAAAA
reefer.i2p=OgiTTgA1xghkPcwgGpnyIJCBG1cZk17UKMAJ7H0YiroAvrAU4mexKPkWoy3JFmsenJuHnydvHJLaVbfGfEYu9EIDPynmIfBdM~WzM3Fe4IYtg1BxNnPmkz98p7lERcXiZdKXgyKPXsMta3kyEWNLrfKDwcmC4CUphE2sKjM7J61uxriCNseXaahP-U6c0aON8AFWnW5nJzF60llQGdGibKsUXUJCC5K-kphU2TEnRKMSaoNOa-QDzFsWxc3PhTDC1sRcttfsYDmW3LrXBsSaVHxAUdPveDCfKHQDoMCWN8rTBLN2GcOrqEKt85CkU1fhACt~g5asUYEvPeqVW3XS~AysfJmWWdVFdSCtQ2LkGGt1CZ9SMDThs1kqiMinly6Srpcj~eUZbb4iyumcfclmiVotLGhAkm8JNwWXJc8yfTxas1GxlTlmTaATt1J1LZS~WRTykILPGRY8HEQNp1j-vFgpXpI7xubNzPGCHQWAkOTsWfU8NvyGnXk481O6uRS5AAAA
ugha.i2p=318iVBG9JmuN5R~ClMb6rkWqkKoCZ3yGvD4dyIJAZum4GXFMHKSLrRiBf~2tFJ2KW1Lg9tKeyDgTC6sMt0aEQTcmzZl48BsFPZlH~WiI0JnPadihezDowBSdBhMi0RXoa3~xbfOKgAsHJv1zjrfRJHYz9fgG9bNQv9~oLeKz6YRdi97yrvKZLuzaUakYWfwdk7t9ZVhaXxsW8USXLSeHfdeQb0NYmsNc6is7Gp15HvsVUMgZXuBGea-AzMY1SSA6KdJwwPlXFvvcTM7neZKJzFVXyFsaOMEDjoUFfEC3tq277H0cqv4rAVmYp7WN78oBC7JgjfvFtXCY1r1l2-Qh1AEMwevVwI7tWdPUp50eMKEiccaHNZ7q2Zt0Uk0vlxKYW84p8ZTvXqfYDWyN8DQH2NKcy87MV1ZapDrbJSrF7cb-LvZy~nHtx~UwdKLS2gziM25JTtGiC1litArYS5KaY8rQXtL9kkSx9J66gc-05S-nbFMN-f-rEO9Fl27RvdQPAAAA
tor-www-proxy.i2p=9bZhTZvATJzpBa6UPslEwJCiDcsNhguT1mwbayD-rY0TN4Igj1PqPeVranzoO4Ity87ABVu2XJXatMzp~xHHtNiH6hF2XwJlpzx1Rr7A7SCTZwfBB1qLglwEBqYNEV5WT1faIDaArLc6o-ukhrOkIa8aJdaEpkjDkHjPtoOWt3nIIYlDxIliABrjFxPDeQZcOJyw7ftckqWfv7RKjdC8YpZrjXuojmi-TuhdRPu58tCNoH3j9laG4fUuU1RPK0YEj1HNSRHJVHDpCATtgHHfPiQvYK2HbMb0UBUfCtccmYqu6Cft5xZGHEOKvrMLXeEryV9ye-aczeNfBG2KzaF9pgQ2AN~eKBW7~UaNOQakyIaHDiY7aZ5qOCNW1CjuyJsEkjgRvqHogh6k5d3CkP82VlbEpTl5XaJFuflNJ0pHqZq2Le2T3wMkyECLbR0cX~qifE6Uw79AJnu-XEYQHFvHb0tV3XY2STDulgZu3fqZsjOVw2lZMHPHsszqlhiDhZIZAAAA
jar.i2p=xPIYObh2AirO1xoWCj7Wwc5RsGmQ3qulIAOHux9pOm9tzErjAfxv~2EazsZjyXCZ0zi3ylUjxqfj3L1pWEQBM-VM7HshHwg-PTuGWcdcUSRRFpQ7Gcp9u~~I3HSLRdHDj6ZkBNBk0jXM03tSKQEE4V1eum0tJwlOhBCNVqtt~FhyOBvo9~ypv6zW0sb6I3NppTYGq1LL4py4KrHSjb80e3Adfyhl1E2TfSHv6Uwp8qB~a2ac2IGhB2s-FK4gKSolpV-cUsn3roZRyq9jKJ2ciT4Y-PVcIDl6D8kV~LcNUbqD7vHy1yQxv1ByCCyIi3IYDacl5n5udwHO64L0M2mtZ1zWHS7K0~IxZTyk~mpO98qslbRfQqbk0ohZ-JGFhuB~cbVWlH6tSLmMJqmD-rOWnuxRfHLVtnbQstObQ9~KVIaX9KeLusgna2ZOhFjcy424BHtaVnbnLVyh-DEq~LkJGNx9Feyi6Z-aSQvThuhyE-ALiSvSw05x2G0yM9Me6MOWAAAA

278
installer/doc/COPYING Normal file
View File

@ -0,0 +1,278 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

View File

@ -0,0 +1,10 @@
$Id$
the i2p/installer/ module is the root of the I2P
installer, and everything within it is released
according to the terms of the I2P license policy.
That means everything contained within the
i2p/installer module is released into the public
domain unless otherwise marked. Alternate licenses
that may be used include GPL, GPL + java exception,
BSD, Cryptix, and MIT.

77
installer/java/build.xml Normal file
View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="installer">
<target name="all" depends="clean, build" />
<target name="build" depends="builddep, jar" />
<target name="builddep">
<ant dir="../../core/java/" target="build" />
<ant dir="../../router/java/" target="build" />
<ant dir="../../apps/ministreaming/java/" target="build" />
<ant dir="../../apps/i2ptunnel/java/" target="build" />
</target>
<target name="compile">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac srcdir="./src" debug="true" destdir="./build/obj" />
</target>
<target name="jar" depends="installer, guiinstaller" />
<target name="fetchseeds" depends="compile">
<jar destfile="./build/fetchseeds.jar" >
<fileset dir="./build/obj" includes="FetchSeeds.class" />
<fileset file="../doc/COPYING" />
<manifest>
<attribute name="Main-Class" value="FetchSeeds" />
</manifest>
</jar>
</target>
<target name="installer" depends="compile, fetchseeds">
<jar destfile="./build/install.jar" >
<fileset dir="./build/obj" includes="*.class" />
<fileset dir="./src" includes="*.template" />
<fileset dir="./src" includes="install.config" />
<fileset file="./build/fetchseeds.jar" />
<fileset file="../../core/java/build/i2p.jar" />
<fileset file="../../router/java/build/router.jar" />
<fileset file="../../apps/ministreaming/java/build/mstreaming.jar" />
<fileset file="../../apps/i2ptunnel/java/build/i2ptunnel.jar" />
<fileset file="../doc/COPYING" />
<fileset file="../../readme.txt" />
<fileset file="../../hosts.txt" />
<manifest>
<attribute name="Main-Class" value="Install" />
</manifest>
</jar>
</target>
<target name="guiinstaller" depends="compile, fetchseeds">
<jar destfile="./build/guiinstall.jar" >
<fileset dir="./build/obj" includes="*.class" />
<fileset dir="./src" includes="*.template" />
<fileset dir="./src" includes="install.config" />
<fileset file="./build/fetchseeds.jar" />
<fileset file="../../core/java/build/i2p.jar" />
<fileset file="../../router/java/build/router.jar" />
<fileset file="../../apps/ministreaming/java/build/mstreaming.jar" />
<fileset file="../../apps/i2ptunnel/java/build/i2ptunnel.jar" />
<fileset file="../doc/COPYING" />
<fileset file="../../readme.txt" />
<fileset file="../../hosts.txt" />
<manifest>
<attribute name="Main-Class" value="GUIInstall" />
</manifest>
</jar>
</target>
<target name="clean">
<delete dir="./build" />
</target>
<target name="cleandep" depends="clean">
<ant dir="../../core/java/" target="cleandep" />
<ant dir="../../router/java/" target="cleandep" />
<ant dir="../../apps/ministreaming/java/" target="cleandep" />
<ant dir="../../apps/i2ptunnel/java/" target="cleandep" />
</target>
<target name="distclean" depends="clean">
<ant dir="../../core/java/" target="distclean" />
<ant dir="../../router/java/" target="distclean" />
<ant dir="../../apps/ministreaming/java/" target="distclean" />
<ant dir="../../apps/i2ptunnel/java/" target="distclean" />
</target>
</project>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE attributes PUBLIC "-//NetBeans//DTD DefaultAttributes 1.0//EN" "http://www.netbeans.org/dtds/attributes-1_0.dtd">
<attributes version="1.0">
<fileobject name="startRouter.sh.template">
<attr name="org.netbeans.modules.text.IsTextFile" boolvalue="true"/>
</fileobject>
</attributes>

View File

@ -0,0 +1,67 @@
import java.io.*;
public class CliInstall extends Install {
private BufferedReader _in;
private PrintStream _out;
public CliInstall() {
_out = System.out;
_in = new BufferedReader(new InputStreamReader(System.in));
}
public void showStatus(String s) {
_out.println(s);
}
public void showOptError(String s) {
_out.println(s);
}
public void handleOptInfo(String s) {
_out.println(s);
}
public void startOptCategory(String s) {
_out.println("* "+s+"\n");
}
public void finishOptions() {}
public void handleOption(int number, String question,
String def, String type) {
Object value;
while(true) {
String answer;
_out.print(question+(def == null?"": (" ["+def+"]"))+": ");
answer = readLine();
if ("".equals(answer) && def != null) {
answer = def;
}
if (setOption(number,answer)) break;
}
}
public boolean confirmOption(String question, boolean defaultYes) {
_out.print(question);
return readBool(defaultYes);
}
private String readLine() {
try {
return _in.readLine().trim();
} catch (IOException ex) {
ex.printStackTrace();
System.exit(1);
return null;
}
}
private boolean readBool(boolean defaultYes) {
String str = readLine().toLowerCase();
if ("".equals(str)) return defaultYes;
return "yes".equals(str) || "y".equals(str) || "true".equals(str)
|| "ok".equals(str) || "sure".equals(str)
|| "whatever".equals(str);
}
}

View File

@ -0,0 +1,86 @@
/*
* $Id $
* Copyright (c) 2003 mihi
* Licensed under the GNU Public License (GPL) as published by the
* Free Software Foundation, using version 2 or later of the GPL. You
* should have recieved the GPL with this source code, otherwise see
* http://www.fsf.org/copyleft/
*/
import java.io.*;
import java.net.*;
public class FetchSeeds {
/**
* Fetch seednodes.
*
* @param destination the dir to store the seednodes to
* @param sourceURL the URL to fetch the seednode from - must end
* with a slash
* @return whether new seed nodes could be fetched
*/
public static boolean fetchSeeds(File destination, String sourceURL) {
InputStream in = null;
try {
URL source = new URL(sourceURL);
URLConnection con = source.openConnection();
in = con.getInputStream();
BufferedReader br = new BufferedReader
(new InputStreamReader(in));
String line;
while ((line = br.readLine())!= null) {
int pos = line.indexOf(" <a href=\"routerInfo-");
if (pos == -1) continue;
line=line.substring(pos+10);
pos=line.indexOf("\"");
if (pos == -1) continue;
line=line.substring(0,pos);
fetchFile(new File(destination, line), sourceURL+line);
System.out.println(line);
}
br.close();
return true;
} catch (IOException ex) {
System.err.println("Unable to fetch seeds from " + sourceURL + ": " + ex.getMessage());
//ex.printStackTrace();
return false;
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
}
public static void fetchFile(File destFile, String fileURL)
throws IOException {
URL url = new URL(fileURL);
InputStream in = url.openStream();
OutputStream out = new FileOutputStream(destFile);
byte[] buf = new byte[1024];
int len;
while ((len=in.read(buf)) != -1) {
out.write(buf,0,len);
}
in.close();
out.flush();
out.close();
}
/**
* test main method.
*/
public static void main(String[] args) {
switch (args.length) {
case 1:
fetchSeeds(new File(args[0]), "http://i2p.dnsalias.net/i2pdb/");
return;
case 2:
fetchSeeds(new File(args[0]), args[1]);
return;
default:
System.out.println("Usage: FetchSeeds <outDir>");
System.out.println(" or FetchSeeds <outDir> <seedURL>");
System.out.println("The default seedURL is http://i2p.dnsalias.net/i2pdb/");
return;
}
}
}

View File

@ -0,0 +1,338 @@
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.StringTokenizer;
public class GUIInstall extends Install {
static final GridBagConstraints gbcLeft=new GridBagConstraints();
static final GridBagConstraints gbcRight=new GridBagConstraints();
static final GridBagConstraints gbcBottom=new GridBagConstraints();
static {
gbcLeft.anchor=GridBagConstraints.EAST;
gbcRight.fill=GridBagConstraints.HORIZONTAL;
gbcRight.gridwidth=GridBagConstraints.REMAINDER;
gbcRight.weightx=1.0;
gbcBottom.weighty=1.0;
gbcBottom.gridwidth=GridBagConstraints.REMAINDER;
gbcBottom.fill=GridBagConstraints.BOTH;
gbcBottom.insets=new Insets(4,4,4,4);
}
public static void main(String[] args) {
new GUIInstall().runInstall();
}
private InstallFrame frame;
boolean installing=false;
ArrayList categories = new ArrayList();
InstallCategory currentCategory = null;
public String handleSpliceParams(String s) {
// any better ideas?
return s;
}
public GUIInstall() {
frame = new InstallFrame();
}
public void showStatus(String s) {
if (!installing)
throw new RuntimeException("Not installing yet!");
frame.showStatus(s);
}
public void showOptError(String s) {
frame.showOptError(s);
}
public void handleOptInfo(String s) {
currentCategory.addInfo(s);
}
public void startOptCategory(String s) {
currentCategory = new InstallCategory();
categories.add(currentCategory);
}
public void finishOptions() {
frame.startInstall(categories);
System.out.println("Starting install...");
}
public void handleOption(int number, String question,
String def, String type) {
currentCategory.addOption(number, question, def, type);
}
public boolean confirmOption(String question, boolean defaultYes) {
ConfirmFrame cf = new ConfirmFrame(frame, question, defaultYes);
return cf.getResult();
}
private class ConfirmFrame extends Dialog {
private boolean result;
private ConfirmFrame(Frame parent, String msg,
boolean defaultYes) {
super(parent,"Installer question",true);
setBackground(Color.lightGray);
setLayout(new BorderLayout());
TextArea ta;
Panel p;
Button b1, b2;
add("Center", ta = new TextArea(msg, 3, 80));
ta.setEditable(false);
add("South", p = new Panel(new FlowLayout()));
p.add(b1 = new Button("Yes"));
p.add(b2 = new Button("No"));
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
result = evt.getActionCommand().equals("Yes");
ConfirmFrame.this.dispose();
}
};
b1.addActionListener(al);
b2.addActionListener(al);
pack();
// java 1.4
//setLocationRelativeTo(parent);
show();
(defaultYes?b1:b2).requestFocus();
}
private boolean getResult() {
return result;
}
}
private class InstallCategory extends ArrayList {
public void addInfo(String s) {
add(new InfoOption(s));
}
public void addOption(int number, String question,
String def, String type) {
add(new RealOption(number, question, def, type));
}
}
private interface InstallOption {
public Component getComponent1();
public Component getComponent2();
public boolean setValue();
public String getQuestion();
}
private class InfoOption extends Panel implements InstallOption {
public InfoOption(String s) {
super(new GridLayout(0,1,0,0));
for(StringTokenizer st = new StringTokenizer(s,"\n");
st.hasMoreTokens();) {
add(new Label(st.nextToken()));
}
}
public Component getComponent1() { return null;}
public Component getComponent2() { return this;}
public boolean setValue() {return true;}
public String getQuestion() { return "<no question>";}
}
private class RealOption implements InstallOption {
private int number;
private String def, question;
private Label l;
private TextField t;
public RealOption(int number, String question,
String def, String type) {
this.number = number;
l = new Label(question);
t = new TextField(def);
this.def=def;
this.question=question;
// type is not needed yet
}
public void reset() {t.setText(def);}
public String getQuestion() { return question; }
public boolean setValue() {
return GUIInstall.this.setOption(number, t.getText());
}
public Component getComponent1() { return l;}
public Component getComponent2() { return t;}
}
private class InstallFrame extends Frame {
private int current = -1;
private Panel cats;
private CardLayout cl;
private boolean windowOpen = true;
private TextArea log;
public InstallFrame() {
super("I2P Installer");
setBackground(Color.lightGray);
Panel p;
Button b;
setLayout(new BorderLayout());
add("Center", cats = new Panel(cl = new CardLayout()));
cats.add("Start", p= new Panel(new BorderLayout()));
p.add("Center", new Label("Loading installer..."));
cats.add("Install", p= new Panel(new BorderLayout()));
p.add("Center", log=new TextArea("Installing...\n\n"));
log.setEditable(false);
add("South", p = new Panel(new FlowLayout()));
p.add(b = new Button("<< Back"));
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (current > 0) {
current --;
cl.show(cats,""+current);
}
}
});
p.add(b = new Button("Next >>"));
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if (current != -1) {
if (!saveCurrent()) return;
current ++;
if (current == categoryPanels.length) {
cl.show(cats,"Install");
current = -1;
synchronized(InstallFrame.this) {
installing=true;
windowOpen=false;
InstallFrame.this.notify();
}
} else {
cl.show(cats,""+current);
}
}
}
});
p.add(b = new Button("Quit"));
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.exit(0);
}
});
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent evt) {
System.exit(0);
}
});
setSize(600,450);
// java 1.4
//setLocationRelativeTo(null);
show();
}
public void showStatus(String s) {
log.append(s+"\n");
}
public void showOptError(String s) {
if (current == -1) throw new RuntimeException("No options here!");
categoryPanels[current].showError(s);
}
private CategoryPanel[] categoryPanels;
public void startInstall(ArrayList categories) {
Panel p;
categoryPanels = new CategoryPanel[categories.size()];
//build a panel for each category
Iterator it = categories.iterator();
for (int i=0; it.hasNext(); i++) {
cats.add(""+i, categoryPanels[i] =
new CategoryPanel((InstallCategory)it.next()));
}
current = 0;
cl.show(cats,"0");
// wait till config is complete
synchronized(this) {
while(windowOpen) {
try {
wait();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
private boolean saveCurrent() {
return categoryPanels[current].saveOptions();
}
}
private class CategoryPanel extends Panel {
private TextArea errorBox;
private InstallCategory ic;
public CategoryPanel(InstallCategory ic) {
super(new GridBagLayout());
this.ic=ic;
for (Iterator it = ic.iterator(); it.hasNext();) {
InstallOption io = (InstallOption) it.next();
Component c1 = io.getComponent1(),
c2 = io.getComponent2();
if (c1 != null) add(c1, gbcLeft);
add(c2, gbcRight);
}
add (errorBox = new TextArea(), gbcBottom);
errorBox.setEditable(false);
}
private InstallOption currentOption;
public boolean saveOptions() {
errorBox.setText("Saving options...\n\n");
for (Iterator it = ic.iterator(); it.hasNext();) {
InstallOption io = (InstallOption) it.next();
currentOption=io;
if (!io.setValue()) return false;
currentOption= null;
}
return true;
}
public void showError(String s) {
if (currentOption==null) {
throw new RuntimeException("No option to test");
}
errorBox.append("While setting \""+currentOption.getQuestion()+
"\":\n"+s+"\n\n");
}
}
}

View File

@ -0,0 +1,612 @@
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
/**
* Please could someone write a real installer? Please.
* I should be shot for doing this.
*
* Note: this has dependencies upon how i2p/build.xml executes the "installer"
* task - namely that this class is in the jar "install.jar", and in that jar
* are the files COPYING, i2p.jar, i2ptunnel.jar, phttprelay.war, readme.txt,
* hosts.txt, and router.jar.
*
* Can you say "fragile code"?
* (oh well, enough caveats. commit)
*/
public abstract class Install {
private File _installDir;
private String _externalAddress;
private boolean _externalAddressIsReachable;
private int _inTCP;
private int _i2cpPort;
private String _phttpRegister;
private String _phttpSend;
private int _inBPS;
private int _outBPS;
private HashMap _answers;
private Properties _p;
private boolean _isWindows;
/**
* Show some installation status.
*/
public abstract void showStatus(String s);
/**
* Show an error when setting an option.
*/
public abstract void showOptError(String s);
/**
* Show some information about the following/preceding options
*/
public abstract void handleOptInfo(String s);
/**
* Start a new Option category
*/
public abstract void startOptCategory(String s);
/**
* Handle an option.
*/
public abstract void handleOption(int number, String question,
String def, String type);
/**
* Tell the installer that all options have been given.
* Install will not continue until this method has ended.
* When this method has ended, all options must have been set.
*/
public abstract void finishOptions();
/**
* Confirm an option. This can occur both while verifying options
* and later in the install process.
*/
public abstract boolean confirmOption(String question, boolean defaultYes);
public static void main(String args[]) {
Install install = new CliInstall();
install.runInstall();
}
public Install() {
_inTCP = -2;
_i2cpPort = -2;
_phttpRegister = null;
_phttpSend = null;
_inBPS = -2;
_outBPS = -2;
_externalAddressIsReachable = false;
}
public void runInstall() {
askQuestions();
detectOS();
configureAll();
createConfigFile();
createLoggerConfig();
createStartScript();
createReseedScript();
createEepProxyScript();
//createScripts("startSquid.sh", "startSquid.bat", 5555, "squid.i2p", "log-squid-#.txt", "Squid Proxy", "Squid proxying scripts written to startSquid");
createScripts("startIrcProxy.sh", "startIrcProxy.bat", 6668, "irc.duck.i2p", "log-irc-#.txt", "IRC Proxy", "IRC proxying scripts written to startIrcProxy", "Starting IRC proxy (when you see Ready! you can connect your IRC client to localhost:6668)");
//createScripts("startI2PCVSProxy.sh", "startI2PCVSProxy.bat", 2401, "i2pcvs.i2p", "log-i2pcvs-#.txt", "CVS Proxy", "Proxying scripts for I2P's CVS server written to startCVSProxy");
// only pulling them temporarily, duck, until the network is
// reliable enough
//createScripts("startJabber.sh", "startJabber.bat", 5222, "jabber.duck.i2p", "log-jabber-#.txt", "Jabber Proxy", "Squid proxying scripts written to startSquid");
//createScripts("startNntpProxy.sh", "startNntpProxy.bat", 1119, "nntp.duck.i2p", "log-nntp-#.txt", "NNTP Proxy","NNTP proxying scripts written to startNntpProxy");
createSeedNodes();
copyLibraries();
if (_isWindows) {
showStatus("To run the router, please run startRouter.bat in " + _installDir.getAbsolutePath());
} else {
showStatus("To run the router, please run startRouter.sh in " + _installDir.getAbsolutePath());
}
showStatus("");
}
private String numberTo4Digits(int number) {
String res = "0000"+number; // use four digit indices
return res.substring(res.length()-4);
}
private void askQuestions() {
try {
InputStream in =
Install.class.getResourceAsStream("/install.config");
_p = new Properties();
_p.load(in);
in.close();
} catch (IOException ex) {
ex.printStackTrace();
System.exit(1);
}
int count = Integer.parseInt(_p.getProperty("qs.count"));
_answers = new HashMap(count+count); // load factor is 0.75, so
// there is some room left
for (int i=1;i<=count;i++) {
String ii = numberTo4Digits(i);
String question = _p.getProperty("qs."+ii+".question"),
param = _p.getProperty("qs."+ii+".param"),
type = _p.getProperty("qs."+ii+".type"),
def = _p.getProperty("qs."+ii+".default");
if (question == null) continue;
if (type == null || "info".equals(type)) {
// just print some text
handleOptInfo(question);
} else if ("info_spliced".equals(type)) {
// splice in some params already queried
handleOptInfo(handleSpliceParams(question));
} else if ("category".equals(type)) {
startOptCategory(question);
} else if ("skip".equals(type)) {
i = Integer.parseInt(question)-1;
} else { // a real question
if ("<none>".equals(question)) {
if (!setOption(i, def))
throw new RuntimeException("Fixed and invalid value");
} else {
handleOption(i,question,def,type);
}
}
}
finishOptions();
}
public /* overridable */ String handleSpliceParams(String s) {
return spliceParams(s);
}
public boolean setOption(int number, String answer) {
String ii = numberTo4Digits(number);
String param = _p.getProperty("qs."+ii+".param"),
type = _p.getProperty("qs."+ii+".type");
Object value = getOptionValue(answer, type);
if (value == null) {
return false;
} else {
if (param == null || value == null)
throw new NullPointerException();
_answers.put(param,value);
return true;
}
}
private Object getOptionValue(String answer, String type) {
if ("string".equals(type)) {
// everything's okay till the very end
return answer;
} else if ("string>0".equals(type)) {
if (answer.length() > 0) {
return answer;
} else {
showOptError("Empty answers are not allowed.");
}
} else if ("directory".equals(type)) {
File f = new File(answer);
if (f.exists()) {
if (f.isDirectory()) {
showOptError("Using existing target directory " + f.getAbsolutePath());
return f;
} else {
showOptError("Location " + f.getAbsolutePath()+
" is not a directory. "+
"Lets try again");
}
} else {
boolean create = confirmOption
("Target directory " + f.getAbsolutePath() +
" does not exist - create? ", false);
if (!create) {
showOptError("Lets try that again");
} else {
boolean created = f.mkdirs();
if (created) {
showOptError("Target directory " +
f.getAbsolutePath() +
" created");
return f;
} else {
showOptError("Failed to create the "+
"directory. Lets choose "+
"another.");
}
}
}
} else if ("boolean".equals(type)) {
answer=answer.toLowerCase();
if ("yes".equals(answer) || "y".equals(answer))
answer="true";
if ("no".equals(answer) || "n".equals(answer))
answer="false";
if ("true".equals(answer) || "false".equals(answer)) {
return new Boolean("true".equals(answer));
}
showOptError("Incorrect boolean value, try `yes' <20>r `no'");
} else if ("numeric".equals(type) || "port".equals(type)) {
try {
int num = Integer.parseInt(answer);
if ("numeric".equals(type) ||
(num >0 && num < 65536)) {
return new Integer(num);
}
showOptError("Port number must be from 1 to 65535");
} catch (NumberFormatException ex) {
showOptError("Incorrect value: "+ex.getMessage());
}
} else if ("bandwidth".equals(type)) {
try {
answer = answer.toLowerCase();
int factor = 1;
// first check to see if it ends with m, k, or g
if (answer.endsWith("g"))
factor = 1024*1024*1024;
if (answer.endsWith("m"))
factor = 1024*1024;
if (answer.endsWith("k"))
factor = 1024;
if (factor > 1)
answer = answer.substring(0, answer.length()-1);
int val = factor * Integer.parseInt(answer);
if (val == -1 || val >0 ) {
return new Integer(val);
}
showOptError("Value must be -1 or positive.");
} catch (NumberFormatException ex) {
showOptError("Invalid number [" + answer + "]. Valid numbers are of the form -1, 42, 68k, 7m, 9g");
}
} else {
throw new RuntimeException ("cannot read installer option: " + type);
}
return null;
}
private String spliceParams(String s) {
StringBuffer txt = new StringBuffer(s.length()+100);
int ind;
while((ind = s.indexOf("##")) != -1) {
txt.append(s.substring(0,ind));
String temp = s.substring(ind+2);
ind = temp.indexOf("##");
if (ind == -1) throw new RuntimeException
("Incorrect info_spliced param");
s=temp.substring(ind+2);
Object value = _answers.get(temp.substring(0,ind));
if (value == null) {
System.err.println("ERROR: Could not insert parameter "+temp.substring(0,ind));
System.exit(1);
} else {
txt.append(value.toString());
}
}
txt.append(s);
return txt.toString();
}
private void detectOS() {
String os = System.getProperty("os.name");
if (os.toLowerCase().indexOf("win") != -1)
_isWindows = true;
else
_isWindows = false;
// yes, this treats pre-os-x macs as unix, and perhaps some
// windows-esque OSes don't have "win" in their name, or some
// unix-esque OS does. fix when it occurs.
}
private void configureAll() {
_installDir = (File) _answers.get("installDir");
_externalAddress = _answers.get("externalAddress").toString();
_externalAddressIsReachable = ((Boolean)_answers.get("externalAddressIsReachable")).booleanValue();
_inTCP=((Integer)_answers.get("inTCP")).intValue();
_phttpRegister = _answers.get("phttpRegister").toString();
_phttpSend = _answers.get("phttpSend").toString();
_i2cpPort = ((Integer)_answers.get("i2cpPort")).intValue();
_inBPS = ((Integer)_answers.get("inBPS")).intValue();
_outBPS = ((Integer)_answers.get("outBPS")).intValue();
}
private void useTemplate(String templateName, File destFile) {
try {
BufferedWriter bw = new BufferedWriter(new FileWriter(destFile));
BufferedReader br = new BufferedReader
(new InputStreamReader
(Install.class.getResourceAsStream(templateName)));
String line;
while ((line = br.readLine()) != null) {
if (!line.startsWith("####")) {
bw.write(spliceParams(line));
bw.newLine();
}
}
br.close();
bw.close();
} catch (IOException ioe) {
ioe.printStackTrace();
System.exit(0);
}
}
private void createLoggerConfig() {
boolean verbose = ((Boolean)_answers.get("verboseLogs")).booleanValue();
createLogConfigOptions(verbose);
File logCfgFile = new File(_installDir, "logger.config");
useTemplate("logger.config.template",logCfgFile);
}
private void createSeedNodes() {
showStatus("To connect to I2P, you will need a reference to at least one other I2P router");
showStatus("Rather than bundle some (soon to be out of date) references with the software, ");
showStatus("you can either run the included reseed script or get get your own references ");
showStatus("from some out of band location. ");
showStatus("");
showStatus("The reseed script simply connects to http://i2p.net/i2pdb/ and downloads all");
showStatus("of the routerInfo-*.dat files and save them into " + (new File(_installDir, "i2pdb")).getAbsolutePath());
showStatus("That ../i2pdb/ directory is simply a mirror of one router's netDb directory, so those files");
showStatus("can come from anyone else too");
showStatus("");
showStatus("You can run the reseed script or download references (from your friends, etc) as often");
showStatus("as you like without restarting your router. If you find your netDb directory to have ");
showStatus("only one file in it (thats your router info), you will need more peers to get anything done.");
showStatus("");
boolean reseed = confirmOption("Do you want to run the reseed script now? ", true);
if (reseed) {
reseed();
} else {
showStatus("Ok ok, not reseeding - but please reseed before running the router");
}
}
private void reseed() {
try {
URL dir = new URL("http://i2p.net/i2pdb/");
String content = new String(readURL(dir));
Set urls = new HashSet();
int cur = 0;
while (true) {
int start = content.indexOf("href=\"routerInfo-", cur);
if (start < 0)
break;
int end = content.indexOf(".dat\">", start);
String name = content.substring(start+"href=\"routerInfo-".length(), end);
urls.add(name);
cur = end + 1;
}
for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
fetchSeed((String)iter.next());
}
} catch (Throwable t) {
t.printStackTrace();
showStatus("Error reseeding - " + t.getMessage());
}
}
private void fetchSeed(String peer) throws Exception {
URL url = new URL("http://i2p.net/i2pdb/routerInfo-" + peer + ".dat");
showStatus("Fetching seed from " + url.toExternalForm());
byte data[] = readURL(url);
writeSeed(peer, data);
}
private byte[] readURL(URL url) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
URLConnection con = url.openConnection();
InputStream in = con.getInputStream();
byte buf[] = new byte[1024];
while (true) {
int read = in.read(buf);
if (read < 0)
break;
baos.write(buf, 0, read);
}
in.close();
return baos.toByteArray();
}
private void writeSeed(String name, byte data[]) throws Exception {
File netDbDir = new File(_installDir, "netDb");
if (!netDbDir.exists())
netDbDir.mkdirs();
FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat"));
fos.write(data);
fos.close();
}
private void copyLibraries() {
File libDir = new File(_installDir, "lib");
if (!libDir.exists()) {
boolean libCreated = libDir.mkdirs();
if (!libCreated) {
showStatus("Error creating library directory " + libDir.getAbsolutePath());
return;
}
}
showStatus("Installing the libraries into " + libDir.getAbsolutePath());
int cnt = Integer.parseInt(_p.getProperty("libs.count"));
try {
for (int i=1;i<=cnt;i++) {
String ii = numberTo4Digits(i),
file = _p.getProperty("libs."+ii+".name");
boolean isLib = "true".equals(_p.getProperty("libs."+ii+".islib"));
InputStream is = Install.class.getResourceAsStream("/"+file);
if (is == null) throw new IOException("Resource /"+file+" not found");
copyFile(is, file, isLib?libDir:_installDir);
}
} catch (IOException ioe) {
showStatus("Error extracting the libraries: " + ioe.getMessage());
}
File dbDir = new File(_installDir, "netDb");
dbDir.mkdirs();
File logDir = new File(_installDir, "logs");
logDir.mkdirs();
}
private void copyFile(InputStream in, String name, File destDir) {
File destFile = new File(destDir, name);
try {
byte buf[] = new byte[16*1024];
FileOutputStream out = new FileOutputStream(destFile);
while (true) {
int read = in.read(buf);
if (read == -1)
break;
out.write(buf, 0, read);
}
in.close();
out.close();
showStatus("Installed file " + destFile.getName() + " in " + destFile.getParent());
} catch (IOException ioe) {
showStatus("Error saving " + name + " to " + destFile.getAbsolutePath() + ": " + ioe.getMessage());
}
}
private void createLogConfigOptions(boolean verbose) {
_answers.put("_logger_level", verbose?"DEBUG":"INFO");
_answers.put("_logger_level2", verbose?"WARN":"ERROR");
StringBuffer buf = new StringBuffer();
if (!verbose) {
// overrides for particularly chatty classes
_answers.put("_logger_notverbose",
"logger.record.net.i2p.router.transport.Triv=ERROR"+NL+
"logger.record.net.i2p.router.transport.Band=ERROR"+NL+
"logger.record.net.i2p.crypto=ERROR" +NL+
"logger.record.net.i2p.crypto.DH=ERROR");
} else {
_answers.put("_logger_notverbose","");
}
}
private void createScripts(String unixName, String windowsName, int listenPort, String targetDest, String logfilePattern, String windowTitle, String message, String scriptMessage) {
createScripts(unixName, windowsName, "client "+listenPort+" "+targetDest, logfilePattern, windowTitle, message, scriptMessage);
}
private void createScripts(String unixName, String windowsName, String command, String logfilePattern, String windowTitle, String message, String scriptMessage) {
_answers.put("_scripts_port", ""+_i2cpPort);
_answers.put("_scripts_cmd", command);
_answers.put("_scripts_logname", logfilePattern);
_answers.put("_scripts_winttl", windowTitle);
_answers.put("_scripts_message", scriptMessage);
if (_isWindows) {
File windowsFile = new File(_installDir, windowsName);
useTemplate("startFoo.bat.template", windowsFile);
} else {
File unixFile = new File(_installDir, unixName);
useTemplate("startFoo.sh.template", unixFile);
chmodaplusx(unixFile);
}
showStatus(message);
}
private void createEepProxyScript() {
StringBuffer buf = new StringBuffer(512);
buf.append("Eepsite proxying scripts written to startEepProxy").append(NL);
buf.append("IMPORTANT: While this installer packages the latest hosts.txt file available at the time ($Date: 2004/03/25 00:56:23 $), ").append(NL);
buf.append("a more recently updated one may be available. You can check for updates by fetching the file ").append(NL);
buf.append("http://i2p.net/i2p/hosts.txt and saving it in ").append(_installDir.getAbsolutePath()).append(NL);
buf.append("Or, of course, you can edit and update hosts.txt yourself to include mappings of your liking").append(NL);
createScripts("startEepProxy.sh", "startEepProxy.bat", "httpclient 4444", "log-eepProxy-#.txt", "Eep Proxy", buf.toString(), "Starting EepProxy (when you see Ready, set your browsers HTTP proxy to localhost:4444)");
}
private void createReseedScript() {
if (_isWindows) {
File windowsFile = new File(_installDir, "reseed.bat");
useTemplate("reseed.bat.template", windowsFile);
} else {
File unixFile = new File(_installDir, "reseed.sh");
useTemplate("reseed.sh.template", unixFile);
chmodaplusx(unixFile);
}
}
private void chmodaplusx(File f) {
try {
Runtime.getRuntime().exec("chmod a+x " + f.getAbsolutePath());
} catch (IOException ioe) {
showStatus("Error setting "+f.getName()+" as executable");
}
}
private void createStartScript() {
_answers.put("_scripts_installdir", _installDir.getAbsolutePath());
if (_isWindows) {
File windowsFile = new File(_installDir, "startRouter.bat");
useTemplate("startRouter.bat.template", windowsFile);
} else {
File unixFile = new File(_installDir, "startRouter.sh");
useTemplate("startRouter.sh.template", unixFile);
File unixStopFile = new File(_installDir, "stopRouter.sh");
useTemplate("stopRouter.sh.template", unixStopFile);
chmodaplusx(unixFile);
chmodaplusx(unixStopFile);
}
}
private void createConfigFile() {
File configFile = new File(_installDir, "router.config");
setConfigFileOptions();
useTemplate("router.config.template", configFile);
showStatus("Router configuration file written to " + configFile.getAbsolutePath());
showStatus("");
}
private final static String NL = System.getProperty("line.separator");
private void setConfigFileOptions() {
// set fields needed for the config template
_answers.put("NOW", new Date().toString());
if (_inTCP <= 0) {
_answers.put("_router_hn", "#i2np.tcp.hostname=[externally reachable hostname or IP address goes here]");
_answers.put("_router_port", "#i2np.tcp.port=[TCP/IP port number]");
_answers.put("_router_lavalid","#i2np.tcp.listenAddressIsValid=[true/false for whether your external address is locally reachable]");
_answers.put("_router_tcpdisable","#i2np.tcp.disable=[true/false for whether you want absolutely no tcp connections to be established (forcing phttp, etc)])");
} else {
_answers.put("_router_hn","i2np.tcp.hostname="+_externalAddress);
_answers.put("_router_port","i2np.tcp.port="+_inTCP);
_answers.put("_router_lavalid","i2np.tcp.listenAddressIsValid="+_externalAddressIsReachable);
_answers.put("_router_tcpdisable","i2np.tcp.disable=false");
}
if ( (_phttpRegister == null) || (_phttpSend == null) ) {
_answers.put("_router_phttpreg","#i2np.phttp.registerURL=[full URL to a PHTTP registration server, e.g. http://someHost:8080/phttprelay/phttpRegister]");
_answers.put("_router_phttpsend","#i2np.phttp.sendURL=[full URL to a PHTTP relay server, e.g. http://someHost:8080/phttprelay/phttpSend]");
} else {
_answers.put("_router_phttpreg","i2np.phttp.registerURL="+_phttpRegister);
_answers.put("_router_phttpsend","i2np.phttp.sendURL="+_phttpSend);
}
_answers.put("_router_i2cp_port",""+_i2cpPort);
_answers.put("_router_inbps",""+(_inBPS*60));
_answers.put("_router_outbps",""+(_outBPS*60));
}
}

View File

@ -0,0 +1,108 @@
# config file for the installer
# intention for this file is that a gui installer can use the same
# settings as the cli installer
qs.count=50
qs.0001.question=General settings
qs.0001.type=category
qs.0002.question=Installation directory
qs.0002.param=installDir
qs.0002.type=directory
qs.0002.default=.
qs.0003.question=
qs.0006.question=Network settings
qs.0006.type=category
qs.0007.question=Currently, to use I2P you must have a publicly reachable TCP/IP address \nthat you can receive new connections on. This means that if you're\nbehind a firewall or NAT, you will have to poke a hole in it for the \ninbound TCP port (which you'll specify in a moment), and if you have DHCP,\nyou must use a service like dyndns.org and specify that hostname as your\nexternal address.
qs.0007.type=info
qs.0008.question=External address
qs.0008.param=externalAddress
qs.0008.type=string>0
qs.0009.question=Note to advanced users: the default settings bind any TCP listeners to \nall local IP addresses, but you can have it bind to a specific one if necessary\nPlease see the router.config for the options i2np.tcp.listenAddressIsValid
qs.0009.type=info
qs.0010.question=<none>
qs.0010.param=externalAddressIsReachable
qs.0010.type=boolean
qs.0010.default=false
qs.0011.question=Inbound TCP port? (any unused port will do, just pick a number)
qs.0011.param=inTCP
qs.0011.type=port
qs.0011.default=8887
qs.0012.question=If you have a NAT or firewall, please be sure port ##inTCP## is open and can receive inbound connections!
qs.0012.type=info_spliced
qs.0015.question=<none>
qs.0015.param=phttpRegister
qs.0015.type=string>0
qs.0015.default=http://i2p.net:8080/phttprelay/phttpRegister
qs.0016.question=<none>
qs.0016.param=phttpSend
qs.0016.type=string>0
qs.0016.default=http://i2p.net:8080/phttprelay/phttpSend
qs.0017.question=<none>
qs.0017.param=i2cpPort
qs.0017.type=port
qs.0017.default=7654
qs.0018.question=
qs.0019.question=40
qs.0019.type=skip
## leave some numbers free, for future additions
## bandwidth questions, skipped over
qs.0030.question=Bandwidth limits
qs.0030.type=category
qs.0031.question=These bandwidth limits are fairly hard and unforgiving.\nWe do our best to not let any data beyond these limits be transferred at all, ever.\nSo keep that in mind, and set the limits to the upper bounds of what you can handle.\n\nPlease take note that these bandwidth limits are currently very, very strict, and the network itself does not consume\nmuch bandwidth. Everything will stop transferring data without warning if the limits are met - you really should set\nthis to -1 for now, at least until the new classed bandwidth limiter is implemented (currently planned for the 0.3.2 release)
qs.0031.type=info
qs.0032.question=
qs.0033.question=Inbound bytes per second: (e.g. 16K, 16384, or -1 for unlimited)
qs.0033.param=inBPS
qs.0033.type=bandwidth
qs.0033.default=-1
qs.0034.question=Outbound bytes per second: (e.g. 16K, 16384, or -1 for unlimited)
qs.0034.param=outBPS
qs.0034.type=bandwidth
qs.0034.default=-1
qs.0035.question=
qs.0036.question=45
qs.0036.type=skip
qs.0040.question=<none>
qs.0040.param=inBPS
qs.0040.default=-1
qs.0040.type=numeric
qs.0041.question=<none>
qs.0041.param=outBPS
qs.0041.default=-1
qs.0041.type=numeric
qs.0042.question=45
qs.0042.type=skip
qs.0045.question=<none>
#qs.0045.question=Do you want very verbose debug logs when running the router by default (y/n)
qs.0045.param=verboseLogs
qs.0045.default=no
qs.0045.type=boolean
qs.0050.question=End of configuration.
libs.count=8
libs.0001.name=i2p.jar
libs.0001.islib=true
libs.0002.name=i2ptunnel.jar
libs.0002.islib=true
libs.0003.name=router.jar
libs.0003.islib=true
libs.0004.name=fetchseeds.jar
libs.0004.islib=true
libs.0005.name=COPYING
libs.0005.islib=false
libs.0006.name=readme.txt
libs.0006.islib=false
libs.0007.name=hosts.txt
libs.0007.islib=false
libs.0008.name=mstreaming.jar
libs.0008.islib=true

View File

@ -0,0 +1,66 @@
# I2P Log Configuration File
# Format:
# d = date
# c = class or log name
# t = thread name
# p = priority level of the log record
# m = message
logger.format=d p [t] c: m
# Date format:
# uses the java.text.SimpleDateFormat
logger.dateFormat=HH:mm:ss.SSS
# Log file name:
# This is the log file name before being rotated
# '#' is replaced with the current log number for that day
# If # is not specified, logs are not rotated
logger.logFileName=log-#.txt
# Log file size:
# Maximum size of each log file:
# 32g <-- 32 gigabytes (32*1024*1024*1024 bytes)
# 10m <-- 10 megabytes (10*1024*1024 bytes)
# 66k <-- 66 kilobytes (66*1024 bytes)
# 42 <-- 42 bytes
logger.logFileSize=3m
# Log rotation limit:
# Maximum number of logs to keep per day - lower log numbers
# are reused once the limit is reached. Ignored if there is no
# '#' in the logFileName.
logger.logRotationLimit=3
# Display on screen:
# In addition to the logging, send filtered log messages to the
# standard output
# true or false
logger.displayOnScreen=true
# Default level:
# Define the minimum log level to be displayed unless
# specified otherwise.
#
# Log levels, from least severe to most, are:
# DEBUG : verbose debugging info
# INFO : component status messages
# WARN : bad situation but recoverable
# ERROR : component error
# CRIT : your hard drive is on fire
#
# Less severe levels always include more severe ones (e.g.
# if you're listening to debug messages, you'll get info messages
# too)
logger.defaultLevel=##_logger_level##
# Minimum log level for a record to be displayed on the screen
# This check occurs after other filters occur, and only affects what is
# show on console, and only does anything if displayOnScreen=true
logger.minimumOnScreenLevel=DEBUG
# Records:
# Override the defaultLevel for all classes under the given package
# or class
logger.record.net.i2p=##_logger_level2##
##_logger_notverbose##

View File

@ -0,0 +1,3 @@
cd ##_scripts_installdir##
java -jar lib\fetchseeds.jar netDb
pause

View File

@ -0,0 +1,4 @@
#!/bin/sh
cd ##_scripts_installdir##
java -jar lib/fetchseeds.jar netDb
echo Router network database reseeded

View File

@ -0,0 +1,152 @@
# I2P router configuration
# Created on ##NOW##
# TCP configuration, for inbound TCP/IP connections
##_router_hn##
##_router_port##
##_router_lavalid##
# unless you really really know what you're doing, keep listenAddressIsValid=false
##_router_tcpdisable##
# maximum number of TCP connections we will want to
# attempt to establish at once (each of which
# requires a 2048bit DH exchange)
i2np.tcp.concurrentEstablishers=5
# Polling HTTP configuration, which is used to keep your router's clock in sync
# [also for communication when no inbound connections are possible, once its fixed up again]
##_router_phttpreg##
##_router_phttpsend##
# The following option specifies whether the router wants to keep the router's internal time in sync
# with the PHTTP relay's clock (which should be NTP synced). If however you are sure your local machine
# always has the correct time, you can set this to false (but your clock MUST be synced - see
# http://wiki.invisiblenet.net/iip-wiki?I2PTiming for more info.
i2np.phttp.trustRelayTime=true
# I2CP client port, for client connections
i2cp.port=##_router_i2cp_port##
# I2P router administrative web port (currently only responds to /routerConsole.html)
router.adminPort=7655
# Bandwidth limits
# These limits are for all i2np connections - tcp or whatever
# They are hard enforced with no smoothing.
# XXX Until the 0.3.2 release, these should NOT BE USED. Their values will be ignored!!!
i2np.bandwidth.inboundBytesPerMinute=##_router_inbps##
i2np.bandwidth.outboundBytesPerMinute=##_router_outbps##
# Publish peer rankings
# If true, include the current liveliness and reliability rankings in one's published RouterInfo data
# Setting this to true will help debug the network and is especially useful while we'return still testing
# However, traffic analysis may be easier with this data published (though there's no reason to think people
# can't just fake the info in this).
# Since we're still very much < 1.0, this will be true for the current release by default. As we get some
# network helth information and tune the ranking algorithms, this will become false by default.
# You, of course, can change this to either true or false whenever you'd like. This is only read
# on router startup though, so you need to restart the router if you change it.
router.publishPeerRankings=true
# Keep message history
# This series of options can help out in debugging the network by keeping a
# seperate log of all messages sent over the network (but without any personally identifiable information)
# This is entirely optional, but would be greatly appreciated during the
# development phase of the network since it would allow the developers to detect
# errors much more easily
router.keepHistory=false
# Submit message history
# This option works only if router.keepHistory is true and periodically sends
# in the router history logs to the developers (specifically, it submits the file
# via HTTP POST to http://i2p.net/cgi-bin/submitMessageHistory - you can see a sample of what
# one of those files looks like at http://i2p.net/~jrandom/sampleHist.txt)
# After submitting this file, it deletes the local copy (otherwise the file will grow
# without bound - tens of MB per day)
# Again, this is entirely optional, but would be greatly appreciated as it should help
# out the development process
router.submitHistory=false
# If your router is really slow, you'll need to update the following job parameters
# limit the maximum number of concurrent operations
router.maxJobRunners=1
# if a job waits more than this amount of time (in
# milliseconds) before running, spit out a warning
router.jobLagWarning=8000
# if a job waits more than this amount of time (in
# milliseconds) before running, kill the router
router.jobLagFatal=30000
# if a job takes more than this amount of time (in
# milliseconds) to run, spit out a warning
router.jobRunWarning=8000
# if a job takes more than this amount of time (in
# milliseconds) to run, kill the router
router.jobRunFatal=30000
# wait until the router has been up for this long
# (in milliseconds) before honoring any fatalities
# since during startup, jobs are run sequentially
# and CPU intensive tasks are needed
router.jobWarmupTime=600000
# Target clients
# How many concurrent clients the router should prepare for
# This, factored in with the tunnel settings, determines the size of the pools -
# too many, and your machine consumes excessive CPU and bandwidth, too few and your
# clients take too long to startup.
# e.g. If you are running an eepsite, an eepProxy, an irc proxy, and a squid proxy, set this to 4
router.targetClients=2
# Number of inbound tunnels per client
# This determines how many inbound tunnels will be allocated per client at a time.
# This is a key factor in the reliability of a client receiving messages
# As above, too many and your machine gets hosed, too few and the pool is slow.
# 2 should be sufficient - prior to 0.2.5, we have all had only 1
tunnels.numInbound=2
# Number of outbound tunnels per client
# This determines how many outbound tunnels must exist when a client is in operation.
# XXX Not currently enforced - ignore this setting
tunnels.numOutbound=2
# Depth of inbound tunnels
# This determines the length of inbound tunnels created - how many remote routers to
# include (0 means no remote routers, 3 means a total of four routers, including
# the local one, etc). This is a key factor in the reliability and anonymity
# provided by I2P
# Users should simply leave this as 2 for now, at least until the tunnels are more reliable (post 0.3)
tunnels.depthInbound=2
# Depth of outbound tunnels
# This determines the length of outbound tunnels created - how many remote routers to
# include (0 means no remote routers, 3 means a total of four routers, including
# the local one, etc). This is a key factor in the reliability and anonymity
# provided by I2P
# Users should simply leave this as 2 for now, at least until the tunnels are more reliable (post 0.3)
tunnels.depthOutbound=2
# Tunnel duration
# This determines how long tunnels we create should last for (in milliseconds). Too
# long and they are more prone to failure, too short and people need to do more network
# database lookups. The default of 10 minutes (600000 ms) should be used
# You should not change this setting unless you really know what you're doing
tunnels.tunnelDuration=600000
# Max waiting jobs
# If your router is getting heavily overloaded (due to slow CPU or excess network
# activity), your router's performance will seriously degrade, increasing its
# load further and delaying any messages sent through your router. The max waiting
# jobs configuration parameter is a throttle, saying that if there are more than
# that many 'jobs' that want to run ASAP at any given time, additional jobs may
# be summarily dropped. That will reduce your load and cause others to reduce
# their dependence on you (further reducing your load). The default value of 20
# should be sufficient, but may be increased if desired. Less than 20 is not
# recommended, as certain normal events can queue up 10 or so jobs at a time
# (all of which only take a few milliseconds). Leave this alone unless you know
# what you're doing
router.maxWaitingJobs=20

View File

@ -0,0 +1,5 @@
@echo off
title ##_scripts_winttl##
cd ##_scripts_installdir##
echo ##_scripts_message##
java -DloggerFilenameOverride=logs\##_scripts_logname## -Djava.library.path=. -Dcrypto.dh.precalc.min=0 -cp lib\mstreaming.jar;lib\i2p.jar -jar lib\i2ptunnel.jar -nocli -e "config localhost ##_scripts_port##" -e "##_scripts_cmd##"

View File

@ -0,0 +1,6 @@
#!/bin/sh
cd ##_scripts_installdir##
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
nohup nice java -DloggerFilenameOverride=logs/##_scripts_logname## -Djava.library.path=. -Dcrypto.dh.precalc.min=0 -cp lib/mstreaming.jar:lib/i2p.jar -jar lib/i2ptunnel.jar -nocli -e "config localhost ##_scripts_port##" -e "##_scripts_cmd##" > /dev/null &
echo '##_scripts_message##'

View File

@ -0,0 +1,9 @@
@echo off
title I2P Router
cd ##_scripts_installdir##
REM the -XX args are workarounds for bugs in java 1.4.2's garbage collector
REM replace java with javaw if you don't want a window to pop up
java -cp lib\i2p.jar;lib\router.jar -Djava.library.path=. -DloggerFilenameOverride=logs\log-router-#.txt -XX:NewSize=4M -XX:MaxNewSize=8M -XX:PermSize=8M -XX:MaxPermSize=32M net.i2p.router.Router
pause

View File

@ -0,0 +1,8 @@
#!/bin/sh
cd ##_scripts_installdir##
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
# the -XX args are workarounds for bugs in java 1.4.2's garbage collector
nohup nice java -cp lib/i2p.jar:lib/router.jar -Djava.library.path=. -DloggerFilenameOverride=logs/log-router-#.txt -XX:NewSize=4M -XX:MaxNewSize=8M -XX:PermSize=8M -XX:MaxPermSize=32M net.i2p.router.Router --quiet > /dev/null &
# Save the pid just in case we ever want to stop the router
echo $! > router.pid
echo I2P Router started

View File

@ -0,0 +1,3 @@
#!/bin/sh
cd ##_scripts_installdir##
kill `cat router.pid`

88
readme.txt Normal file
View File

@ -0,0 +1,88 @@
$Id: readme.txt,v 1.5 2004/03/21 20:03:54 jrandom Exp $
I2P Router 0.3
You have installed a development release of I2P - a variable latency secure
and anonymous communication network. I2P is NOT a filesharing network, or an
email network, or a publishing network - it is simply an anonymous communication
layer - kind of an anonymous IP. This installation includes an application
called "I2PTunnel", which allows normal TCP/IP applications to run over I2P,
offering functionality such as access to anonymous irc servers, anonymous
websites ("eepsites"), etc.
Since this is a *development* release, I2P should not be depended upon for
anonymity or security. The network is very small, and there are certainly bugs
and suboptimal features. Participating in the network at this time should be
for exploration and to evaluate I2P's functionality and suitability for your
later needs. Once I2P 1.0 is out, wider adoption may be appropriate, but until
that time, we do want to keep the I2P community small, since you are all part of
the development team.
=== How to get started
Like a TCP/IP stack, installing the I2P "router" itself doesn't really do much.
You can fire it up with the script startRouter.sh (or startRouter.bat on
windows), and its management console can be seen via http://localhost:7655/
Once your router has started up, it may take a few minutes to get integrated
with the network (you'll see a few TCP connections listed on the management
console). At that point, you can start up any of the various proxies:
* startEepProxy: starts an HTTP proxy to access eepsites. Set your browser's
HTTP proxy to localhost:4444 and you can browse various anonymously hosted
sites, ala http://duck.i2p/. In addition, the default proxy is set up to
tunnel any HTTP requests that don't point at an eepsite (e.g. http://i2p.net/)
through I2P to an outbound squid proxy - with this, you can browse the web
anonymously.
* startIrcProxy: starts an anonymous tunnel to an anonymously hosted IRC server
(irc.duck.i2p) - use your favorite irc client and connect to localhost:6668
and join us on #i2p
=== Problems accessing eepsites or servers
I2P is not a distributed data store (ala freenet / mnet / etc) - sites are only
reachable when the host serving that data is up (and their router is running).
If you persistently can't reach the irc server, the squid proxy, or some common
eepsites, check your management console (http://localhost:7655/) and make sure
you have TCP connections. If you don't have any, make sure your firewall / NAT
allows inbound access to your I2P port (which you specified during
installation). If thats fine, but you only see one routerInfo-*.dat file
underneath your netDb/ directory, run the reseed script to pull some new peer
references (it contacts http://i2p.net/i2pdb/ and downloads those files.
alternately, you can get routerInfo files from any of your friends, etc)
If you still have problems, get in touch with the I2P team (contact info below)
=== Resources / contact info
I2P is currently revamping our website, so the two main resources are
http://i2p.net/ and http://wiki.invisiblenet.net/iip-wiki?I2P (our website's
work-in-progress location is http://drupal.i2p.net/ for those who want to see
whats coming down the pipe). The development and user community hangs out on
a few different linked irc chats - IIP's #i2p, freenode.net's #i2p, and the
in-I2P irc network's #i2p (both irc.baffled.i2p and irc.duck.i2p). All of those
channels are hooked together, so join whichever one meets your needs.
There is also a relatively low traffic mailing list:
http://i2p.net/mailman/listinfo/i2p with archives at
http://i2p.net/pipermail/i2p/
The source can be retrieved from http://i2p.net/i2p/ as well as the latest
binary distributions.
You can pull the latest code via cvs:
cvs -d :pserver:anoncvs@i2p.net:/cvsroot login
cvs -d :pserver:anoncvs@i2p.net:/cvsroot co i2p
The password is "anoncvs".
=== Acknowledgements
We are a small group of volunteers spread around several continents, working to
advance different aspects of the project and discussing the design of the
network. For a current list of team members, please see
http://drupal.i2p.net/team
=== Licenses
All code included here is released under an open source license. To review
the I2P license policy, please see http://drupal.i2p.net/node/view/66.
If there is any confusion, please see the source code or contact the
developers on the i2p list.

View File

@ -0,0 +1,9 @@
$Id$
the i2p/router/ module is the root of the I2P
router, and everything within it is released
according to the terms of the I2P license policy.
That means everything contained within the
i2p/router module is released into the public
domain unless otherwise marked. Alternate licenses
that may be used include BSD, Cryptix, and MIT.

35
router/java/build.xml Normal file
View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="i2p_router">
<target name="all" depends="clean, build" />
<target name="build" depends="builddep, jar" />
<target name="builddep">
<ant dir="../../core/java/" target="build" />
</target>
<target name="compile">
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac srcdir="./src:./test" debug="true" destdir="./build/obj" classpath="../../core/java/build/i2p.jar" />
</target>
<target name="jar" depends="compile">
<jar destfile="./build/router.jar" basedir="./build/obj" includes="**/*.class" />
</target>
<target name="javadoc">
<mkdir dir="./build" />
<mkdir dir="./build/javadoc" />
<javadoc
sourcepath="./src:./test:../../core/java/src:../../core/java/test" destdir="./build/javadoc"
packagenames="*"
use="true"
splitindex="true"
windowtitle="I2P Router" />
</target>
<target name="clean">
<delete dir="./build" />
</target>
<target name="cleandep" depends="clean">
<ant dir="../../core/java/" target="cleandep" />
</target>
<target name="distclean" depends="clean">
<ant dir="../../core/java/" target="distclean" />
</target>
</project>

View File

@ -0,0 +1,84 @@
package net.i2p.data.i2np;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
/**
* Defines a message containing arbitrary bytes of data
*
* @author jrandom
*/
public class DataMessage extends I2NPMessageImpl {
private final static Log _log = new Log(DataMessage.class);
public final static int MESSAGE_TYPE = 20;
private byte _data[];
public DataMessage() {
_data = null;
}
public byte[] getData() { return _data; }
public void setData(byte data[]) { _data = data; }
public int getSize() { return _data.length; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
int size = (int)DataHelper.readLong(in, 4);
_data = new byte[size];
int read = read(in, _data);
if (read != size)
throw new DataFormatException("Not enough bytes to read (read = " + read + ", expected = " + size + ")");
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
}
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream((_data != null ? _data.length + 4 : 4));
try {
DataHelper.writeLong(os, 4, (_data != null ? _data.length : 0));
os.write(_data);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
}
return os.toByteArray();
}
public int getType() { return MESSAGE_TYPE; }
public int hashCode() {
return DataHelper.hashCode(getData());
}
public boolean equals(Object object) {
if ( (object != null) && (object instanceof DataMessage) ) {
DataMessage msg = (DataMessage)object;
return DataHelper.eq(getData(),msg.getData());
} else {
return false;
}
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("[DataMessage: ");
buf.append("\n\tData: ").append(DataHelper.toString(getData(), 64));
buf.append("]");
return buf.toString();
}
}

View File

@ -0,0 +1,99 @@
package net.i2p.data.i2np;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.util.Log;
/**
* Defines the message a router sends to another router to help integrate into
* the network by searching for routers in a particular keyspace.
*
* @author jrandom
*/
public class DatabaseFindNearestMessage extends I2NPMessageImpl {
private final static Log _log = new Log(DatabaseFindNearestMessage.class);
public final static int MESSAGE_TYPE = 4;
private Hash _key;
private Hash _from;
public DatabaseFindNearestMessage() {
setSearchKey(null);
setFromHash(null);
}
/**
* Defines the key being searched for
*/
public Hash getSearchKey() { return _key; }
public void setSearchKey(Hash key) { _key = key; }
/**
* Contains the SHA256 Hash of the RouterIdentity sending the message
*/
public Hash getFromHash() { return _from; }
public void setFromHash(Hash from) { _from = from; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_key = new Hash();
_key.readBytes(in);
_from = new Hash();
_from.readBytes(in);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
}
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
if ( (_key == null) || (_from == null) ) throw new I2NPMessageException("Not enough data to write out");
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
try {
_key.writeBytes(os);
_from.writeBytes(os);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
}
return os.toByteArray();
}
public int getType() { return MESSAGE_TYPE; }
public int hashCode() {
return DataHelper.hashCode(getSearchKey()) +
DataHelper.hashCode(getFromHash());
}
public boolean equals(Object object) {
if ( (object != null) && (object instanceof DatabaseFindNearestMessage) ) {
DatabaseFindNearestMessage msg = (DatabaseFindNearestMessage)object;
return DataHelper.eq(getSearchKey(),msg.getSearchKey()) &&
DataHelper.eq(getFromHash(),msg.getFromHash());
} else {
return false;
}
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("[DatabaseFindNearestMessage: ");
buf.append("\n\tSearch Key: ").append(getSearchKey());
buf.append("\n\tFrom: ").append(getFromHash());
buf.append("]");
return buf.toString();
}
}

View File

@ -0,0 +1,165 @@
package net.i2p.data.i2np;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.RouterInfo;
import net.i2p.data.TunnelId;
import net.i2p.util.Log;
/**
* Defines the message a router sends to another router to search for a
* key in the network database.
*
* @author jrandom
*/
public class DatabaseLookupMessage extends I2NPMessageImpl {
private final static Log _log = new Log(DatabaseLookupMessage.class);
public final static int MESSAGE_TYPE = 2;
private Hash _key;
private RouterInfo _from;
private TunnelId _replyTunnel;
private Set _dontIncludePeers;
public DatabaseLookupMessage() {
setSearchKey(null);
setFrom(null);
setDontIncludePeers(null);
}
/**
* Defines the key being searched for
*/
public Hash getSearchKey() { return _key; }
public void setSearchKey(Hash key) { _key = key; }
/**
* Contains the current router info of the router who requested this lookup
*
*/
public RouterInfo getFrom() { return _from; }
public void setFrom(RouterInfo from) { _from = from; }
/**
* Contains the tunnel ID a reply should be sent to
*
*/
public TunnelId getReplyTunnel() { return _replyTunnel; }
public void setReplyTunnel(TunnelId replyTunnel) { _replyTunnel = replyTunnel; }
/**
* Set of peers that a lookup reply should NOT include
*
* @return Set of Hash objects, each of which is the H(routerIdentity) to skip
*/
public Set getDontIncludePeers() { return _dontIncludePeers; }
public void setDontIncludePeers(Set peers) {
if (peers != null)
_dontIncludePeers = new HashSet(peers);
else
_dontIncludePeers = null;
}
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_key = new Hash();
_key.readBytes(in);
_from = new RouterInfo();
_from.readBytes(in);
boolean tunnelSpecified = DataHelper.readBoolean(in).booleanValue();
if (tunnelSpecified) {
_replyTunnel = new TunnelId();
_replyTunnel.readBytes(in);
}
int numPeers = (int)DataHelper.readLong(in, 2);
if ( (numPeers < 0) || (numPeers >= (1<<16) ) )
throw new DataFormatException("Invalid number of peers - " + numPeers);
Set peers = new HashSet(numPeers);
for (int i = 0; i < numPeers; i++) {
Hash peer = new Hash();
peer.readBytes(in);
peers.add(peer);
}
_dontIncludePeers = peers;
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
}
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
if (_key == null) throw new I2NPMessageException("Key being searched for not specified");
if (_from == null) throw new I2NPMessageException("From address not specified");
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
try {
_key.writeBytes(os);
_from.writeBytes(os);
if (_replyTunnel != null) {
DataHelper.writeBoolean(os, Boolean.TRUE);
_replyTunnel.writeBytes(os);
} else {
DataHelper.writeBoolean(os, Boolean.FALSE);
}
if ( (_dontIncludePeers == null) || (_dontIncludePeers.size() <= 0) ) {
DataHelper.writeLong(os, 2, 0);
} else {
DataHelper.writeLong(os, 2, _dontIncludePeers.size());
for (Iterator iter = _dontIncludePeers.iterator(); iter.hasNext(); ) {
Hash peer = (Hash)iter.next();
peer.writeBytes(os);
}
}
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
}
return os.toByteArray();
}
public int getType() { return MESSAGE_TYPE; }
public int hashCode() {
return DataHelper.hashCode(getSearchKey()) +
DataHelper.hashCode(getFrom()) +
DataHelper.hashCode(getReplyTunnel()) +
DataHelper.hashCode(_dontIncludePeers);
}
public boolean equals(Object object) {
if ( (object != null) && (object instanceof DatabaseLookupMessage) ) {
DatabaseLookupMessage msg = (DatabaseLookupMessage)object;
return DataHelper.eq(getSearchKey(),msg.getSearchKey()) &&
DataHelper.eq(getFrom(),msg.getFrom()) &&
DataHelper.eq(getReplyTunnel(),msg.getReplyTunnel()) &&
DataHelper.eq(_dontIncludePeers,msg.getDontIncludePeers());
} else {
return false;
}
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("[DatabaseLookupMessage: ");
buf.append("\n\tSearch Key: ").append(getSearchKey());
buf.append("\n\tFrom: ").append(getFrom());
buf.append("\n\tReply Tunnel: ").append(getReplyTunnel());
buf.append("\n\tDont Include Peers: ").append(getDontIncludePeers());
buf.append("]");
return buf.toString();
}
}

View File

@ -0,0 +1,149 @@
package net.i2p.data.i2np;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.RouterInfo;
import net.i2p.util.Log;
/**
* Defines the message a router sends to another router in response to a
* search (DatabaseFindNearest or DatabaseLookup) when it doesn't have the value,
* specifying what routers it would search.
*
* @author jrandom
*/
public class DatabaseSearchReplyMessage extends I2NPMessageImpl {
private final static Log _log = new Log(DatabaseSearchReplyMessage.class);
public final static int MESSAGE_TYPE = 3;
private Hash _key;
private List _routerInfoStructures;
private Hash _from;
public DatabaseSearchReplyMessage() {
setSearchKey(null);
_routerInfoStructures = new ArrayList();
setFromHash(null);
}
/**
* Defines the key being searched for
*/
public Hash getSearchKey() { return _key; }
public void setSearchKey(Hash key) { _key = key; }
public int getNumReplies() { return _routerInfoStructures.size(); }
public RouterInfo getReply(int index) { return (RouterInfo)_routerInfoStructures.get(index); }
public void addReply(RouterInfo info) { _routerInfoStructures.add(info); }
public void addReplies(Collection replies) { _routerInfoStructures.addAll(replies); }
public Hash getFromHash() { return _from; }
public void setFromHash(Hash from) { _from = from; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_key = new Hash();
_key.readBytes(in);
int compressedLength = (int)DataHelper.readLong(in, 2);
byte compressedData[] = new byte[compressedLength];
int read = DataHelper.read(in, compressedData);
if (read != compressedLength)
throw new IOException("Not enough data to decompress");
byte decompressedData[] = DataHelper.decompress(compressedData);
ByteArrayInputStream bais = new ByteArrayInputStream(decompressedData);
int num = (int)DataHelper.readLong(bais, 1);
_routerInfoStructures.clear();
for (int i = 0; i < num; i++) {
RouterInfo info = new RouterInfo();
info.readBytes(bais);
addReply(info);
}
_from = new Hash();
_from.readBytes(in);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
}
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
if (_key == null)
throw new I2NPMessageException("Key in reply to not specified");
if (_routerInfoStructures == null)
throw new I2NPMessageException("RouterInfo replies are null");
if (_routerInfoStructures.size() <= 0)
throw new I2NPMessageException("No replies specified in SearchReply! Always include oneself!");
if (_from == null)
throw new I2NPMessageException("No 'from' address specified!");
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
try {
_key.writeBytes(os);
ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
DataHelper.writeLong(baos, 1, _routerInfoStructures.size());
for (int i = 0; i < getNumReplies(); i++) {
RouterInfo info = getReply(i);
info.writeBytes(baos);
}
byte compressed[] = DataHelper.compress(baos.toByteArray());
DataHelper.writeLong(os, 2, compressed.length);
os.write(compressed);
_from.writeBytes(os);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
}
return os.toByteArray();
}
public int getType() { return MESSAGE_TYPE; }
public boolean equals(Object object) {
if ( (object != null) && (object instanceof DatabaseSearchReplyMessage) ) {
DatabaseSearchReplyMessage msg = (DatabaseSearchReplyMessage)object;
return DataHelper.eq(getSearchKey(),msg.getSearchKey()) &&
DataHelper.eq(getFromHash(),msg.getFromHash()) &&
DataHelper.eq(_routerInfoStructures,msg._routerInfoStructures);
} else {
return false;
}
}
public int hashCode() {
return DataHelper.hashCode(getSearchKey()) +
DataHelper.hashCode(getFromHash()) +
DataHelper.hashCode(_routerInfoStructures);
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("[DatabaseSearchReplyMessage: ");
buf.append("\n\tSearch Key: ").append(getSearchKey());
buf.append("\n\tReplies: # = ").append(getNumReplies());
for (int i = 0; i < getNumReplies(); i++) {
buf.append("\n\t\tReply [").append(i).append("]: ").append(getReply(i));
}
buf.append("\n\tFrom: ").append(getFromHash());
buf.append("]");
return buf.toString();
}
}

View File

@ -0,0 +1,170 @@
package net.i2p.data.i2np;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
import net.i2p.data.RouterInfo;
import net.i2p.util.Log;
/**
* Defines the message a router sends to another router to test the network
* database reachability, as well as the reply message sent back.
*
* @author jrandom
*/
public class DatabaseStoreMessage extends I2NPMessageImpl {
private final static Log _log = new Log(DatabaseStoreMessage.class);
public final static int MESSAGE_TYPE = 1;
private Hash _key;
private int _type;
private LeaseSet _leaseSet;
private RouterInfo _info;
public final static int KEY_TYPE_ROUTERINFO = 0;
public final static int KEY_TYPE_LEASESET = 1;
public DatabaseStoreMessage() {
setValueType(-1);
setKey(null);
setLeaseSet(null);
setRouterInfo(null);
}
/**
* Defines the key in the network database being stored
*
*/
public Hash getKey() { return _key; }
public void setKey(Hash key) { _key = key; }
/**
* Defines the router info value in the network database being stored
*
*/
public RouterInfo getRouterInfo() { return _info; }
public void setRouterInfo(RouterInfo routerInfo) {
_info = routerInfo;
if (_info != null)
setValueType(KEY_TYPE_ROUTERINFO);
}
/**
* Defines the lease set value in the network database being stored
*
*/
public LeaseSet getLeaseSet() { return _leaseSet; }
public void setLeaseSet(LeaseSet leaseSet) {
_leaseSet = leaseSet;
if (_leaseSet != null)
setValueType(KEY_TYPE_LEASESET);
}
/**
* Defines type of key being stored in the network database -
* either KEY_TYPE_ROUTERINFO or KEY_TYPE_LEASESET
*
*/
public int getValueType() { return _type; }
public void setValueType(int type) { _type = type; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_key = new Hash();
_key.readBytes(in);
_log.debug("Hash read: " + _key.toBase64());
_type = (int)DataHelper.readLong(in, 1);
if (_type == KEY_TYPE_LEASESET) {
_leaseSet = new LeaseSet();
_leaseSet.readBytes(in);
} else if (_type == KEY_TYPE_ROUTERINFO) {
_info = new RouterInfo();
int compressedSize = (int)DataHelper.readLong(in, 2);
byte compressed[] = new byte[compressedSize];
int read = DataHelper.read(in, compressed);
if (read != compressedSize)
throw new I2NPMessageException("Invalid compressed data size");
ByteArrayInputStream bais = new ByteArrayInputStream(DataHelper.decompress(compressed));
_info.readBytes(bais);
} else {
throw new I2NPMessageException("Invalid type of key read from the structure - " + _type);
}
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
}
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
if (_key == null) throw new I2NPMessageException("Invalid key");
if ( (_type != KEY_TYPE_LEASESET) && (_type != KEY_TYPE_ROUTERINFO) ) throw new I2NPMessageException("Invalid key type");
if ( (_type == KEY_TYPE_LEASESET) && (_leaseSet == null) ) throw new I2NPMessageException("Missing lease set");
if ( (_type == KEY_TYPE_ROUTERINFO) && (_info == null) ) throw new I2NPMessageException("Missing router info");
ByteArrayOutputStream os = new ByteArrayOutputStream(256);
try {
_key.writeBytes(os);
DataHelper.writeLong(os, 1, _type);
if (_type == KEY_TYPE_LEASESET) {
_leaseSet.writeBytes(os);
} else if (_type == KEY_TYPE_ROUTERINFO) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*1024);
_info.writeBytes(baos);
byte uncompressed[] = baos.toByteArray();
byte compressed[] = DataHelper.compress(uncompressed);
DataHelper.writeLong(os, 2, compressed.length);
os.write(compressed);
}
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
}
return os.toByteArray();
}
public int getType() { return MESSAGE_TYPE; }
public int hashCode() {
return DataHelper.hashCode(getKey()) +
DataHelper.hashCode(getLeaseSet()) +
DataHelper.hashCode(getRouterInfo()) +
getValueType();
}
public boolean equals(Object object) {
if ( (object != null) && (object instanceof DatabaseStoreMessage) ) {
DatabaseStoreMessage msg = (DatabaseStoreMessage)object;
return DataHelper.eq(getKey(),msg.getKey()) &&
DataHelper.eq(getLeaseSet(),msg.getLeaseSet()) &&
DataHelper.eq(getRouterInfo(),msg.getRouterInfo()) &&
DataHelper.eq(getValueType(),msg.getValueType());
} else {
return false;
}
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("[DatabaseStoreMessage: ");
buf.append("\n\tExpiration: ").append(getMessageExpiration());
buf.append("\n\tUnique ID: ").append(getUniqueId());
buf.append("\n\tKey: ").append(getKey());
buf.append("\n\tValue Type: ").append(getValueType());
buf.append("\n\tRouter Info: ").append(getRouterInfo());
buf.append("\n\tLease Set: ").append(getLeaseSet());
buf.append("]");
return buf.toString();
}
}

View File

@ -0,0 +1,274 @@
package net.i2p.data.i2np;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import net.i2p.util.Log;
import net.i2p.data.DataHelper;
import net.i2p.data.DataStructureImpl;
import net.i2p.data.DataFormatException;
import net.i2p.data.Hash;
import net.i2p.data.SessionKey;
import net.i2p.data.TunnelId;
/**
* Contains the delivery instructions
*
* @author jrandom
*/
public class DeliveryInstructions extends DataStructureImpl {
private final static Log _log = new Log(DeliveryInstructions.class);
private boolean _encrypted;
private SessionKey _encryptionKey;
private int _deliveryMode;
public final static int DELIVERY_MODE_LOCAL = 0;
public final static int DELIVERY_MODE_DESTINATION = 1;
public final static int DELIVERY_MODE_ROUTER = 2;
public final static int DELIVERY_MODE_TUNNEL = 3;
private Hash _destinationHash;
private Hash _routerHash;
private TunnelId _tunnelId;
private boolean _delayRequested;
private long _delaySeconds;
private final static int FLAG_MODE_LOCAL = 0;
private final static int FLAG_MODE_DESTINATION = 1;
private final static int FLAG_MODE_ROUTER = 2;
private final static int FLAG_MODE_TUNNEL = 3;
private final static long FLAG_ENCRYPTED = 128;
private final static long FLAG_MODE = 96;
private final static long FLAG_DELAY = 16;
public DeliveryInstructions() {
setEncrypted(false);
setEncryptionKey(null);
setDeliveryMode(-1);
setDestination(null);
setRouter(null);
setTunnelId(null);
setDelayRequested(false);
setDelaySeconds(0);
}
public boolean getEncrypted() { return _encrypted; }
public void setEncrypted(boolean encrypted) { _encrypted = encrypted; }
public SessionKey getEncryptionKey() { return _encryptionKey; }
public void setEncryptionKey(SessionKey key) { _encryptionKey = key; }
public int getDeliveryMode() { return _deliveryMode; }
public void setDeliveryMode(int mode) { _deliveryMode = mode; }
public Hash getDestination() { return _destinationHash; }
public void setDestination(Hash dest) { _destinationHash = dest; }
public Hash getRouter() { return _routerHash; }
public void setRouter(Hash router) { _routerHash = router; }
public TunnelId getTunnelId() { return _tunnelId; }
public void setTunnelId(TunnelId id) { _tunnelId = id; }
public boolean getDelayRequested() { return _delayRequested; }
public void setDelayRequested(boolean req) { _delayRequested = req; }
public long getDelaySeconds() { return _delaySeconds; }
public void setDelaySeconds(long seconds) { _delaySeconds = seconds; }
public void readBytes(InputStream in) throws DataFormatException, IOException {
long flags = DataHelper.readLong(in, 1);
_log.debug("Read flags: " + flags + " mode: " + flagMode(flags));
if (flagEncrypted(flags)) {
SessionKey k = new SessionKey();
k.readBytes(in);
setEncryptionKey(k);
setEncrypted(true);
} else {
setEncrypted(false);
}
setDeliveryMode(flagMode(flags));
switch (flagMode(flags)) {
case FLAG_MODE_LOCAL:
break;
case FLAG_MODE_DESTINATION:
Hash destHash = new Hash();
destHash.readBytes(in);
setDestination(destHash);
break;
case FLAG_MODE_ROUTER:
Hash routerHash = new Hash();
routerHash.readBytes(in);
setRouter(routerHash);
break;
case FLAG_MODE_TUNNEL:
Hash tunnelRouterHash = new Hash();
tunnelRouterHash.readBytes(in);
setRouter(tunnelRouterHash);
TunnelId id = new TunnelId();
id.readBytes(in);
setTunnelId(id);
break;
}
if (flagDelay(flags)) {
long delay = DataHelper.readLong(in, 4);
setDelayRequested(true);
setDelaySeconds(delay);
} else {
setDelayRequested(false);
}
}
private boolean flagEncrypted(long flags) {
return (0 != (flags & FLAG_ENCRYPTED));
}
private int flagMode(long flags) {
long v = flags & FLAG_MODE;
v >>>= 5;
return (int)v;
}
private boolean flagDelay(long flags) {
return (0 != (flags & FLAG_DELAY));
}
private long getFlags() {
long val = 0L;
if (getEncrypted())
val = val | FLAG_ENCRYPTED;
long fmode = 0;
switch (getDeliveryMode()) {
case FLAG_MODE_LOCAL:
break;
case FLAG_MODE_DESTINATION:
fmode = FLAG_MODE_DESTINATION << 5;
break;
case FLAG_MODE_ROUTER:
fmode = FLAG_MODE_ROUTER << 5;
break;
case FLAG_MODE_TUNNEL:
fmode = FLAG_MODE_TUNNEL << 5;
break;
}
val = val | fmode;
if (getDelayRequested())
val = val | FLAG_DELAY;
_log.debug("getFlags() = " + val);
return val;
}
private byte[] getAdditionalInfo() throws DataFormatException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(64);
try {
if (getEncrypted()) {
if (_encryptionKey == null) throw new DataFormatException("Encryption key is not set");
_encryptionKey.writeBytes(baos);
_log.debug("IsEncrypted");
} else {
_log.debug("Is NOT Encrypted");
}
switch (getDeliveryMode()) {
case FLAG_MODE_LOCAL:
_log.debug("mode = local");
break;
case FLAG_MODE_DESTINATION:
if (_destinationHash == null) throw new DataFormatException("Destination hash is not set");
_destinationHash.writeBytes(baos);
_log.debug("mode = destination, hash = " + _destinationHash);
break;
case FLAG_MODE_ROUTER:
if (_routerHash == null) throw new DataFormatException("Router hash is not set");
_routerHash.writeBytes(baos);
_log.debug("mode = router, routerHash = " + _routerHash);
break;
case FLAG_MODE_TUNNEL:
if ( (_routerHash == null) || (_tunnelId == null) ) throw new DataFormatException("Router hash or tunnel ID is not set");
_routerHash.writeBytes(baos);
_tunnelId.writeBytes(baos);
_log.debug("mode = tunnel, tunnelId = " + _tunnelId.getTunnelId() + ", routerHash = " + _routerHash);
break;
}
if (getDelayRequested()) {
_log.debug("delay requested: " + getDelaySeconds());
DataHelper.writeLong(baos, 4, getDelaySeconds());
} else {
_log.debug("delay NOT requested");
}
} catch (IOException ioe) {
throw new DataFormatException("Unable to write out additional info", ioe);
}
return baos.toByteArray();
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ( (_deliveryMode < 0) || (_deliveryMode > FLAG_MODE_TUNNEL) ) throw new DataFormatException("Invalid data: mode = " + _deliveryMode);
long flags = getFlags();
_log.debug("Write flags: " + flags + " mode: " + getDeliveryMode() + " =?= " + flagMode(flags));
byte additionalInfo[] = getAdditionalInfo();
DataHelper.writeLong(out, 1, flags);
if (additionalInfo != null) {
out.write(additionalInfo);
out.flush();
}
}
public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof DeliveryInstructions))
return false;
DeliveryInstructions instr = (DeliveryInstructions)obj;
return (getDelayRequested() == instr.getDelayRequested()) &&
(getDelaySeconds() == instr.getDelaySeconds()) &&
(getDeliveryMode() == instr.getDeliveryMode()) &&
(getEncrypted() == instr.getEncrypted()) &&
DataHelper.eq(getDestination(), instr.getDestination()) &&
DataHelper.eq(getEncryptionKey(), instr.getEncryptionKey()) &&
DataHelper.eq(getRouter(), instr.getRouter()) &&
DataHelper.eq(getTunnelId(), instr.getTunnelId());
}
public int hashCode() {
return (int)getDelaySeconds() +
getDeliveryMode() +
DataHelper.hashCode(getDestination()) +
DataHelper.hashCode(getEncryptionKey()) +
DataHelper.hashCode(getRouter()) +
DataHelper.hashCode(getTunnelId());
}
public String toString() {
StringBuffer buf = new StringBuffer(128);
buf.append("[DeliveryInstructions: ");
buf.append("\n\tDelivery mode: ");
switch (getDeliveryMode()) {
case DELIVERY_MODE_LOCAL:
buf.append("local");
break;
case DELIVERY_MODE_DESTINATION:
buf.append("destination");
break;
case DELIVERY_MODE_ROUTER:
buf.append("router");
break;
case DELIVERY_MODE_TUNNEL:
buf.append("tunnel");
break;
}
buf.append("\n\tDelay requested: ").append(getDelayRequested());
buf.append("\n\tDelay seconds: ").append(getDelaySeconds());
buf.append("\n\tDestination: ").append(getDestination());
buf.append("\n\tEncrypted: ").append(getEncrypted());
buf.append("\n\tEncryption key: ").append(getEncryptionKey());
buf.append("\n\tRouter: ").append(getRouter());
buf.append("\n\tTunnelId: ").append(getTunnelId());
return buf.toString();
}
}

View File

@ -0,0 +1,91 @@
package net.i2p.data.i2np;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.util.Log;
/**
* Defines the message sent back in reply to a message when requested, containing
* the private ack id.
*
* @author jrandom
*/
public class DeliveryStatusMessage extends I2NPMessageImpl {
private final static Log _log = new Log(DeliveryStatusMessage.class);
public final static int MESSAGE_TYPE = 10;
private long _id;
private Date _arrival;
public DeliveryStatusMessage() {
setMessageId(-1);
setArrival(null);
}
public long getMessageId() { return _id; }
public void setMessageId(long id) { _id = id; }
public Date getArrival() { return _arrival; }
public void setArrival(Date arrival) { _arrival = arrival; }
public void readMessage(InputStream in, int type) throws I2NPMessageException, IOException {
if (type != MESSAGE_TYPE) throw new I2NPMessageException("Message type is incorrect for this message");
try {
_id = DataHelper.readLong(in, 4);
_arrival = DataHelper.readDate(in);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Unable to load the message data", dfe);
}
}
protected byte[] writeMessage() throws I2NPMessageException, IOException {
if ( (_id < 0) || (_arrival == null) ) throw new I2NPMessageException("Not enough data to write out");
ByteArrayOutputStream os = new ByteArrayOutputStream(32);
try {
DataHelper.writeLong(os, 4, _id);
DataHelper.writeDate(os, _arrival);
} catch (DataFormatException dfe) {
throw new I2NPMessageException("Error writing out the message data", dfe);
}
return os.toByteArray();
}
public int getType() { return MESSAGE_TYPE; }
public int hashCode() {
return (int)getMessageId() +
DataHelper.hashCode(getArrival());
}
public boolean equals(Object object) {
if ( (object != null) && (object instanceof DeliveryStatusMessage) ) {
DeliveryStatusMessage msg = (DeliveryStatusMessage)object;
return DataHelper.eq(getMessageId(),msg.getMessageId()) &&
DataHelper.eq(getArrival(),msg.getArrival());
} else {
return false;
}
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("[DeliveryStatusMessage: ");
buf.append("\n\tMessage ID: ").append(getMessageId());
buf.append("\n\tArrival: ").append(getArrival());
buf.append("]");
return buf.toString();
}
}

View File

@ -0,0 +1,62 @@
package net.i2p.data.i2np;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log;
import net.i2p.data.DataHelper;
import net.i2p.data.DataStructureImpl;
import net.i2p.data.DataFormatException;
import net.i2p.data.PrivateKey;
/**
* Contains the private key which matches the EndPointPublicKey which, in turn,
* is published on the LeaseSet and used to encrypt messages to the router to
* which a Destination is currently connected.
*
* @author jrandom
*/
public class EndPointPrivateKey extends DataStructureImpl {
private final static Log _log = new Log(EndPointPrivateKey.class);
private PrivateKey _key;
public EndPointPrivateKey() { setKey(null); }
public PrivateKey getKey() { return _key; }
public void setKey(PrivateKey key) { _key= key; }
public void readBytes(InputStream in) throws DataFormatException, IOException {
_key = new PrivateKey();
_key.readBytes(in);
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_key == null) throw new DataFormatException("Invalid key");
_key.writeBytes(out);
}
public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof EndPointPublicKey))
return false;
return DataHelper.eq(getKey(), ((EndPointPublicKey)obj).getKey());
}
public int hashCode() {
if (_key == null) return 0;
return getKey().hashCode();
}
public String toString() {
return "[EndPointPrivateKey: " + getKey() + "]";
}
}

View File

@ -0,0 +1,62 @@
package net.i2p.data.i2np;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import net.i2p.util.Log;
import net.i2p.data.DataHelper;
import net.i2p.data.DataStructureImpl;
import net.i2p.data.DataFormatException;
import net.i2p.data.PublicKey;
/**
* Contains the public key which matches the EndPointPrivateKey. This is
* published on the LeaseSet and used to encrypt messages to the router to
* which a Destination is currently connected.
*
* @author jrandom
*/
public class EndPointPublicKey extends DataStructureImpl {
private final static Log _log = new Log(EndPointPublicKey.class);
private PublicKey _key;
public EndPointPublicKey() { setKey(null); }
public PublicKey getKey() { return _key; }
public void setKey(PublicKey key) { _key= key; }
public void readBytes(InputStream in) throws DataFormatException, IOException {
_key = new PublicKey();
_key.readBytes(in);
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_key == null) throw new DataFormatException("Invalid key");
_key.writeBytes(out);
}
public boolean equals(Object obj) {
if ( (obj == null) || !(obj instanceof EndPointPublicKey))
return false;
return DataHelper.eq(getKey(), ((EndPointPublicKey)obj).getKey());
}
public int hashCode() {
if (_key == null) return 0;
return getKey().hashCode();
}
public String toString() {
return "[EndPointPublicKey: " + getKey() + "]";
}
}

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