beginning of branch i2p.i2p.i2p
This commit is contained in:
43
apps/phttprelay/java/build.xml
Normal file
43
apps/phttprelay/java/build.xml
Normal 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>
|
159
apps/phttprelay/java/lib/LICENSE.html
Normal file
159
apps/phttprelay/java/lib/LICENSE.html
Normal 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>
|
||||
|
||||
|
6
apps/phttprelay/java/lib/readme.txt
Normal file
6
apps/phttprelay/java/lib/readme.txt
Normal 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.
|
@ -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();
|
||||
}
|
||||
}
|
40
apps/phttprelay/java/src/net/i2p/phttprelay/LockManager.java
Normal file
40
apps/phttprelay/java/src/net/i2p/phttprelay/LockManager.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
263
apps/phttprelay/java/src/net/i2p/phttprelay/PollServlet.java
Normal file
263
apps/phttprelay/java/src/net/i2p/phttprelay/PollServlet.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
154
apps/phttprelay/java/src/net/i2p/phttprelay/RegisterServlet.java
Normal file
154
apps/phttprelay/java/src/net/i2p/phttprelay/RegisterServlet.java
Normal 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;
|
||||
}
|
||||
}
|
318
apps/phttprelay/java/src/net/i2p/phttprelay/SendServlet.java
Normal file
318
apps/phttprelay/java/src/net/i2p/phttprelay/SendServlet.java
Normal 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 + "]");
|
||||
}
|
||||
}
|
||||
}
|
71
apps/phttprelay/java/web.xml
Normal file
71
apps/phttprelay/java/web.xml
Normal 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>
|
Reference in New Issue
Block a user