forked from I2P_Developers/i2p.i2p
Compare commits
137 Commits
i2p_0_3_3
...
i2p_0_3_4_
Author | SHA1 | Date | |
---|---|---|---|
7fcc05c037 | |||
edf17d0a46 | |||
9cccd0bfc9 | |||
e57c010e3d | |||
4dfcf1c1c8 | |||
8d7786e97d | |||
2cb519cd06 | |||
bc46ad4331 | |||
be08e8f23b | |||
f937809903 | |||
c0f32c942d | |||
39c5c830bb | |||
83c8953d1b | |||
4b100a5a64 | |||
b7e50e0b3a | |||
6933052de7 | |||
22d945f7b7 | |||
b81c5628ce | |||
cdb4576bd7 | |||
4859cd7dcf | |||
3f70593ca8 | |||
676288e6c0 | |||
1aa3e0cc5a | |||
b0f8064d0d | |||
e5e85732d4 | |||
f97c1ef0d9 | |||
83cf815160 | |||
fea62a529b | |||
2cff5ae2bb | |||
8aa29f5340 | |||
8051bfef1d | |||
85bc79ab1b | |||
97e5588184 | |||
e622fdc885 | |||
4f81e1debe | |||
9ccfd852d8 | |||
9df57a47d5 | |||
f2cadb7278 | |||
3f6e7cb84c | |||
4ed4ce8240 | |||
4373956a3f | |||
36fb99a00d | |||
8f5d325c4d | |||
8d8b6da0bf | |||
3a61d260d7 | |||
5bc433d1c8 | |||
a0e4bbac6f | |||
d44d8cc53d | |||
1305969247 | |||
94becebafa | |||
8add433966 | |||
c5b289fb1f | |||
f85ce180ed | |||
8101fa1c92 | |||
8a091e0205 | |||
edc3a54ad3 | |||
a3cd7d1068 | |||
337441b8de | |||
bd78a66bd4 | |||
96f9618081 | |||
cf7be2d601 | |||
598732915e | |||
99c18396ab | |||
6d5dd81066 | |||
7cd9451a22 | |||
29b5a7c5c2 | |||
393a04165e | |||
e97e834a5b | |||
bec685682b | |||
34f119ca23 | |||
09ed1b1f9e | |||
fcb109f46d | |||
f30823e4ac | |||
c04885449d | |||
8c31e47eeb | |||
d8ee5c180b | |||
823f4a26b3 | |||
a05e8a446d | |||
21126f766c | |||
8f46ead756 | |||
75652fc2c4 | |||
7cdc46f007 | |||
ed9f9625ae | |||
7b60d3dab9 | |||
a6993fa489 | |||
bc2774bde4 | |||
d10dc1e8d3 | |||
48556de92b | |||
7f6b477d2e | |||
11d8c67d12 | |||
fd2a4029e7 | |||
4467928845 | |||
cc85a00bfd | |||
15d58ecdcd | |||
08d93b9a78 | |||
a75a999e3b | |||
684ef709f5 | |||
2e98dd09e7 | |||
6635425bbc | |||
5d4bdc5697 | |||
0fdb286005 | |||
59a8493aa7 | |||
25378e894b | |||
3b9fea20b6 | |||
c6bb8f09ca | |||
4a9bd84bf0 | |||
c02522b0fe | |||
c2a71ef756 | |||
e669110cf4 | |||
f4cf31c13d | |||
7b23a5dcce | |||
b2fda0c79d | |||
5af96f5ccb | |||
ca445ac178 | |||
16fb31b6eb | |||
a1ff325b7b | |||
5eaec4c841 | |||
ffcc34c4f9 | |||
2dbe33e769 | |||
60c7db0733 | |||
0ed95bbdf1 | |||
c901bcf9b7 | |||
0ccf915a18 | |||
52b1c0a926 | |||
399865e6c8 | |||
54aeab1524 | |||
91f83277e2 | |||
c937cb2f07 | |||
ebd150e473 | |||
9218f7b82c | |||
edaf7aee5d | |||
43c18d0f4d | |||
65d85f7479 | |||
476e23db5b | |||
abaa5d87f6 | |||
ce3e7e623c | |||
3fd35a9c18 |
2
apps/bogobot/.cvsignore
Normal file
2
apps/bogobot/.cvsignore
Normal file
@ -0,0 +1,2 @@
|
||||
.classpath
|
||||
.project
|
214
apps/bogobot/Bogobot.java
Normal file
214
apps/bogobot/Bogobot.java
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
* bogobot - A simple join/part stats logger bot for I2P IRC.
|
||||
*
|
||||
* Bogobot.java
|
||||
* 2004 The I2P Project
|
||||
* This code is public domain.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.log4j.DailyRollingFileAppender;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.PatternLayout;
|
||||
|
||||
import org.jibble.pircbot.IrcException;
|
||||
import org.jibble.pircbot.NickAlreadyInUseException;
|
||||
import org.jibble.pircbot.PircBot;
|
||||
import org.jibble.pircbot.User;
|
||||
|
||||
/**
|
||||
* TODO 0.4 Implement java.util.Properties for configuration.
|
||||
* TODO 0.4 Add NickServ interface.
|
||||
* TODO 0.5 Add multi-server capability.
|
||||
*
|
||||
* @author hypercubus
|
||||
* @version 0.3.1
|
||||
*/
|
||||
public class Bogobot extends PircBot {
|
||||
|
||||
private static final String INTERVAL_DAILY = "daily";
|
||||
private static final String INTERVAL_MONTHLY = "monthly";
|
||||
private static final String INTERVAL_WEEKLY = "weekly";
|
||||
|
||||
private boolean _isIntentionalDisconnect = false;
|
||||
private long _lastUserlistCommandTimestamp = 0;
|
||||
private Logger _logger = Logger.getLogger(Bogobot.class);
|
||||
/*
|
||||
* The following will soon be moved to bogo.config and loaded via
|
||||
* java.util.Properties when I get some time.
|
||||
*/
|
||||
private String _botPrimaryNick = "somebot";
|
||||
private String _botSecondaryNick = "somebot_";
|
||||
private String _botShutdownPassword = "take off eh";
|
||||
private long _commandAntiFloodInterval = 60;
|
||||
private String _ircChannel = "#i2p-chat";
|
||||
private String _ircServer = "irc.duck.i2p";
|
||||
private int _ircServerPort = 6668;
|
||||
private boolean _isLoggerEnabled = true;
|
||||
private boolean _isUserlistCommandEnabled = true;
|
||||
private String _logFilePrefix = "irc.duck.i2p.i2p-chat";
|
||||
private String _logFileRotationInterval = INTERVAL_DAILY;
|
||||
private String _ownerPrimaryNick = "somenick";
|
||||
private String _ownerSecondaryNick = "somenick_";
|
||||
private String _userlistCommandTrigger = "!who";
|
||||
|
||||
public Bogobot() {
|
||||
this.setName(_botPrimaryNick);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Bogobot bogobot = new Bogobot();
|
||||
|
||||
bogobot.setVerbose(true);
|
||||
|
||||
if (bogobot._isLoggerEnabled)
|
||||
bogobot.initLogger();
|
||||
|
||||
bogobot.connectToServer();
|
||||
}
|
||||
|
||||
protected void onDisconnect() {
|
||||
|
||||
if (_isIntentionalDisconnect)
|
||||
System.exit(0);
|
||||
|
||||
if (_isLoggerEnabled)
|
||||
_logger.info(System.currentTimeMillis() + " quits *** " + this.getName() + " *** (Lost connection)");
|
||||
|
||||
try {
|
||||
Thread.sleep(60000);
|
||||
} catch (InterruptedException e) {
|
||||
// No worries.
|
||||
}
|
||||
connectToServer();
|
||||
}
|
||||
|
||||
protected void onJoin(String channel, String sender, String login, String hostname) {
|
||||
if (sender.equals(this.getName())) {
|
||||
|
||||
if (_isLoggerEnabled)
|
||||
_logger.info(System.currentTimeMillis() + " joins *** " + _botPrimaryNick + " ***");
|
||||
|
||||
} else {
|
||||
|
||||
if (_isLoggerEnabled)
|
||||
_logger.info(System.currentTimeMillis() + " joins " + sender);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected void onMessage(String channel, String sender, String login, String hostname, String message) {
|
||||
message = message.replaceFirst("<.+?> ", "");
|
||||
if (_isUserlistCommandEnabled && message.equals(_userlistCommandTrigger)) {
|
||||
|
||||
if (System.currentTimeMillis() - _lastUserlistCommandTimestamp < _commandAntiFloodInterval * 1000)
|
||||
return;
|
||||
|
||||
Object[] users = getUsers(_ircChannel);
|
||||
String output = "Userlist for " + _ircChannel + ": ";
|
||||
|
||||
for (int i = 0; i < users.length; i++)
|
||||
output += "[" + ((User) users[i]).getNick() + "] ";
|
||||
|
||||
sendMessage(_ircChannel, output);
|
||||
_lastUserlistCommandTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onPart(String channel, String sender, String login, String hostname) {
|
||||
|
||||
if (_isLoggerEnabled)
|
||||
_logger.info(System.currentTimeMillis() + " parts " + sender);
|
||||
|
||||
}
|
||||
|
||||
protected void onPrivateMessage(String sender, String login, String hostname, String message) {
|
||||
/*
|
||||
* Nobody else except the bot's owner can shut it down, unless of
|
||||
* course the owner's nick isn't registered and someone's spoofing it.
|
||||
*/
|
||||
if ((sender.equals(_ownerPrimaryNick) || sender.equals(_ownerSecondaryNick)) && message.equals(_botShutdownPassword)) {
|
||||
|
||||
if (_isLoggerEnabled)
|
||||
_logger.info(System.currentTimeMillis() + " quits *** " + this.getName() + " ***");
|
||||
|
||||
_isIntentionalDisconnect = true;
|
||||
disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onQuit(String sourceNick, String sourceLogin, String sourceHostname, String reason) {
|
||||
|
||||
if (_isLoggerEnabled)
|
||||
_logger.info(System.currentTimeMillis() + " quits " + sourceNick + " " + reason);
|
||||
|
||||
}
|
||||
|
||||
private void connectToServer() {
|
||||
|
||||
int loginAttempts = 0;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
connect(_ircServer, _ircServerPort);
|
||||
break;
|
||||
} catch (NickAlreadyInUseException e) {
|
||||
if (loginAttempts == 1) {
|
||||
System.out.println("Sorry, the primary and secondary bot nicks are already taken. Exiting.");
|
||||
System.exit(1);
|
||||
}
|
||||
loginAttempts++;
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e1) {
|
||||
// Hmph.
|
||||
}
|
||||
|
||||
if (getName().equals(_botPrimaryNick))
|
||||
setName(_botSecondaryNick);
|
||||
else
|
||||
setName(_botPrimaryNick);
|
||||
|
||||
continue;
|
||||
} catch (IOException e) {
|
||||
System.out.println("Error during login: ");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
} catch (IrcException e) {
|
||||
System.out.println("Error during login: ");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
joinChannel(_ircChannel);
|
||||
}
|
||||
|
||||
private void initLogger() {
|
||||
|
||||
String logFilePath = "logs" + File.separator + _logFilePrefix;
|
||||
DailyRollingFileAppender rollingFileAppender = null;
|
||||
|
||||
if (!(new File("logs").exists()))
|
||||
(new File("logs")).mkdirs();
|
||||
|
||||
try {
|
||||
|
||||
if (_logFileRotationInterval.equals("monthly"))
|
||||
rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-MM'.log'");
|
||||
else if (_logFileRotationInterval.equals("weekly"))
|
||||
rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-ww'.log'");
|
||||
else
|
||||
rollingFileAppender = new DailyRollingFileAppender(new PatternLayout("%m%n"), logFilePath, "'.'yyyy-MM-dd'.log'");
|
||||
|
||||
rollingFileAppender.setThreshold(Level.INFO);
|
||||
_logger.addAppender(rollingFileAppender);
|
||||
} catch (IOException ex) {
|
||||
System.out.println("Error: Couldn't create or open an existing log file. Exiting.");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
352
apps/bogobot/Bogoparser.java
Normal file
352
apps/bogobot/Bogoparser.java
Normal file
@ -0,0 +1,352 @@
|
||||
/*
|
||||
* bogoparser - A simple logfile analyzer for bogobot.
|
||||
*
|
||||
* Bogoparser.java
|
||||
* 2004 The I2P Project
|
||||
* This code is public domain.
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author hypercubus
|
||||
* @version 0.3.1
|
||||
*/
|
||||
public class Bogoparser {
|
||||
|
||||
private static void displayUsageAndExit() {
|
||||
System.out.println("\r\nUsage:\r\n\r\n java Bogoparser [--by-duration] <logfile>\r\n");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Bogoparser bogoparser;
|
||||
|
||||
if (args.length < 1 || args.length > 2)
|
||||
displayUsageAndExit();
|
||||
|
||||
if (args.length == 2) {
|
||||
if (!args[0].equals("--by-duration"))
|
||||
displayUsageAndExit();
|
||||
bogoparser = new Bogoparser(args[1], true);
|
||||
}
|
||||
|
||||
if (args.length == 1)
|
||||
bogoparser = new Bogoparser(args[0], false);
|
||||
}
|
||||
|
||||
private Bogoparser(String logfile, boolean sortByDuration) {
|
||||
|
||||
ArrayList sortedSessions;
|
||||
|
||||
if (sortByDuration) {
|
||||
sortedSessions = sortSessionsByDuration(calculateSessionDurations(sortSessionsByTime(readLogfile(logfile))));
|
||||
formatAndOutputByDuration(sortedSessions);
|
||||
} else {
|
||||
sortedSessions = calculateSessionDurations(sortSessionsByQuitReason(sortSessionsByNick(sortSessionsByTime(readLogfile(logfile)))));
|
||||
formatAndOutput(sortedSessions);
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList calculateSessionDurations(ArrayList sortedSessionsByQuitReasonOrDuration) {
|
||||
|
||||
ArrayList calculatedSessionDurations = new ArrayList();
|
||||
|
||||
for (int i = 0; i+1 < sortedSessionsByQuitReasonOrDuration.size(); i += 2) {
|
||||
|
||||
String joinsEntry = (String) sortedSessionsByQuitReasonOrDuration.get(i);
|
||||
String[] joinsEntryFields = joinsEntry.split(" ");
|
||||
|
||||
String quitsEntry = (String) sortedSessionsByQuitReasonOrDuration.get(i+1);
|
||||
Pattern p = Pattern.compile("^([^ ]+) [^ ]+ ([^ ]+) (.*)$");
|
||||
Matcher m = p.matcher(quitsEntry);
|
||||
|
||||
if (m.matches()) {
|
||||
|
||||
String currentJoinTime = joinsEntryFields[0];
|
||||
String currentNick = m.group(2);
|
||||
String currentQuitReason = m.group(3);
|
||||
String currentQuitTime = m.group(1);
|
||||
long joinsTimeInMilliseconds;
|
||||
long quitsTimeInMilliseconds;
|
||||
long sessionLengthInMilliseconds;
|
||||
|
||||
joinsTimeInMilliseconds = Long.parseLong(currentJoinTime);
|
||||
quitsTimeInMilliseconds = Long.parseLong(currentQuitTime);
|
||||
sessionLengthInMilliseconds = quitsTimeInMilliseconds - joinsTimeInMilliseconds;
|
||||
|
||||
String hours = "" + sessionLengthInMilliseconds/1000/60/60;
|
||||
String minutes = "" + (sessionLengthInMilliseconds/1000/60)%60;
|
||||
|
||||
if (hours.length() < 2)
|
||||
hours = "0" + hours;
|
||||
|
||||
if (hours.length() < 3)
|
||||
hours = "0" + hours;
|
||||
|
||||
if (minutes.length() < 2)
|
||||
minutes = "0" + minutes;
|
||||
|
||||
int columnPadding = 19-currentNick.length();
|
||||
String columnPaddingString = " ";
|
||||
|
||||
for (int j = 0; j < columnPadding; j++)
|
||||
columnPaddingString = columnPaddingString + " ";
|
||||
|
||||
calculatedSessionDurations.add(sessionLengthInMilliseconds + " " + currentNick + columnPaddingString + " online " + hours + " hours " + minutes + " minutes " + currentQuitReason);
|
||||
} else {
|
||||
System.out.println("\r\nError: Unexpected entry in logfile: " + quitsEntry);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
return calculatedSessionDurations;
|
||||
}
|
||||
|
||||
private void formatAndOutput(ArrayList sortedSessions) {
|
||||
|
||||
String quitReason = null;
|
||||
|
||||
for (int i = 0; i < sortedSessions.size(); i++) {
|
||||
|
||||
String entry = (String) sortedSessions.get(i);
|
||||
Pattern p = Pattern.compile("^[\\d]+ ([^ ]+ +online [\\d]+ hours [\\d]+ minutes) (.*)$");
|
||||
Matcher m = p.matcher(entry);
|
||||
|
||||
if (m.matches()) {
|
||||
|
||||
if (quitReason == null) {
|
||||
quitReason = m.group(2);
|
||||
System.out.println("\r\nQUIT: " + ((m.group(2).equals("")) ? "No Reason Given" : quitReason) + "\r\n");
|
||||
}
|
||||
|
||||
String tempQuitReason = m.group(2);
|
||||
String tempSession = m.group(1);
|
||||
|
||||
if (tempQuitReason.equals(quitReason)) {
|
||||
System.out.println(" " + tempSession);
|
||||
} else {
|
||||
quitReason = null;
|
||||
i -= 1;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
System.out.println("\r\nError: Unexpected entry in logfile: " + entry);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
System.out.println("\r\n");
|
||||
}
|
||||
|
||||
private void formatAndOutputByDuration(ArrayList sortedSessions) {
|
||||
System.out.println("\r\n");
|
||||
|
||||
for (int i = 0; i < sortedSessions.size(); i++) {
|
||||
String[] columns = ((String) sortedSessions.get(i)).split(" ", 2);
|
||||
System.out.println(columns[1]);
|
||||
}
|
||||
|
||||
System.out.println("\r\n");
|
||||
}
|
||||
|
||||
private ArrayList readLogfile(String logfile) {
|
||||
|
||||
ArrayList log = new ArrayList();
|
||||
|
||||
try {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(logfile)));
|
||||
|
||||
for (String line; (line = in.readLine()) != null; )
|
||||
log.add(line);
|
||||
|
||||
in.close();
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println("\r\nError: Can't find logfile '" + logfile + "'.\r\n");
|
||||
System.exit(1);
|
||||
|
||||
} catch (IOException e) {
|
||||
System.out.println("\r\nError: Can't read logfile '" + logfile + "'.\r\n");
|
||||
System.exit(1);
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs an odd-even transposition sort.
|
||||
*/
|
||||
private ArrayList sortSessionsByDuration(ArrayList calculatedSessionDurations) {
|
||||
|
||||
for (int i = 0; i < calculatedSessionDurations.size()/2; i++) {
|
||||
for (int j = 0; j+1 < calculatedSessionDurations.size(); j += 2) {
|
||||
|
||||
String[] currentDurationString = ((String) calculatedSessionDurations.get(j)).split(" ", 2);
|
||||
long currentDuration = Long.parseLong(currentDurationString[0]);
|
||||
String[] nextDurationString = ((String) calculatedSessionDurations.get(j+1)).split(" ", 2);
|
||||
long nextDuration = Long.parseLong(nextDurationString[0]);
|
||||
|
||||
if (currentDuration > nextDuration) {
|
||||
calculatedSessionDurations.add(j, calculatedSessionDurations.get(j+1));
|
||||
calculatedSessionDurations.remove(j+2);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 1; j+1 < calculatedSessionDurations.size(); j += 2) {
|
||||
|
||||
String[] currentDurationString = ((String) calculatedSessionDurations.get(j)).split(" ", 2);
|
||||
long currentDuration = Long.parseLong(currentDurationString[0]);
|
||||
String[] nextDurationString = ((String) calculatedSessionDurations.get(j+1)).split(" ", 2);
|
||||
long nextDuration = Long.parseLong(nextDurationString[0]);
|
||||
|
||||
if (currentDuration > nextDuration) {
|
||||
calculatedSessionDurations.add(j, calculatedSessionDurations.get(j+1));
|
||||
calculatedSessionDurations.remove(j+2);
|
||||
}
|
||||
}
|
||||
}
|
||||
return calculatedSessionDurations;
|
||||
}
|
||||
|
||||
private ArrayList sortSessionsByNick(ArrayList sortedSessionsByTime) {
|
||||
|
||||
ArrayList sortedSessionsByNick = new ArrayList();
|
||||
|
||||
while (sortedSessionsByTime.size() != 0) {
|
||||
|
||||
String entry = (String) sortedSessionsByTime.get(0);
|
||||
String[] entryFields = entry.split(" ");
|
||||
String currentNick = entryFields[2];
|
||||
|
||||
sortedSessionsByNick.add(entry);
|
||||
sortedSessionsByNick.add(sortedSessionsByTime.get(1));
|
||||
sortedSessionsByTime.remove(0);
|
||||
sortedSessionsByTime.remove(0);
|
||||
for (int i = 0; i+1 < sortedSessionsByTime.size(); i += 2) {
|
||||
|
||||
String nextEntry = (String) sortedSessionsByTime.get(i);
|
||||
String[] nextEntryFields = nextEntry.split(" ");
|
||||
|
||||
if (nextEntryFields[2].equals(currentNick)) {
|
||||
sortedSessionsByNick.add(nextEntry);
|
||||
sortedSessionsByNick.add(sortedSessionsByTime.get(i+1));
|
||||
sortedSessionsByTime.remove(i);
|
||||
sortedSessionsByTime.remove(i);
|
||||
i -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sortedSessionsByNick;
|
||||
}
|
||||
|
||||
private ArrayList sortSessionsByQuitReason(ArrayList sortedSessionsByNick) {
|
||||
|
||||
ArrayList sortedSessionsByQuitReason = new ArrayList();
|
||||
|
||||
while (sortedSessionsByNick.size() != 0) {
|
||||
|
||||
String entry = (String) sortedSessionsByNick.get(1);
|
||||
Pattern p = Pattern.compile("^[^ ]+ [^ ]+ [^ ]+ (.*)$");
|
||||
Matcher m = p.matcher(entry);
|
||||
|
||||
if (m.matches()) {
|
||||
|
||||
String currentQuitReason = m.group(1);
|
||||
|
||||
sortedSessionsByQuitReason.add(sortedSessionsByNick.get(0));
|
||||
sortedSessionsByQuitReason.add(entry);
|
||||
sortedSessionsByNick.remove(0);
|
||||
sortedSessionsByNick.remove(0);
|
||||
for (int i = 0; i+1 < sortedSessionsByNick.size(); i += 2) {
|
||||
|
||||
String nextEntry = (String) sortedSessionsByNick.get(i+1);
|
||||
Pattern p2 = Pattern.compile("^[^ ]+ [^ ]+ [^ ]+ (.*)$");
|
||||
Matcher m2 = p2.matcher(nextEntry);
|
||||
|
||||
if (m2.matches()) {
|
||||
|
||||
String nextQuitReason = m2.group(1);
|
||||
|
||||
if (nextQuitReason.equals(currentQuitReason)) {
|
||||
sortedSessionsByQuitReason.add(sortedSessionsByNick.get(i));
|
||||
sortedSessionsByQuitReason.add(nextEntry);
|
||||
sortedSessionsByNick.remove(i);
|
||||
sortedSessionsByNick.remove(i);
|
||||
i -= 2;
|
||||
}
|
||||
} else {
|
||||
System.out.println("\r\nError: Unexpected entry in logfile: " + nextEntry);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println("\r\nError: Unexpected entry in logfile: " + entry);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
return sortedSessionsByQuitReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sessions terminated with "parts" messages instead of "quits" are filtered
|
||||
* out.
|
||||
*/
|
||||
private ArrayList sortSessionsByTime(ArrayList log) {
|
||||
|
||||
ArrayList sortedSessionsByTime = new ArrayList();
|
||||
|
||||
mainLoop:
|
||||
while (log.size() > 0) {
|
||||
|
||||
String entry = (String) log.get(0);
|
||||
String[] entryFields = entry.split(" ");
|
||||
|
||||
if (entryFields[1].equals("quits") && !entryFields[1].equals("joins")) {
|
||||
/*
|
||||
* Discard entry. The specified log either doesn't contain
|
||||
* the corresponding "joins" time for this quit entry or the
|
||||
* entry is a "parts" or unknown message, and in both cases
|
||||
* the entry's data is useless.
|
||||
*/
|
||||
log.remove(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 1; i < log.size(); i++) { // Find corresponding "quits" entry.
|
||||
|
||||
String tempEntry = (String) log.get(i);
|
||||
String[] tempEntryFields = tempEntry.split(" ");
|
||||
|
||||
if (tempEntryFields[2].equals(entryFields[2])) { // Check if the nick fields for the two entries match.
|
||||
if (!tempEntryFields[1].equals("quits")) {
|
||||
if (tempEntryFields[1].equals("joins")) { // Don't discard a subsequent "joins" entry.
|
||||
log.remove(0);
|
||||
continue mainLoop;
|
||||
}
|
||||
log.remove(i);
|
||||
continue;
|
||||
}
|
||||
sortedSessionsByTime.add(entry);
|
||||
sortedSessionsByTime.add(tempEntry);
|
||||
log.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Discard "joins" entry. The specified log doesn't contain the
|
||||
* corresponding "quits" time for this entry so the entry's
|
||||
* data is useless.
|
||||
*/
|
||||
|
||||
log.remove(0);
|
||||
}
|
||||
|
||||
return sortedSessionsByTime;
|
||||
}
|
||||
}
|
48
apps/bogobot/LICENSE_log4j.txt
Normal file
48
apps/bogobot/LICENSE_log4j.txt
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* ============================================================================
|
||||
* The Apache Software License, Version 1.1
|
||||
* ============================================================================
|
||||
*
|
||||
* Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modifica-
|
||||
* tion, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The end-user documentation included with the redistribution, if any, must
|
||||
* include the following acknowledgment: "This product includes software
|
||||
* developed by the Apache Software Foundation (http://www.apache.org/)."
|
||||
* Alternately, this acknowledgment may appear in the software itself, if
|
||||
* and wherever such third-party acknowledgments normally appear.
|
||||
*
|
||||
* 4. The names "log4j" and "Apache Software Foundation" must not be used to
|
||||
* endorse or promote products derived from this software without prior
|
||||
* written permission. For written permission, please contact
|
||||
* apache@apache.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Apache", nor may
|
||||
* "Apache" appear in their name, without prior written permission of the
|
||||
* Apache Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
|
||||
* DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* on behalf of the Apache Software Foundation. For more information on the
|
||||
* Apache Software Foundation, please see <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
340
apps/bogobot/LICENSE_pircbot.txt
Normal file
340
apps/bogobot/LICENSE_pircbot.txt
Normal file
@ -0,0 +1,340 @@
|
||||
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.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 of the License, 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; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
70
apps/bogobot/bogo.config
Normal file
70
apps/bogobot/bogo.config
Normal file
@ -0,0 +1,70 @@
|
||||
#####
|
||||
# Bogobot user configuration (This file is not yet used.)
|
||||
#####
|
||||
|
||||
###
|
||||
# The bot's nick and backup nick. You will probably want to register these with
|
||||
# the IRC server's NickServ.(a NickServ interface is forthcoming).
|
||||
#
|
||||
botPrimaryNick=somebot
|
||||
botSecondaryNick=somebot_
|
||||
|
||||
#####
|
||||
# The bot owner's nick and backup nick. One of these must match the owner's
|
||||
# currently-used nick or else remote shutdown will not be possible. You will
|
||||
# probably want to register these with the IRC server's NickServ.
|
||||
#
|
||||
ownerPrimaryNick=somenick
|
||||
ownerSecondaryNick=somenick_
|
||||
|
||||
###
|
||||
# The bot will disconnect and shut down when sent this password via private
|
||||
# message (aka query) from either of the owner nicks specified above. DO NOT USE
|
||||
# THIS DEFAULT VALUE!
|
||||
#
|
||||
botShutdownPassword=take off eh
|
||||
|
||||
###
|
||||
# The server, channel, and port the bot will connect to.
|
||||
#
|
||||
ircChannel=#i2p-chat
|
||||
ircServer=irc.duck.i2p
|
||||
ircServerPort=6668
|
||||
|
||||
###
|
||||
# Set to "true" to enable logging, else "false" (but don't use quotation marks).
|
||||
#
|
||||
isLoggerEnabled=true
|
||||
|
||||
###
|
||||
# The prefix to be used for the filenames of logs.
|
||||
#
|
||||
logFilePrefix=irc.duck.i2p.i2p-chat
|
||||
|
||||
###
|
||||
# How often the logs should be rotated. Either "daily", "weekly", or "monthly"
|
||||
# (but don't use quotation marks).
|
||||
#
|
||||
logFileRotationInterval=daily
|
||||
|
||||
###
|
||||
# Set to "true" to enable the userlist command, else "false" (but don't use
|
||||
# quotation marks).
|
||||
#
|
||||
isUserlistCommandEnabled=true
|
||||
|
||||
###
|
||||
# The userlist trigger command to listen for. It is a good idea to prefix
|
||||
# triggers with some non-alphanumeric character in order to avoid accidental
|
||||
# trigger use during normal channel conversation. In most cases you will
|
||||
# probably want to choose a unique trigger here that no other bots in the
|
||||
# channel will respond to.
|
||||
#
|
||||
userlistCommandTrigger=!who
|
||||
|
||||
###
|
||||
# The number of seconds to rest after replying to a userlist command issued by
|
||||
# a user in the channel. The bot will ignore subsequent userlist commands during
|
||||
# this period. This helps prevent flooding.
|
||||
#
|
||||
commandAntiFloodInterval=60
|
1
apps/bogobot/bogobot.bat
Normal file
1
apps/bogobot/bogobot.bat
Normal file
@ -0,0 +1 @@
|
||||
java -cp .;log4j-1.2.8.jar;pircbot.jar Bogobot
|
2
apps/bogobot/bogobot.sh
Normal file
2
apps/bogobot/bogobot.sh
Normal file
@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
java -cp .:log4j-1.2.8.jar:pircbot.jar Bogobot
|
63
apps/bogobot/build.xml
Normal file
63
apps/bogobot/build.xml
Normal file
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- ********************************************************** -->
|
||||
<!-- bogobot - A simple join/part stats logger bot for I2P IRC. -->
|
||||
<!-- -->
|
||||
<!-- build.xml -->
|
||||
<!-- 2004 The I2P Project -->
|
||||
<!-- This code is public domain. -->
|
||||
<!-- -->
|
||||
<!-- author hypercubus -->
|
||||
<!-- version 0.3.1 -->
|
||||
<!-- ********************************************************** -->
|
||||
|
||||
<project basedir="." default="compile" name="Bogobot">
|
||||
|
||||
<!-- init:
|
||||
Create distribution directory if missing and initialize time stamp for
|
||||
archive naming -->
|
||||
<target name="init">
|
||||
<mkdir dir="dist" />
|
||||
<tstamp>
|
||||
<format pattern="yyyy-MM-dd" property="DSTAMP" />
|
||||
</tstamp>
|
||||
</target>
|
||||
|
||||
<!-- compile:
|
||||
Compile source code -->
|
||||
<target depends="init" description="Compile source code" name="compile">
|
||||
<javac classpath="${basedir}" source="1.4" srcdir="." />
|
||||
</target>
|
||||
|
||||
<!-- dist.bin:
|
||||
Create the binary distribution archive -->
|
||||
<target depends="init,compile" description="Create the binary distribution archive" name="dist.bin">
|
||||
<zip destfile="dist/Bogobot_${DSTAMP}.zip">
|
||||
<zipfileset dir="${basedir}" includes="bogobot.bat Bogobot.class bogobot.sh Bogoparser.class LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
<!-- dist.source:
|
||||
Create the source distribution archive -->
|
||||
<target depends="init" description="Create the source distribution archive" name="dist.source">
|
||||
<zip destfile="dist/Bogobot_source_${DSTAMP}.zip">
|
||||
<zipfileset dir="${basedir}" includes="bogobot.bat Bogobot.java bogobot.sh Bogoparser.java build.xml build_eclipse.xml LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
<!-- dist:
|
||||
Create both the binary and source distribution archives -->
|
||||
<target depends="dist.bin,dist.source" description="Create both the binary and source distribution archives" name="dist">
|
||||
<echo message="Successfully created binary and source distribution archives in directory 'dist'." />
|
||||
</target>
|
||||
|
||||
<!-- clean:
|
||||
Delete all class files and temporary directories -->
|
||||
<target description="Delete all class files and temporary directories" name="clean">
|
||||
<delete>
|
||||
<fileset dir="${basedir}" includes="**/*.class" />
|
||||
</delete>
|
||||
<echo message="Clean successful." />
|
||||
</target>
|
||||
|
||||
</project>
|
57
apps/bogobot/build_eclipse.xml
Normal file
57
apps/bogobot/build_eclipse.xml
Normal file
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- ********************************************************** -->
|
||||
<!-- bogobot - A simple join/part stats logger bot for I2P IRC. -->
|
||||
<!-- -->
|
||||
<!-- build_eclipse.xml -->
|
||||
<!-- 2004 The I2P Project -->
|
||||
<!-- This code is public domain. -->
|
||||
<!-- -->
|
||||
<!-- author hypercubus -->
|
||||
<!-- version 0.3.1 -->
|
||||
<!-- ********************************************************** -->
|
||||
|
||||
<project basedir="." default="dist" name="Bogobot">
|
||||
|
||||
<!-- init:
|
||||
Create distribution directory if missing and initialize time stamp for
|
||||
archive naming -->
|
||||
<target name="init">
|
||||
<mkdir dir="dist" />
|
||||
<tstamp>
|
||||
<format pattern="yyyy-MM-dd" property="DSTAMP" />
|
||||
</tstamp>
|
||||
</target>
|
||||
|
||||
<!-- dist.bin:
|
||||
Create the binary distribution archive -->
|
||||
<target depends="init" description="Create the binary distribution archive" name="dist.bin">
|
||||
<zip destfile="dist/Bogobot_${DSTAMP}.zip">
|
||||
<zipfileset dir="${basedir}" includes="bogobot.bat Bogobot.class bogobot.sh Bogoparser.class LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
<!-- dist.source:
|
||||
Create the source distribution archive -->
|
||||
<target depends="init" description="Create the source distribution archive" name="dist.source">
|
||||
<zip destfile="dist/Bogobot_source_${DSTAMP}.zip">
|
||||
<zipfileset dir="${basedir}" includes="bogobot.bat Bogobot.java bogobot.sh Bogoparser.java build.xml build_eclipse.xml LICENSE_log4j.txt LICENSE_pircbot.txt log4j-1.2.8.jar pircbot.jar" />
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
<!-- dist:
|
||||
Create both the binary and source distribution archives -->
|
||||
<target depends="dist.bin,dist.source" description="Create both the binary and source distribution archives" name="dist">
|
||||
<echo message="Successfully created binary and source distribution archives in directory 'dist'." />
|
||||
</target>
|
||||
|
||||
<!-- clean:
|
||||
Delete all class files and temporary directories -->
|
||||
<target description="Delete all class files and temporary directories" name="clean">
|
||||
<delete>
|
||||
<fileset dir="${basedir}" includes="**/*.class" />
|
||||
</delete>
|
||||
<echo message="Clean successful." />
|
||||
</target>
|
||||
|
||||
</project>
|
BIN
apps/bogobot/log4j-1.2.8.jar
Normal file
BIN
apps/bogobot/log4j-1.2.8.jar
Normal file
Binary file not shown.
BIN
apps/bogobot/pircbot.jar
Normal file
BIN
apps/bogobot/pircbot.jar
Normal file
Binary file not shown.
@ -27,7 +27,7 @@
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: platform.hpp,v 1.4 2004/07/16 23:54:45 mpc Exp $
|
||||
* $Id: platform.hpp,v 1.5 2004/07/22 03:54:01 mpc Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -56,6 +56,7 @@
|
||||
#endif
|
||||
#include <cassert>
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
@ -65,10 +66,12 @@
|
||||
#ifndef WINTHREAD
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#include <stdint.h> // TODO replace with Boost's version
|
||||
#include <string>
|
||||
#if defined WINSOCK || defined WINTHREAD
|
||||
#include <windows.h>
|
||||
#endif
|
||||
using namespace std;
|
||||
#include "types.hpp"
|
||||
|
||||
#endif // LIBSOCKTHREAD_PLATFORM_HPP
|
||||
|
@ -27,7 +27,7 @@
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: socket.cpp,v 1.7 2004/07/22 03:54:01 mpc Exp $
|
||||
* $Id: socket.cpp,v 1.8 2004/07/22 22:08:20 mpc Exp $
|
||||
*/
|
||||
|
||||
#include "platform.hpp"
|
||||
@ -35,10 +35,38 @@
|
||||
#include "socket.hpp"
|
||||
using namespace Libsockthread;
|
||||
|
||||
/*
|
||||
* Closes the socket
|
||||
*/
|
||||
void Socket::close()
|
||||
{
|
||||
if (sock != SERR) {
|
||||
if (close(sock) == -1)
|
||||
; // FIXME log the error
|
||||
}
|
||||
sock = SERR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Changes the address associated with the socket
|
||||
*/
|
||||
void Socket::set_addr(Socket_addr& addr)
|
||||
{
|
||||
close();
|
||||
this->addr = addr;
|
||||
setup_socket();
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepares the socket for use
|
||||
*/
|
||||
void Socket::setup_socket()
|
||||
{
|
||||
assert(sock == SERR); // the descriptor shouldn't be active
|
||||
if (!addr.is_ready())
|
||||
throw Socket_error("Socket isn't ready");
|
||||
throw Socket_error("Couldn't create socket: address isn't ready");
|
||||
|
||||
sock = socket(addr.get_family(), get_type(), 0);
|
||||
sock = socket(addr.get_family(), addr.get_type(), 0);
|
||||
if (sock == SERR)
|
||||
throw Socket_error(strerror(errno));
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: socket.hpp,v 1.7 2004/07/22 03:54:01 mpc Exp $
|
||||
* $Id: socket.hpp,v 1.8 2004/07/22 22:08:20 mpc Exp $
|
||||
*/
|
||||
|
||||
#ifndef LIBSOCKTHREAD_SOCKET_HPP
|
||||
@ -36,19 +36,32 @@
|
||||
namespace Libsockthread {
|
||||
class Socket {
|
||||
public:
|
||||
#ifdef WINSOCK
|
||||
typedef SOCKET socket_t;
|
||||
enum { SERR = SOCKET_ERROR };
|
||||
#else
|
||||
typedef int socket_t;
|
||||
enum { SERR = -1 };
|
||||
#endif
|
||||
|
||||
Socket()
|
||||
: addr(), sock(SERR) {}
|
||||
Socket(Socket_addr& addr) // throws Socket_error
|
||||
: addr(addr) { setup_socket(); }
|
||||
: addr(addr), sock(SERR) { setup_socket(); }
|
||||
|
||||
void close();
|
||||
size_t read(string& buf, size_t max);
|
||||
void set_addr(Socket_addr& addr) // throws Socket_error
|
||||
{ this->addr = addr; setup_socket(); }
|
||||
size_t read(vector<uchar_t>& buf, size_t max = 0);
|
||||
size_t read_until(vector<uchar_t>& buf, uchar_t delim = '\n');
|
||||
void set_addr(Socket_addr& addr); // throws Socket_error
|
||||
void set_blocking(bool blocking);
|
||||
size_t write(vector<uchar_t>& buf);
|
||||
void write_all(vector<uchar_t>& buf);
|
||||
size_t write_until(vector<uchar_t& buf, uchar_t delim = '\n');
|
||||
private:
|
||||
void setup_socket(); // throws Socket_error
|
||||
|
||||
Socket_addr addr;
|
||||
int sock;
|
||||
socket_t sock;
|
||||
};
|
||||
}
|
||||
|
||||
|
45
apps/enclave/libsockthread/src/types.hpp
Normal file
45
apps/enclave/libsockthread/src/types.hpp
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the author nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef LIBSOCKTHREAD_TYPES_HPP
|
||||
#define LIBSOCKTHREAD_TYPES_HPP
|
||||
|
||||
/*
|
||||
* Shorten some standard variable types
|
||||
*/
|
||||
typedef signed char schar_t;
|
||||
typedef unsigned char uchar_t;
|
||||
typedef unsigned int uint_t;
|
||||
typedef unsigned long ulong_t;
|
||||
typedef unsigned short ushort_t;
|
||||
|
||||
#endif // LIBSOCKTHREAD_TYPES_HPP
|
@ -27,7 +27,7 @@ public class PeerData {
|
||||
/** date sent (Long) to EventDataPoint containing the datapoints sent in the current period */
|
||||
private Map _dataPoints;
|
||||
/** date sent (Long) to EventDataPoint containing pings that haven't yet timed out or been ponged */
|
||||
private Map _pendingPings;
|
||||
private TreeMap _pendingPings;
|
||||
private long _sessionStart;
|
||||
private long _lifetimeSent;
|
||||
private long _lifetimeReceived;
|
||||
@ -208,14 +208,32 @@ public class PeerData {
|
||||
public void pongReceived(long dateSent, long pongSent) {
|
||||
long now = Clock.getInstance().now();
|
||||
synchronized (_updateLock) {
|
||||
EventDataPoint data = (EventDataPoint) _pendingPings.remove(new Long(dateSent));
|
||||
if (_pendingPings.size() <= 0) {
|
||||
_log.warn("Pong received (sent at " + dateSent + ", " + (now-dateSent)
|
||||
+ "ms ago, pong delay " + (pongSent-dateSent) + "ms, pong receive delay "
|
||||
+ (now-pongSent) + "ms)");
|
||||
return;
|
||||
}
|
||||
Long first = (Long)_pendingPings.firstKey();
|
||||
EventDataPoint data = (EventDataPoint)_pendingPings.remove(new Long(dateSent));
|
||||
|
||||
if (data != null) {
|
||||
data.setPongReceived(now);
|
||||
data.setPongSent(pongSent);
|
||||
data.setWasPonged(true);
|
||||
locked_addDataPoint(data);
|
||||
|
||||
if (dateSent != first.longValue()) {
|
||||
_log.error("Out of order delivery: received " + dateSent
|
||||
+ " but the first pending is " + first.longValue()
|
||||
+ " (delta " + (dateSent - first.longValue()) + ")");
|
||||
} else {
|
||||
_log.info("In order delivery for " + dateSent + " in ping "
|
||||
+ _peer.getComment());
|
||||
}
|
||||
} else {
|
||||
_log.warn("Pong received, but no matching ping? ping sent at = " + dateSent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_sendRate.addData(pongSent - dateSent, 0);
|
||||
|
@ -22,6 +22,47 @@
|
||||
<attribute name="Class-Path" value="i2p.jar mstreaming.jar" />
|
||||
</manifest>
|
||||
</jar>
|
||||
<ant target="war" />
|
||||
</target>
|
||||
<target name="war" depends="precompilejsp">
|
||||
<war destfile="build/i2ptunnel.war" webxml="../jsp/web-out.xml"
|
||||
basedir="../jsp/" excludes="web.xml, *.java, *.jsp">
|
||||
</war>
|
||||
</target>
|
||||
<target name="precompilejsp">
|
||||
<mkdir dir="../jsp/WEB-INF/" />
|
||||
<mkdir dir="../jsp/WEB-INF/classes" />
|
||||
<!-- there are various jspc ant tasks, but they all seem a bit flakey -->
|
||||
<java classname="org.apache.jasper.JspC" fork="true" >
|
||||
<classpath>
|
||||
<pathelement location="../../jetty/jettylib/jasper-compiler.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/ant.jar" />
|
||||
<pathelement location="build/i2ptunnel.jar" />
|
||||
</classpath>
|
||||
<arg value="-d" />
|
||||
<arg value="../jsp/WEB-INF/classes" />
|
||||
<arg value="-v9" />
|
||||
<arg value="-p" />
|
||||
<arg value="net.i2p.i2ptunnel.jsp" />
|
||||
<arg value="-webinc" />
|
||||
<arg value="../jsp/web-fragment.xml" />
|
||||
<arg value="-webapp" />
|
||||
<arg value="../jsp/" />
|
||||
</java>
|
||||
<javac destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="*.java">
|
||||
<classpath>
|
||||
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="build/i2ptunnel.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
<copy file="../jsp/web.xml" tofile="../jsp/web-out.xml" />
|
||||
<loadfile property="jspc.web.fragment" srcfile="../jsp/web-fragment.xml" />
|
||||
<replace file="../jsp/web-out.xml">
|
||||
<replacefilter token="<!-- precompiled servlets -->" value="${jspc.web.fragment}" />
|
||||
</replace>
|
||||
</target>
|
||||
<target name="javadoc">
|
||||
<mkdir dir="./build" />
|
||||
|
@ -74,13 +74,13 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
|
||||
public static final int PACKET_DELAY = 100;
|
||||
|
||||
public static boolean ownDest = false;
|
||||
public 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;
|
||||
public String port = System.getProperty(I2PClient.PROP_TCP_PORT, "7654");
|
||||
public String host = System.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||
public String listenHost = host;
|
||||
|
||||
public static long readTimeout = -1;
|
||||
public long readTimeout = -1;
|
||||
|
||||
private static final String nocli_args[] = { "-nocli", "-die"};
|
||||
|
||||
@ -408,9 +408,9 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
*/
|
||||
public void runClient(String args[], Logging l) {
|
||||
if (args.length == 2) {
|
||||
int port = -1;
|
||||
int portNum = -1;
|
||||
try {
|
||||
port = Integer.parseInt(args[0]);
|
||||
portNum = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
l.log("invalid port");
|
||||
_log.error(getPrefix() + "Port specified is not valid: " + args[0], nfe);
|
||||
@ -418,9 +418,15 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
return;
|
||||
}
|
||||
I2PTunnelTask task;
|
||||
task = new I2PTunnelClient(port, args[1], l, ownDest, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
notifyEvent("clientTaskId", new Integer(task.getId()));
|
||||
try {
|
||||
task = new I2PTunnelClient(portNum, args[1], l, ownDest, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
notifyEvent("clientTaskId", new Integer(task.getId()));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_log.error(getPrefix() + "Invalid I2PTunnel config to create a client [" + host + ":"+ port + "]", iae);
|
||||
l.log("Invalid I2PTunnel configuration [" + host + ":" + port + "]");
|
||||
notifyEvent("clientTaskId", new Integer(-1));
|
||||
}
|
||||
} else {
|
||||
l.log("client <port> <pubkey>|file:<pubkeyfile>");
|
||||
l.log(" creates a client that forwards port to the pubkey.\n"
|
||||
@ -455,9 +461,15 @@ public class I2PTunnel implements Logging, EventDispatcher {
|
||||
proxy = args[1];
|
||||
}
|
||||
I2PTunnelTask task;
|
||||
task = new I2PTunnelHTTPClient(port, l, ownDest, proxy, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
notifyEvent("httpclientTaskId", new Integer(task.getId()));
|
||||
try {
|
||||
task = new I2PTunnelHTTPClient(port, l, ownDest, proxy, (EventDispatcher) this, this);
|
||||
addtask(task);
|
||||
notifyEvent("httpclientTaskId", new Integer(task.getId()));
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_log.error(getPrefix() + "Invalid I2PTunnel config to create an httpclient [" + host + ":"+ port + "]", iae);
|
||||
l.log("Invalid I2PTunnel configuration [" + host + ":" + port + "]");
|
||||
notifyEvent("httpclientTaskId", new Integer(-1));
|
||||
}
|
||||
} else {
|
||||
l.log("httpclient <port> [<proxy>]");
|
||||
l.log(" creates a client that distributes HTTP requests.");
|
||||
|
@ -19,7 +19,13 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
|
||||
protected long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
|
||||
public I2PTunnelClient(int localPort, String destination, Logging l, boolean ownDest, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
/**
|
||||
* @throws IllegalArgumentException if the I2PTunnel does not contain
|
||||
* valid config to contact the router
|
||||
*/
|
||||
public I2PTunnelClient(int localPort, String destination, Logging l,
|
||||
boolean ownDest, EventDispatcher notifyThis,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||
super(localPort, ownDest, l, notifyThis, "SynSender", tunnel);
|
||||
|
||||
if (waitEventValue("openBaseClientResult").equals("error")) {
|
||||
|
@ -60,7 +60,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
// I2PTunnelClientBase(localPort, ownDest, l, (EventDispatcher)null);
|
||||
//}
|
||||
|
||||
public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l, EventDispatcher notifyThis, String handlerName, I2PTunnel tunnel) {
|
||||
/**
|
||||
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
|
||||
* badly that we cant create a socketManager
|
||||
*/
|
||||
public I2PTunnelClientBase(int localPort, boolean ownDest, Logging l,
|
||||
EventDispatcher notifyThis, String handlerName,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException{
|
||||
super(localPort + " (uninitialized)", notifyThis, tunnel);
|
||||
_clientId = ++__clientId;
|
||||
this.localPort = localPort;
|
||||
@ -74,7 +80,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
sockMgr = getSocketManager();
|
||||
}
|
||||
}
|
||||
if (sockMgr == null) throw new NullPointerException();
|
||||
if (sockMgr == null) {
|
||||
l.log("Invalid I2CP configuration");
|
||||
throw new IllegalArgumentException("Socket manager could not be created");
|
||||
}
|
||||
l.log("I2P session created");
|
||||
|
||||
Thread t = new I2PThread(this);
|
||||
@ -122,7 +131,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
props.putAll(System.getProperties());
|
||||
else
|
||||
props.putAll(tunnel.getClientOptions());
|
||||
I2PSocketManager sockManager = I2PSocketManagerFactory.createManager(I2PTunnel.host, Integer.parseInt(I2PTunnel.port), props);
|
||||
I2PSocketManager sockManager = I2PSocketManagerFactory.createManager(tunnel.host, Integer.parseInt(tunnel.port), props);
|
||||
if (sockManager == null) return null;
|
||||
sockManager.setName("Client");
|
||||
return sockManager;
|
||||
}
|
||||
@ -133,9 +143,9 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
|
||||
protected final InetAddress getListenHost(Logging l) {
|
||||
try {
|
||||
return InetAddress.getByName(I2PTunnel.listenHost);
|
||||
return InetAddress.getByName(getTunnel().listenHost);
|
||||
} catch (UnknownHostException uhe) {
|
||||
l.log("Could not find listen host to bind to [" + I2PTunnel.host + "]");
|
||||
l.log("Could not find listen host to bind to [" + getTunnel().host + "]");
|
||||
_log.error("Error finding host to bind", uhe);
|
||||
notifyEvent("openBaseClientResult", "error");
|
||||
return null;
|
||||
@ -212,7 +222,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
localPort = ss.getLocalPort();
|
||||
}
|
||||
notifyEvent("clientLocalPort", new Integer(ss.getLocalPort()));
|
||||
l.log("Listening for clients on port " + localPort + " of " + I2PTunnel.listenHost);
|
||||
l.log("Listening for clients on port " + localPort + " of " + getTunnel().listenHost);
|
||||
|
||||
// Notify constructor that port is ready
|
||||
synchronized (this) {
|
||||
|
@ -9,8 +9,12 @@ import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataFormatException;
|
||||
@ -42,7 +46,7 @@ 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 List proxyList;
|
||||
|
||||
private final static byte[] ERR_REQUEST_DENIED =
|
||||
("HTTP/1.1 403 Access Denied\r\n"+
|
||||
@ -80,8 +84,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
/** used to assign unique IDs to the threads / clients. no logic or functionality */
|
||||
private static volatile long __clientId = 0;
|
||||
|
||||
public I2PTunnelHTTPClient(int localPort, Logging l, boolean ownDest, String wwwProxy, EventDispatcher notifyThis, I2PTunnel tunnel) {
|
||||
|
||||
/**
|
||||
* @throws IllegalArgumentException if the I2PTunnel does not contain
|
||||
* valid config to contact the router
|
||||
*/
|
||||
public I2PTunnelHTTPClient(int localPort, Logging l, boolean ownDest,
|
||||
String wwwProxy, EventDispatcher notifyThis,
|
||||
I2PTunnel tunnel) throws IllegalArgumentException {
|
||||
super(localPort, ownDest, l, notifyThis, "HTTPHandler " + (++__clientId), tunnel);
|
||||
|
||||
if (waitEventValue("openBaseClientResult").equals("error")) {
|
||||
@ -89,9 +99,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
return;
|
||||
}
|
||||
|
||||
this.wwwProxy = wwwProxy;
|
||||
proxyList = new ArrayList();
|
||||
if (wwwProxy != null) {
|
||||
StringTokenizer tok = new StringTokenizer(wwwProxy, ",");
|
||||
while (tok.hasMoreTokens())
|
||||
proxyList.add(tok.nextToken().trim());
|
||||
}
|
||||
|
||||
setName(getLocalPort() + " -> HTTPClient [WWW outproxy: " + this.wwwProxy + "]");
|
||||
setName(getLocalPort() + " -> HTTPClient [WWW outproxy list: " + wwwProxy + "]");
|
||||
|
||||
startRunning();
|
||||
|
||||
@ -100,10 +115,20 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
|
||||
private String getPrefix() { return "Client[" + _clientId + "]: "; }
|
||||
|
||||
private String selectProxy() {
|
||||
if (proxyList.size() <= 0) {
|
||||
l.log("Proxy list is emtpy - no outproxy available");
|
||||
return null;
|
||||
}
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(proxyList.size());
|
||||
return (String)proxyList.get(index);
|
||||
}
|
||||
|
||||
protected void clientConnectionRun(Socket s) {
|
||||
OutputStream out = null;
|
||||
String targetRequest = null;
|
||||
boolean usingWWWProxy = false;
|
||||
String currentProxy = null;
|
||||
InactivityTimeoutThread timeoutThread = null;
|
||||
try {
|
||||
out = s.getOutputStream();
|
||||
@ -154,7 +179,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
line = method + " " + request.substring(pos);
|
||||
} else if (host.indexOf(".") != -1) {
|
||||
// The request must be forwarded to a WWW proxy
|
||||
destination = wwwProxy;
|
||||
currentProxy = selectProxy();
|
||||
destination = currentProxy;
|
||||
usingWWWProxy = true;
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Host doesnt end with .i2p and it contains a period [" + host + "]: wwwProxy!");
|
||||
@ -236,25 +262,25 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
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 = new InactivityTimeoutThread(runner, out, targetRequest, usingWWWProxy, currentProxy, s);
|
||||
timeoutThread.start();
|
||||
} catch (SocketException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info(getPrefix() + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy);
|
||||
closeSocket(s);
|
||||
} catch (IOException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info(getPrefix() + "Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy);
|
||||
closeSocket(s);
|
||||
} catch (I2PException ex) {
|
||||
if (timeoutThread != null) timeoutThread.disable();
|
||||
_log.info("getPrefix() + Error trying to connect", ex);
|
||||
l.log(ex.getMessage());
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, wwwProxy);
|
||||
handleHTTPClientException(ex, out, targetRequest, usingWWWProxy, currentProxy);
|
||||
closeSocket(s);
|
||||
}
|
||||
}
|
||||
@ -269,16 +295,18 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
private OutputStream _out;
|
||||
private String _targetRequest;
|
||||
private boolean _useWWWProxy;
|
||||
private String _currentProxy;
|
||||
private boolean _disabled;
|
||||
private Object _disableLock = new Object();
|
||||
|
||||
public InactivityTimeoutThread(I2PTunnelRunner runner, OutputStream out, String targetRequest,
|
||||
boolean useWWWProxy, Socket s) {
|
||||
boolean useWWWProxy, String currentProxy, Socket s) {
|
||||
this.s = s;
|
||||
_runner = runner;
|
||||
_out = out;
|
||||
_targetRequest = targetRequest;
|
||||
_useWWWProxy = useWWWProxy;
|
||||
_currentProxy = currentProxy;
|
||||
_disabled = false;
|
||||
long timeoutId = ++__timeoutId;
|
||||
setName("InactivityThread " + getPrefix() + timeoutId);
|
||||
@ -328,7 +356,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
if (_runner.getLastActivityOn() > 0) {
|
||||
// some data has been sent, so don't 404 it
|
||||
} else {
|
||||
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest, _useWWWProxy, wwwProxy);
|
||||
writeErrorMessage(ERR_TIMEOUT, _out, _targetRequest, _useWWWProxy, _currentProxy);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.warn(getPrefix() + "Error writing out the 'timeout' message", ioe);
|
||||
|
@ -75,7 +75,7 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
|
||||
Properties props = new Properties();
|
||||
props.putAll(getTunnel().getClientOptions());
|
||||
synchronized (slock) {
|
||||
sockMgr = I2PSocketManagerFactory.createManager(privData, I2PTunnel.host, Integer.parseInt(I2PTunnel.port),
|
||||
sockMgr = I2PSocketManagerFactory.createManager(privData, getTunnel().host, Integer.parseInt(getTunnel().port),
|
||||
props);
|
||||
|
||||
}
|
||||
|
337
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
Normal file
337
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
Normal file
@ -0,0 +1,337 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Coordinate the runtime operation and configuration of a tunnel.
|
||||
* These objects are bundled together under a TunnelControllerGroup where the
|
||||
* entire group is stored / loaded from a single config file.
|
||||
*
|
||||
*/
|
||||
public class TunnelController implements Logging {
|
||||
private Log _log;
|
||||
private Properties _config;
|
||||
private I2PTunnel _tunnel;
|
||||
private List _messages;
|
||||
private boolean _running;
|
||||
|
||||
/**
|
||||
* Create a new controller for a tunnel out of the specific config options.
|
||||
* The config may contain a large number of options - only ones that begin in
|
||||
* the prefix should be used (and, in turn, that prefix should be stripped off
|
||||
* before being interpreted by this controller)
|
||||
*
|
||||
* @param config original key=value mapping
|
||||
* @param prefix beginning of key values that are relevent to this tunnel
|
||||
*/
|
||||
public TunnelController(Properties config, String prefix) {
|
||||
this(config, prefix, false);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param createKey for servers, whether we want to create a brand new destination
|
||||
* with private keys at the location specified or not (does not
|
||||
* overwrite existing ones)
|
||||
*/
|
||||
public TunnelController(Properties config, String prefix, boolean createKey) {
|
||||
_tunnel = new I2PTunnel();
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelController.class);
|
||||
setConfig(config, prefix);
|
||||
_messages = new ArrayList(4);
|
||||
_running = false;
|
||||
if (createKey)
|
||||
createPrivateKey();
|
||||
}
|
||||
|
||||
private void createPrivateKey() {
|
||||
I2PClient client = I2PClientFactory.createClient();
|
||||
File keyFile = new File(getPrivKeyFile());
|
||||
if (keyFile.exists()) {
|
||||
log("Not overwriting existing private keys in " + keyFile.getAbsolutePath());
|
||||
return;
|
||||
} else {
|
||||
File parent = keyFile.getParentFile();
|
||||
if ( (parent != null) && (!parent.exists()) )
|
||||
parent.mkdirs();
|
||||
}
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(keyFile);
|
||||
Destination dest = client.createDestination(fos);
|
||||
String destStr = dest.toBase64();
|
||||
log("Private key created and saved in " + keyFile.getAbsolutePath());
|
||||
log("New destination: " + destStr);
|
||||
} catch (I2PException ie) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error creating new destination", ie);
|
||||
log("Error creating new destination: " + ie.getMessage());
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error creating writing the destination to " + keyFile.getAbsolutePath(), ioe);
|
||||
log("Error writing the keys to " + keyFile.getAbsolutePath());
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start up the tunnel (if it isn't already running)
|
||||
*
|
||||
*/
|
||||
public void startTunnel() {
|
||||
if (_running) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Already running");
|
||||
log("Tunnel " + getName() + " is already running");
|
||||
return;
|
||||
}
|
||||
String type = getType();
|
||||
if ( (type == null) || (type.length() <= 0) ) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Cannot start the tunnel - no type specified");
|
||||
return;
|
||||
}
|
||||
if ("httpclient".equals(type)) {
|
||||
startHttpClient();
|
||||
} else if ("client".equals(type)) {
|
||||
startClient();
|
||||
} else if ("server".equals(type)) {
|
||||
startServer();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.error("Cannot start tunnel - unknown type [" + type + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private void startHttpClient() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
setListenOn();
|
||||
String listenPort = getListenPort();
|
||||
String proxyList = getProxyList();
|
||||
if (proxyList == null)
|
||||
_tunnel.runHttpClient(new String[] { listenPort }, this);
|
||||
else
|
||||
_tunnel.runHttpClient(new String[] { listenPort, proxyList }, this);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void startClient() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
setListenOn();
|
||||
String listenPort = getListenPort();
|
||||
String dest = getTargetDestination();
|
||||
_tunnel.runClient(new String[] { listenPort, dest }, this);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void startServer() {
|
||||
setI2CPOptions();
|
||||
setSessionOptions();
|
||||
String targetHost = getTargetHost();
|
||||
String targetPort = getTargetPort();
|
||||
String privKeyFile = getPrivKeyFile();
|
||||
_tunnel.runServer(new String[] { targetHost, targetPort, privKeyFile }, this);
|
||||
_running = true;
|
||||
}
|
||||
|
||||
private void setListenOn() {
|
||||
String listenOn = getListenOnInterface();
|
||||
if ( (listenOn != null) && (listenOn.length() > 0) ) {
|
||||
_tunnel.runListenOn(new String[] { listenOn }, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSessionOptions() {
|
||||
List opts = new ArrayList();
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
if (key.startsWith("option.")) {
|
||||
key = key.substring("option.".length());
|
||||
opts.add(key + "=" + val);
|
||||
}
|
||||
}
|
||||
String args[] = new String[opts.size()];
|
||||
for (int i = 0; i < opts.size(); i++)
|
||||
args[i] = (String)opts.get(i);
|
||||
_tunnel.runClientOptions(args, this);
|
||||
}
|
||||
|
||||
private void setI2CPOptions() {
|
||||
String host = getI2CPHost();
|
||||
if ( (host != null) && (host.length() > 0) )
|
||||
_tunnel.host = host;
|
||||
String port = getI2CPPort();
|
||||
if ( (port != null) && (port.length() > 0) )
|
||||
_tunnel.port = port;
|
||||
}
|
||||
|
||||
public void stopTunnel() {
|
||||
_tunnel.runClose(new String[] { "forced", "all" }, this);
|
||||
_running = false;
|
||||
}
|
||||
|
||||
public void restartTunnel() {
|
||||
stopTunnel();
|
||||
startTunnel();
|
||||
}
|
||||
|
||||
public void setConfig(Properties config, String prefix) {
|
||||
Properties props = new Properties();
|
||||
for (Iterator iter = config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = config.getProperty(key);
|
||||
if (key.startsWith(prefix)) {
|
||||
key = key.substring(prefix.length());
|
||||
props.setProperty(key, val);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Set prop [" + key + "] to [" + val + "]");
|
||||
}
|
||||
}
|
||||
_config = props;
|
||||
}
|
||||
public Properties getConfig(String prefix) {
|
||||
Properties rv = new Properties();
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
rv.setProperty(prefix + key, val);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
public String getType() { return _config.getProperty("type"); }
|
||||
public String getName() { return _config.getProperty("name"); }
|
||||
public String getDescription() { return _config.getProperty("description"); }
|
||||
public String getI2CPHost() { return _config.getProperty("i2cpHost"); }
|
||||
public String getI2CPPort() { return _config.getProperty("i2cpPort"); }
|
||||
public String getClientOptions() {
|
||||
StringBuffer opts = new StringBuffer(64);
|
||||
for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = _config.getProperty(key);
|
||||
if (key.startsWith("option.")) {
|
||||
key = key.substring("option.".length());
|
||||
if (opts.length() > 0) opts.append(' ');
|
||||
opts.append(key).append('=').append(val);
|
||||
}
|
||||
}
|
||||
return opts.toString();
|
||||
}
|
||||
public String getListenOnInterface() { return _config.getProperty("interface"); }
|
||||
public String getTargetHost() { return _config.getProperty("targetHost"); }
|
||||
public String getTargetPort() { return _config.getProperty("targetPort"); }
|
||||
public String getPrivKeyFile() { return _config.getProperty("privKeyFile"); }
|
||||
public String getListenPort() { return _config.getProperty("listenPort"); }
|
||||
public String getTargetDestination() { return _config.getProperty("targetDestination"); }
|
||||
public String getProxyList() { return _config.getProperty("proxyList"); }
|
||||
|
||||
public boolean getIsRunning() { return _running; }
|
||||
|
||||
public void getSummary(StringBuffer buf) {
|
||||
String type = getType();
|
||||
if ("httpclient".equals(type))
|
||||
getHttpClientSummary(buf);
|
||||
else if ("client".equals(type))
|
||||
getClientSummary(buf);
|
||||
else if ("server".equals(type))
|
||||
getServerSummary(buf);
|
||||
else
|
||||
buf.append("Unknown type ").append(type);
|
||||
}
|
||||
|
||||
private void getHttpClientSummary(StringBuffer buf) {
|
||||
String description = getDescription();
|
||||
if ( (description != null) && (description.trim().length() > 0) )
|
||||
buf.append("<i>").append(description).append("</i><br />\n");
|
||||
buf.append("HTTP proxy listening on port ").append(getListenPort());
|
||||
String listenOn = getListenOnInterface();
|
||||
if ("0.0.0.0".equals(listenOn))
|
||||
buf.append(" (reachable by any machine)");
|
||||
else if ("127.0.0.1".equals(listenOn))
|
||||
buf.append(" (reachable locally only)");
|
||||
else
|
||||
buf.append(" (reachable at the ").append(listenOn).append(" interface)");
|
||||
buf.append("<br />\n");
|
||||
String proxies = getProxyList();
|
||||
if ( (proxies == null) || (proxies.trim().length() <= 0) )
|
||||
buf.append("Outproxy: default [squid.i2p]<br />\n");
|
||||
else
|
||||
buf.append("Outproxy: ").append(proxies).append("<br />\n");
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getClientSummary(StringBuffer buf) {
|
||||
String description = getDescription();
|
||||
if ( (description != null) && (description.trim().length() > 0) )
|
||||
buf.append("<i>").append(description).append("</i><br />\n");
|
||||
buf.append("Client tunnel listening on port ").append(getListenPort());
|
||||
buf.append(" pointing at ").append(getTargetDestination());
|
||||
String listenOn = getListenOnInterface();
|
||||
if ("0.0.0.0".equals(listenOn))
|
||||
buf.append(" (reachable by any machine)");
|
||||
else if ("127.0.0.1".equals(listenOn))
|
||||
buf.append(" (reachable locally only)");
|
||||
else
|
||||
buf.append(" (reachable at the ").append(listenOn).append(" interface)");
|
||||
buf.append("<br />\n");
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getServerSummary(StringBuffer buf) {
|
||||
String description = getDescription();
|
||||
if ( (description != null) && (description.trim().length() > 0) )
|
||||
buf.append("<i>").append(description).append("</i><br />\n");
|
||||
buf.append("Server tunnel pointing at port ").append(getTargetPort());
|
||||
buf.append(" on ").append(getTargetHost());
|
||||
buf.append("<br />\n");
|
||||
buf.append("Private destination loaded from ").append(getPrivKeyFile()).append("<br />\n");
|
||||
getOptionSummary(buf);
|
||||
}
|
||||
|
||||
private void getOptionSummary(StringBuffer buf) {
|
||||
String opts = getClientOptions();
|
||||
if ( (opts != null) && (opts.length() > 0) )
|
||||
buf.append("Network options: ").append(opts).append("<br />\n");
|
||||
}
|
||||
|
||||
public void log(String s) {
|
||||
synchronized (this) {
|
||||
_messages.add(s);
|
||||
while (_messages.size() > 10)
|
||||
_messages.remove(0);
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull off any messages that the I2PTunnel has produced
|
||||
*
|
||||
* @return list of messages pulled off (each is a String, earliest first)
|
||||
*/
|
||||
public List clearMessages() {
|
||||
List rv = null;
|
||||
synchronized (this) {
|
||||
rv = new ArrayList(_messages);
|
||||
_messages.clear();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
@ -0,0 +1,249 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Coordinate a set of tunnels within the JVM, loading and storing their config
|
||||
* to disk, and building new ones as requested.
|
||||
*
|
||||
*/
|
||||
public class TunnelControllerGroup {
|
||||
private Log _log;
|
||||
private List _controllers;
|
||||
private static TunnelControllerGroup _instance = new TunnelControllerGroup();
|
||||
public static TunnelControllerGroup getInstance() { return _instance; }
|
||||
|
||||
private TunnelControllerGroup() {
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelControllerGroup.class);
|
||||
_controllers = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load up all of the tunnels configured in the given file (but do not start
|
||||
* them)
|
||||
*
|
||||
*/
|
||||
public void loadControllers(String configFile) {
|
||||
Properties cfg = loadConfig(configFile);
|
||||
if (cfg == null) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unable to load the config from " + configFile);
|
||||
return;
|
||||
}
|
||||
int i = 0;
|
||||
while (true) {
|
||||
String type = cfg.getProperty("tunnel." + i + ".type");
|
||||
if (type == null)
|
||||
break;
|
||||
TunnelController controller = new TunnelController(cfg, "tunnel." + i + ".");
|
||||
_controllers.add(controller);
|
||||
i++;
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(i + " controllers loaded from " + configFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop and remove reference to all known tunnels (but dont delete any config
|
||||
* file or do other silly things)
|
||||
*
|
||||
*/
|
||||
public void unloadControllers() {
|
||||
stopAllControllers();
|
||||
_controllers.clear();
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("All controllers stopped and unloaded");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given tunnel to the set of known controllers (but dont add it to
|
||||
* a config file or start it or anything)
|
||||
*
|
||||
*/
|
||||
public void addController(TunnelController controller) { _controllers.add(controller); }
|
||||
|
||||
/**
|
||||
* Stop and remove the given tunnel
|
||||
*
|
||||
* @return list of messages from the controller as it is stopped
|
||||
*/
|
||||
public List removeController(TunnelController controller) {
|
||||
if (controller == null) return new ArrayList();
|
||||
controller.stopTunnel();
|
||||
List msgs = controller.clearMessages();
|
||||
_controllers.remove(controller);
|
||||
msgs.add("Tunnel " + controller.getName() + " removed");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all tunnels
|
||||
*
|
||||
* @return list of messages the tunnels generate when stopped
|
||||
*/
|
||||
public List stopAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
controller.stopTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers stopped");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start all tunnels
|
||||
*
|
||||
* @return list of messages the tunnels generate when started
|
||||
*/
|
||||
public List startAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
controller.startTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers started");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart all tunnels
|
||||
*
|
||||
* @return list of messages the tunnels generate when restarted
|
||||
*/
|
||||
public List restartAllControllers() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
controller.restartTunnel();
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info(_controllers.size() + " controllers restarted");
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all outstanding messages from any of the known tunnels
|
||||
*
|
||||
* @return list of messages the tunnels have generated
|
||||
*/
|
||||
public List clearAllMessages() {
|
||||
List msgs = new ArrayList();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
msgs.addAll(controller.clearMessages());
|
||||
}
|
||||
return msgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the configuration of all known tunnels to the given file
|
||||
*
|
||||
*/
|
||||
public void saveConfig(String configFile) {
|
||||
File cfgFile = new File(configFile);
|
||||
File parent = cfgFile.getParentFile();
|
||||
if ( (parent != null) && (!parent.exists()) )
|
||||
parent.mkdirs();
|
||||
|
||||
|
||||
TreeMap map = new TreeMap();
|
||||
for (int i = 0; i < _controllers.size(); i++) {
|
||||
TunnelController controller = (TunnelController)_controllers.get(i);
|
||||
Properties cur = controller.getConfig("tunnel." + i + ".");
|
||||
map.putAll(cur);
|
||||
}
|
||||
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
for (Iterator iter = map.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = (String)map.get(key);
|
||||
buf.append(key).append('=').append(val).append('\n');
|
||||
}
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(cfgFile);
|
||||
fos.write(buf.toString().getBytes());
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Config written to " + cfgFile.getPath());
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing out the config");
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load up the config data from the file
|
||||
*
|
||||
* @return properties loaded or null if there was an error
|
||||
*/
|
||||
private Properties loadConfig(String configFile) {
|
||||
File cfgFile = new File(configFile);
|
||||
if (!cfgFile.exists()) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Unable to load the controllers from " + configFile);
|
||||
return null;
|
||||
}
|
||||
|
||||
Properties props = new Properties();
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(cfgFile);
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
|
||||
String line = null;
|
||||
while ( (line = in.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.length() <= 0) continue;
|
||||
if (line.startsWith("#") || line.startsWith(";"))
|
||||
continue;
|
||||
int eq = line.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= line.length() - 1) )
|
||||
continue;
|
||||
String key = line.substring(0, eq);
|
||||
String val = line.substring(eq+1);
|
||||
props.setProperty(key, val);
|
||||
}
|
||||
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Props loaded with " + props.size() + " lines");
|
||||
return props;
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Error reading the controllers from " + configFile, ioe);
|
||||
return null;
|
||||
} finally {
|
||||
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of tunnels known
|
||||
*
|
||||
* @return list of TunnelController objects
|
||||
*/
|
||||
public List getControllers() { return _controllers; }
|
||||
}
|
@ -0,0 +1,307 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Uuuugly... generate the edit/add forms for the various
|
||||
* I2PTunnel types (httpclient/client/server)
|
||||
*
|
||||
*/
|
||||
class WebEditPageFormGenerator {
|
||||
private static final String SELECT_TYPE_FORM =
|
||||
"<form action=\"edit.jsp\"> Type of tunnel: <select name=\"type\">" +
|
||||
"<option value=\"httpclient\">HTTP proxy</option>" +
|
||||
"<option value=\"client\">Client tunnel</option>" +
|
||||
"<option value=\"server\">Server tunnel</option>" +
|
||||
"</select> <input type=\"submit\" value=\"GO\" />" +
|
||||
"</form>\n";
|
||||
|
||||
/**
|
||||
* Retrieve the form requested
|
||||
*
|
||||
*/
|
||||
public static String getForm(WebEditPageHelper helper) {
|
||||
TunnelController controller = helper.getTunnelController();
|
||||
|
||||
if ( (helper.getType() == null) && (controller == null) )
|
||||
return SELECT_TYPE_FORM;
|
||||
|
||||
String id = helper.getNum();
|
||||
String type = helper.getType();
|
||||
if (controller != null)
|
||||
type = controller.getType();
|
||||
|
||||
if ("httpclient".equals(type))
|
||||
return getEditHttpClientForm(controller, id);
|
||||
else if ("client".equals(type))
|
||||
return getEditClientForm(controller, id);
|
||||
else if ("server".equals(type))
|
||||
return getEditServerForm(controller, id);
|
||||
else
|
||||
return "WTF, unknown type [" + type + "]";
|
||||
}
|
||||
|
||||
private static String getEditHttpClientForm(TunnelController controller, String id) {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
addGeneral(buf, controller, id);
|
||||
buf.append("<b>Type:</b> <i>HTTP proxy</i><input type=\"hidden\" name=\"type\" value=\"httpclient\" /><br />\n");
|
||||
|
||||
addListeningOn(buf, controller, 4444);
|
||||
|
||||
buf.append("<b>Outproxies:</b> <input type=\"text\" name=\"proxyList\" size=\"20\" ");
|
||||
if ( (controller != null) && (controller.getProxyList() != null) )
|
||||
buf.append("value=\"").append(controller.getProxyList()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"squid.i2p\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
|
||||
buf.append("</form>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String getEditClientForm(TunnelController controller, String id) {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
addGeneral(buf, controller, id);
|
||||
buf.append("<b>Type:</b> <i>Client tunnel</i><input type=\"hidden\" name=\"type\" value=\"client\" /><br />\n");
|
||||
|
||||
addListeningOn(buf, controller, 2025 + new Random().nextInt(1000)); // 2025 since nextInt can be negative
|
||||
|
||||
buf.append("<b>Target:</b> <input type=\"text\" size=\"40\" name=\"targetDestination\" ");
|
||||
if ( (controller != null) && (controller.getTargetDestination() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetDestination()).append("\" ");
|
||||
buf.append(" /> (either the hosts.txt name or the full base64 destination)<br />\n");
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\"><br />\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
|
||||
buf.append("</form>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String getEditServerForm(TunnelController controller, String id) {
|
||||
StringBuffer buf = new StringBuffer(1024);
|
||||
addGeneral(buf, controller, id);
|
||||
buf.append("<b>Type:</b> <i>Server tunnel</i><input type=\"hidden\" name=\"type\" value=\"server\" /><br />\n");
|
||||
|
||||
buf.append("<b>Target host:</b> <input type=\"text\" size=\"40\" name=\"targetHost\" ");
|
||||
if ( (controller != null) && (controller.getTargetHost() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetHost()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"localhost\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Target port:</b> <input type=\"text\" size=\"4\" name=\"targetPort\" ");
|
||||
if ( (controller != null) && (controller.getTargetPort() != null) )
|
||||
buf.append("value=\"").append(controller.getTargetPort()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"80\" ");
|
||||
buf.append(" /><br />\n");
|
||||
|
||||
buf.append("<b>Private key file:</b> <input type=\"text\" name=\"privKeyFile\" value=\"");
|
||||
if ( (controller != null) && (controller.getPrivKeyFile() != null) ) {
|
||||
buf.append(controller.getPrivKeyFile()).append("\" /><br />");
|
||||
} else {
|
||||
buf.append("myServer.privKey\" /><br />");
|
||||
buf.append("<input type=\"hidden\" name=\"privKeyGenerate\" value=\"true\" />");
|
||||
}
|
||||
|
||||
addOptions(buf, controller);
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Save\">\n");
|
||||
buf.append("<input type=\"submit\" name=\"action\" value=\"Remove\">\n");
|
||||
buf.append(" <i>confirm</i> <input type=\"checkbox\" name=\"removeConfirm\" value=\"true\" />\n");
|
||||
buf.append("</form>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start off the form and add some common fields (name, num, description)
|
||||
*
|
||||
* @param buf where to shove the form
|
||||
* @param controller tunnel in question, or null if we're creating a new tunnel
|
||||
* @param id index into the current list of tunnelControllerGroup.getControllers() list
|
||||
* (or null if we are generating an 'add' form)
|
||||
*/
|
||||
private static void addGeneral(StringBuffer buf, TunnelController controller, String id) {
|
||||
buf.append("<form action=\"edit.jsp\">");
|
||||
if (id != null)
|
||||
buf.append("<input type=\"hidden\" name=\"num\" value=\"").append(id).append("\" />");
|
||||
|
||||
buf.append("<b>Name:</b> <input type=\"text\" name=\"name\" size=\"20\" ");
|
||||
if ( (controller != null) && (controller.getName() != null) )
|
||||
buf.append("value=\"").append(controller.getName()).append("\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
buf.append("<b>Description:</b> <input type=\"text\" name=\"description\" size=\"60\" ");
|
||||
if ( (controller != null) && (controller.getDescription() != null) )
|
||||
buf.append("value=\"").append(controller.getDescription()).append("\" ");
|
||||
buf.append("/><br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the fields asking for what port and interface the tunnel should
|
||||
* listen on.
|
||||
*
|
||||
* @param buf where to shove the form
|
||||
* @param controller tunnel in question, or null if we're creating a new tunnel
|
||||
* @param defaultPort if we are creating a new tunnel, default the form to the given port
|
||||
*/
|
||||
private static void addListeningOn(StringBuffer buf, TunnelController controller, int defaultPort) {
|
||||
buf.append("<b>Listening on port:</b> <input type=\"text\" name=\"port\" size=\"20\" ");
|
||||
if ( (controller != null) && (controller.getListenPort() != null) )
|
||||
buf.append("value=\"").append(controller.getListenPort()).append("\" ");
|
||||
else
|
||||
buf.append("value=\"").append(defaultPort).append("\" ");
|
||||
buf.append("/><br />\n");
|
||||
|
||||
String selectedOn = null;
|
||||
if ( (controller != null) && (controller.getListenOnInterface() != null) )
|
||||
selectedOn = controller.getListenOnInterface();
|
||||
|
||||
buf.append("<b>Reachable by:</b> ");
|
||||
buf.append("<select name=\"reachableBy\">");
|
||||
buf.append("<option value=\"127.0.0.1\" ");
|
||||
if ( (selectedOn != null) && ("127.0.0.1".equals(selectedOn)) )
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Locally (127.0.0.1)</option>\n");
|
||||
buf.append("<option value=\"0.0.0.0\" ");
|
||||
if ( (selectedOn != null) && ("0.0.0.0".equals(selectedOn)) )
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Everyone (0.0.0.0)</option>\n");
|
||||
buf.append("</select> ");
|
||||
buf.append("Other: <input type=\"text\" name=\"reachableByOther\" value=\"");
|
||||
if ( (selectedOn != null) && (!"127.0.0.1".equals(selectedOn)) && (!"0.0.0.0".equals(selectedOn)) )
|
||||
buf.append(selectedOn);
|
||||
buf.append("\"><br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add fields for customizing the I2PSession options, including helpers for
|
||||
* tunnel depth and count, as well as I2CP host and port.
|
||||
*
|
||||
* @param buf where to shove the form
|
||||
* @param controller tunnel in question, or null if we're creating a new tunnel
|
||||
*/
|
||||
private static void addOptions(StringBuffer buf, TunnelController controller) {
|
||||
int tunnelDepth = 2;
|
||||
int numTunnels = 2;
|
||||
Properties opts = getOptions(controller);
|
||||
if (opts != null) {
|
||||
String depth = opts.getProperty("tunnels.depthInbound");
|
||||
if (depth != null) {
|
||||
try {
|
||||
tunnelDepth = Integer.parseInt(depth);
|
||||
} catch (NumberFormatException nfe) {
|
||||
tunnelDepth = 2;
|
||||
}
|
||||
}
|
||||
String num = opts.getProperty("tunnels.numInbound");
|
||||
if (num != null) {
|
||||
try {
|
||||
numTunnels = Integer.parseInt(num);
|
||||
} catch (NumberFormatException nfe) {
|
||||
numTunnels = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.append("<b>Tunnel depth:</b> ");
|
||||
buf.append("<select name=\"tunnelDepth\">");
|
||||
buf.append("<option value=\"0\" ");
|
||||
if (tunnelDepth == 0) buf.append(" selected=\"true\" ");
|
||||
buf.append(">0 hop tunnel (low anonymity, low latency)</option>");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (tunnelDepth == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 hop tunnel (medium anonymity, medium latency)</option>");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (tunnelDepth == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 hop tunnel (high anonymity, high latency)</option>");
|
||||
if (tunnelDepth > 2) {
|
||||
buf.append("<option value=\"").append(tunnelDepth).append("\" selected=\"true\" >");
|
||||
buf.append(tunnelDepth);
|
||||
buf.append(" hop tunnel (custom)</option>");
|
||||
}
|
||||
buf.append("</select><br />\n");
|
||||
|
||||
buf.append("<b>Tunnel count:</b> ");
|
||||
buf.append("<select name=\"tunnelCount\">");
|
||||
buf.append("<option value=\"1\" ");
|
||||
if (numTunnels == 1) buf.append(" selected=\"true\" ");
|
||||
buf.append(">1 inbound tunnel (low bandwidth usage, less reliability)</option>");
|
||||
buf.append("<option value=\"2\" ");
|
||||
if (numTunnels == 2) buf.append(" selected=\"true\" ");
|
||||
buf.append(">2 inbound tunnels (standard bandwidth usage, standard reliability)</option>");
|
||||
buf.append("<option value=\"3\" ");
|
||||
if (numTunnels == 3) buf.append(" selected=\"true\" ");
|
||||
buf.append(">3 inbound tunnels (higher bandwidth usage, higher reliability)</option>");
|
||||
|
||||
if (numTunnels > 3) {
|
||||
buf.append("<option value=\"").append(numTunnels).append("\" selected=\"true\" >");
|
||||
buf.append(numTunnels);
|
||||
buf.append(" inbound tunnels (custom)</option>");
|
||||
}
|
||||
buf.append("</select><br />\n");
|
||||
|
||||
buf.append("<b>I2CP host:</b> ");
|
||||
buf.append("<input type=\"text\" name=\"clientHost\" size=\"20\" value=\"");
|
||||
if ( (controller != null) && (controller.getI2CPHost() != null) )
|
||||
buf.append(controller.getI2CPHost());
|
||||
else
|
||||
buf.append("localhost");
|
||||
buf.append("\" /><br />\n");
|
||||
buf.append("<b>I2CP port:</b> ");
|
||||
buf.append("<input type=\"text\" name=\"clientPort\" size=\"20\" value=\"");
|
||||
if ( (controller != null) && (controller.getI2CPPort() != null) )
|
||||
buf.append(controller.getI2CPPort());
|
||||
else
|
||||
buf.append("7654");
|
||||
buf.append("\" /><br />\n");
|
||||
|
||||
buf.append("<b>Other custom options:</b> \n");
|
||||
buf.append("<input type=\"text\" name=\"customOptions\" size=\"60\" value=\"");
|
||||
if (opts != null) {
|
||||
int i = 0;
|
||||
for (Iterator iter = opts.keySet().iterator(); iter.hasNext(); ) {
|
||||
String key = (String)iter.next();
|
||||
String val = opts.getProperty(key);
|
||||
if ("tunnels.depthInbound".equals(key)) continue;
|
||||
if ("tunnels.numInbound".equals(key)) continue;
|
||||
if (i != 0) buf.append(' ');
|
||||
buf.append(key).append('=').append(val);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
buf.append("\" /><br />\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the client options from the tunnel
|
||||
*
|
||||
* @return map of name=val to be used as I2P session options
|
||||
*/
|
||||
private static Properties getOptions(TunnelController controller) {
|
||||
if (controller == null) return null;
|
||||
String opts = controller.getClientOptions();
|
||||
StringTokenizer tok = new StringTokenizer(opts);
|
||||
Properties props = new Properties();
|
||||
while (tok.hasMoreTokens()) {
|
||||
String pair = tok.nextToken();
|
||||
int eq = pair.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= pair.length()) )
|
||||
continue;
|
||||
String key = pair.substring(0, eq);
|
||||
String val = pair.substring(eq+1);
|
||||
props.setProperty(key, val);
|
||||
}
|
||||
return props;
|
||||
}
|
||||
}
|
348
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageHelper.java
Normal file
348
apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebEditPageHelper.java
Normal file
@ -0,0 +1,348 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* UUUUuuuuuugly glue code to handle bean interaction from the web, process
|
||||
* that data, and spit out the results (or the form requested). The basic
|
||||
* usage is to set any of the fields with data then query the bean via
|
||||
* getActionResults() which triggers the request processing (taking all the
|
||||
* provided data, doing what needs to be done) and returns the results of those
|
||||
* activites. Then a subsequent call to getEditForm() generates the HTML form
|
||||
* to either edit the currently selected tunnel (if specified) or add a new one.
|
||||
* This functionality is delegated to the WebEditPageFormGenerator.
|
||||
*
|
||||
*/
|
||||
public class WebEditPageHelper {
|
||||
private Log _log;
|
||||
private String _action;
|
||||
private String _type;
|
||||
private String _id;
|
||||
private String _name;
|
||||
private String _description;
|
||||
private String _i2cpHost;
|
||||
private String _i2cpPort;
|
||||
private String _tunnelDepth;
|
||||
private String _tunnelCount;
|
||||
private String _customOptions;
|
||||
private String _proxyList;
|
||||
private String _port;
|
||||
private String _reachableBy;
|
||||
private String _reachableByOther;
|
||||
private String _targetDestination;
|
||||
private String _targetHost;
|
||||
private String _targetPort;
|
||||
private String _privKeyFile;
|
||||
private boolean _privKeyGenerate;
|
||||
private boolean _removeConfirmed;
|
||||
|
||||
public WebEditPageHelper() {
|
||||
_action = null;
|
||||
_type = null;
|
||||
_id = null;
|
||||
_removeConfirmed = false;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(WebEditPageHelper.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for form submit - either "Save" or Remove"
|
||||
*/
|
||||
public void setAction(String action) {
|
||||
_action = (action != null ? action.trim() : null);
|
||||
}
|
||||
/**
|
||||
* What type of tunnel (httpclient, client, or server). This is
|
||||
* required when adding a new tunnel.
|
||||
*
|
||||
*/
|
||||
public void setType(String type) {
|
||||
_type = (type != null ? type.trim() : null);
|
||||
}
|
||||
/**
|
||||
* Which particular tunnel should be edited (index into the current
|
||||
* TunnelControllerGroup's getControllers() list). This is required
|
||||
* when editing a tunnel, but not when adding a new one.
|
||||
*
|
||||
*/
|
||||
public void setNum(String id) {
|
||||
_id = (id != null ? id.trim() : null);
|
||||
}
|
||||
String getType() { return _type; }
|
||||
String getNum() { return _id; }
|
||||
|
||||
/** Short name of the tunnel */
|
||||
public void setName(String name) {
|
||||
_name = (name != null ? name.trim() : null);
|
||||
}
|
||||
/** one line description */
|
||||
public void setDescription(String description) {
|
||||
_description = (description != null ? description.trim() : null);
|
||||
}
|
||||
/** I2CP host the router is on */
|
||||
public void setClientHost(String host) {
|
||||
_i2cpHost = (host != null ? host.trim() : null);
|
||||
}
|
||||
/** I2CP port the router is on */
|
||||
public void setClientPort(String port) {
|
||||
_i2cpPort = (port != null ? port.trim() : null);
|
||||
}
|
||||
/** how many hops to use for inbound tunnels */
|
||||
public void setTunnelDepth(String tunnelDepth) {
|
||||
_tunnelDepth = (tunnelDepth != null ? tunnelDepth.trim() : null);
|
||||
}
|
||||
/** how many parallel inbound tunnels to use */
|
||||
public void setTunnelCount(String tunnelCount) {
|
||||
_tunnelCount = (tunnelCount != null ? tunnelCount.trim() : null);
|
||||
}
|
||||
/** what I2P session overrides should be used */
|
||||
public void setCustomOptions(String customOptions) {
|
||||
_customOptions = (customOptions != null ? customOptions.trim() : null);
|
||||
}
|
||||
/** what HTTP outproxies should be used (httpclient specific) */
|
||||
public void setProxyList(String proxyList) {
|
||||
_proxyList = (proxyList != null ? proxyList.trim() : null);
|
||||
}
|
||||
/** what port should this client/httpclient listen on */
|
||||
public void setPort(String port) {
|
||||
_port = (port != null ? port.trim() : null);
|
||||
}
|
||||
/**
|
||||
* what interface should this client/httpclient listen on (unless
|
||||
* overridden by the setReachableByOther() field)
|
||||
*/
|
||||
public void setReachableBy(String reachableBy) {
|
||||
_reachableBy = (reachableBy != null ? reachableBy.trim() : null);
|
||||
}
|
||||
/**
|
||||
* If specified, defines the exact IP interface to listen for requests
|
||||
* on (in the case of client/httpclient tunnels)
|
||||
*/
|
||||
public void setReachableByOther(String reachableByOther) {
|
||||
_reachableByOther = (reachableByOther != null ? reachableByOther.trim() : null);
|
||||
}
|
||||
/** What peer does this client tunnel point at */
|
||||
public void setTargetDestination(String dest) {
|
||||
_targetDestination = (dest != null ? dest.trim() : null);
|
||||
}
|
||||
/** What host does this server tunnel point at */
|
||||
public void setTargetHost(String host) {
|
||||
_targetHost = (host != null ? host.trim() : null);
|
||||
}
|
||||
/** What port does this server tunnel point at */
|
||||
public void setTargetPort(String port) {
|
||||
_targetPort = (port != null ? port.trim() : null);
|
||||
}
|
||||
/** What filename is this server tunnel's private keys stored in */
|
||||
public void setPrivKeyFile(String file) {
|
||||
_privKeyFile = (file != null ? file.trim() : null);
|
||||
}
|
||||
/**
|
||||
* If called with any value, we want to generate a new destination
|
||||
* for this server tunnel. This won't cause any existing private keys
|
||||
* to be overwritten, however.
|
||||
*/
|
||||
public void setPrivKeyGenerate(String moo) {
|
||||
_privKeyGenerate = true;
|
||||
}
|
||||
/**
|
||||
* If called with any value (and the form submitted with action=Remove),
|
||||
* we really do want to stop and remove the tunnel.
|
||||
*/
|
||||
public void setRemoveConfirm(String moo) {
|
||||
_removeConfirmed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the form and display any resulting messages
|
||||
*
|
||||
*/
|
||||
public String getActionResults() {
|
||||
try {
|
||||
return processAction();
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Internal error processing request", t);
|
||||
return "Internal error - " + t.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an HTML form to edit / create a tunnel according to the
|
||||
* specified fields
|
||||
*/
|
||||
public String getEditForm() {
|
||||
try {
|
||||
return WebEditPageFormGenerator.getForm(this);
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Internal error retrieving edit form", t);
|
||||
return "Internal error - " + t.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the tunnel pointed to by the current id
|
||||
*
|
||||
*/
|
||||
TunnelController getTunnelController() {
|
||||
if (_id == null) return null;
|
||||
int id = -1;
|
||||
try {
|
||||
id = Integer.parseInt(_id);
|
||||
List controllers = TunnelControllerGroup.getInstance().getControllers();
|
||||
if ( (id < 0) || (id >= controllers.size()) )
|
||||
return null;
|
||||
else
|
||||
return (TunnelController)controllers.get(id);
|
||||
} catch (NumberFormatException nfe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Invalid tunnel id [" + _id + "]", nfe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) )
|
||||
return "";
|
||||
if ("Save".equals(_action))
|
||||
return save();
|
||||
else if ("Remove".equals(_action))
|
||||
return remove();
|
||||
else
|
||||
return "Action <i>" + _action + "</i> unknown";
|
||||
}
|
||||
|
||||
private String remove() {
|
||||
if (!_removeConfirmed)
|
||||
return "Please confirm removal";
|
||||
|
||||
TunnelController cur = getTunnelController();
|
||||
if (cur == null)
|
||||
return "Invalid tunnel number";
|
||||
|
||||
List msgs = TunnelControllerGroup.getInstance().removeController(cur);
|
||||
msgs.addAll(doSave());
|
||||
return getMessages(msgs);
|
||||
}
|
||||
|
||||
private String save() {
|
||||
if (_type == null)
|
||||
return "<b>Invalid form submission (no type?)</b>";
|
||||
Properties config = getConfig();
|
||||
if (config == null)
|
||||
return "<b>Invalid params</b>";
|
||||
|
||||
TunnelController cur = getTunnelController();
|
||||
if (cur == null) {
|
||||
// creating new
|
||||
cur = new TunnelController(config, "", _privKeyGenerate);
|
||||
TunnelControllerGroup.getInstance().addController(cur);
|
||||
} else {
|
||||
cur.setConfig(config, "");
|
||||
}
|
||||
|
||||
|
||||
return getMessages(doSave());
|
||||
}
|
||||
private List doSave() {
|
||||
TunnelControllerGroup.getInstance().saveConfig(WebStatusPageHelper.CONFIG_FILE);
|
||||
return TunnelControllerGroup.getInstance().clearAllMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on all provided data, create a set of configuration parameters
|
||||
* suitable for use in a TunnelController. This will replace (not add to)
|
||||
* any existing parameters, so this should return a comprehensive mapping.
|
||||
*
|
||||
*/
|
||||
private Properties getConfig() {
|
||||
Properties config = new Properties();
|
||||
updateConfigGeneric(config);
|
||||
|
||||
if ("httpclient".equals(_type)) {
|
||||
if (_port != null)
|
||||
config.setProperty("listenPort", _port);
|
||||
if (_reachableByOther != null)
|
||||
config.setProperty("interface", _reachableByOther);
|
||||
else
|
||||
config.setProperty("interface", _reachableBy);
|
||||
if (_proxyList != null)
|
||||
config.setProperty("proxyList", _proxyList);
|
||||
} else if ("client".equals(_type)) {
|
||||
if (_port != null)
|
||||
config.setProperty("listenPort", _port);
|
||||
if (_reachableByOther != null)
|
||||
config.setProperty("interface", _reachableByOther);
|
||||
else
|
||||
config.setProperty("interface", _reachableBy);
|
||||
if (_targetDestination != null)
|
||||
config.setProperty("targetDestination", _targetDestination);
|
||||
} else if ("server".equals(_type)) {
|
||||
if (_targetHost != null)
|
||||
config.setProperty("targetHost", _targetHost);
|
||||
if (_targetPort != null)
|
||||
config.setProperty("targetPort", _targetPort);
|
||||
if (_privKeyFile != null)
|
||||
config.setProperty("privKeyFile", _privKeyFile);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private void updateConfigGeneric(Properties config) {
|
||||
config.setProperty("type", _type);
|
||||
if (_name != null)
|
||||
config.setProperty("name", _name);
|
||||
if (_description != null)
|
||||
config.setProperty("description", _description);
|
||||
if (_i2cpHost != null)
|
||||
config.setProperty("i2cpHost", _i2cpHost);
|
||||
if (_i2cpPort != null)
|
||||
config.setProperty("i2cpPort", _i2cpPort);
|
||||
|
||||
if (_customOptions != null) {
|
||||
StringTokenizer tok = new StringTokenizer(_customOptions);
|
||||
while (tok.hasMoreTokens()) {
|
||||
String pair = tok.nextToken();
|
||||
int eq = pair.indexOf('=');
|
||||
if ( (eq <= 0) || (eq >= pair.length()) )
|
||||
continue;
|
||||
String key = pair.substring(0, eq);
|
||||
String val = pair.substring(eq+1);
|
||||
if ("tunnels.numInbound".equals(key)) continue;
|
||||
if ("tunnels.depthInbound".equals(key)) continue;
|
||||
config.setProperty("option." + key, val);
|
||||
}
|
||||
}
|
||||
|
||||
if (_tunnelCount != null)
|
||||
config.setProperty("option.tunnels.numInbound", _tunnelCount);
|
||||
if (_tunnelDepth != null)
|
||||
config.setProperty("option.tunnels.depthInbound", _tunnelDepth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretty print the messages provided
|
||||
*
|
||||
*/
|
||||
private String getMessages(List msgs) {
|
||||
if (msgs == null) return "";
|
||||
int num = msgs.size();
|
||||
switch (num) {
|
||||
case 0: return "";
|
||||
case 1: return (String)msgs.get(0);
|
||||
default:
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("<ul>");
|
||||
for (int i = 0; i < num; i++)
|
||||
buf.append("<li>").append((String)msgs.get(i)).append("</li>\n");
|
||||
buf.append("</ul>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
package net.i2p.i2ptunnel;
|
||||
|
||||
import java.util.List;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Ugly hack to let the web interface access the list of known tunnels and
|
||||
* control their operation. Any data submitted by setting properties are
|
||||
* acted upon by calling getActionResults() (which returns any messages
|
||||
* generated). In addition, the getSummaryList() generates the html for
|
||||
* summarizing all of the tunnels known, including both their status and the
|
||||
* links to edit, stop, or start them.
|
||||
*
|
||||
*/
|
||||
public class WebStatusPageHelper {
|
||||
private Log _log;
|
||||
private String _action;
|
||||
private int _controllerNum;
|
||||
private static boolean _configLoaded = false;
|
||||
|
||||
static final String CONFIG_FILE = "i2ptunnel.cfg";
|
||||
|
||||
public WebStatusPageHelper() {
|
||||
_action = null;
|
||||
_controllerNum = -1;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(WebStatusPageHelper.class);
|
||||
synchronized (WebStatusPageHelper.class) {
|
||||
if (!_configLoaded) {
|
||||
reloadConfig();
|
||||
_configLoaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
_action = action;
|
||||
}
|
||||
public void setNum(String num) {
|
||||
if (num != null) {
|
||||
try {
|
||||
_controllerNum = Integer.parseInt(num);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_controllerNum = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getActionResults() {
|
||||
try {
|
||||
return processAction();
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Internal error processing web status", t);
|
||||
return "Internal error processing request - " + t.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
public String getSummaryList() {
|
||||
StringBuffer buf = new StringBuffer(4*1024);
|
||||
buf.append("<ul>");
|
||||
List tunnels = TunnelControllerGroup.getInstance().getControllers();
|
||||
for (int i = 0; i < tunnels.size(); i++) {
|
||||
buf.append("<li>\n");
|
||||
getSummary(buf, i, (TunnelController)tunnels.get(i));
|
||||
buf.append("</li>\n");
|
||||
}
|
||||
buf.append("</ul>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private void getSummary(StringBuffer buf, int num, TunnelController controller) {
|
||||
buf.append("<b>").append(controller.getName()).append("</b>: ");
|
||||
if (controller.getIsRunning()) {
|
||||
buf.append("<i>running</i> ");
|
||||
buf.append("<a href=\"index.jsp?num=").append(num).append("&action=stop\">stop</a> ");
|
||||
} else {
|
||||
buf.append("<i>not running</i> ");
|
||||
buf.append("<a href=\"index.jsp?num=").append(num).append("&action=start\">start</a> ");
|
||||
}
|
||||
buf.append("<a href=\"edit.jsp?num=").append(num).append("\">edit</a> ");
|
||||
buf.append("<br />\n");
|
||||
controller.getSummary(buf);
|
||||
}
|
||||
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) )
|
||||
return getMessages();
|
||||
if ("Stop all".equals(_action))
|
||||
return stopAll();
|
||||
else if ("Start all".equals(_action))
|
||||
return startAll();
|
||||
else if ("Restart all".equals(_action))
|
||||
return restartAll();
|
||||
else if ("Reload config".equals(_action))
|
||||
return reloadConfig();
|
||||
else if ("stop".equals(_action))
|
||||
return stop();
|
||||
else if ("start".equals(_action))
|
||||
return start();
|
||||
else
|
||||
return "Action <i>" + _action + "</i> unknown";
|
||||
}
|
||||
private String stopAll() {
|
||||
List msgs = TunnelControllerGroup.getInstance().stopAllControllers();
|
||||
return getMessages(msgs);
|
||||
}
|
||||
private String startAll() {
|
||||
List msgs = TunnelControllerGroup.getInstance().startAllControllers();
|
||||
return getMessages(msgs);
|
||||
}
|
||||
private String restartAll() {
|
||||
List msgs = TunnelControllerGroup.getInstance().restartAllControllers();
|
||||
return getMessages(msgs);
|
||||
}
|
||||
private String reloadConfig() {
|
||||
TunnelControllerGroup.getInstance().unloadControllers();
|
||||
TunnelControllerGroup.getInstance().loadControllers(CONFIG_FILE);
|
||||
return "Config reloaded";
|
||||
}
|
||||
private String start() {
|
||||
if (_controllerNum < 0) return "Invalid tunnel";
|
||||
List controllers = TunnelControllerGroup.getInstance().getControllers();
|
||||
if (_controllerNum >= controllers.size()) return "Invalid tunnel";
|
||||
TunnelController controller = (TunnelController)controllers.get(_controllerNum);
|
||||
controller.startTunnel();
|
||||
return getMessages(controller.clearMessages());
|
||||
}
|
||||
|
||||
private String stop() {
|
||||
if (_controllerNum < 0) return "Invalid tunnel";
|
||||
List controllers = TunnelControllerGroup.getInstance().getControllers();
|
||||
if (_controllerNum >= controllers.size()) return "Invalid tunnel";
|
||||
TunnelController controller = (TunnelController)controllers.get(_controllerNum);
|
||||
controller.stopTunnel();
|
||||
return getMessages(controller.clearMessages());
|
||||
}
|
||||
|
||||
private String getMessages() {
|
||||
return getMessages(TunnelControllerGroup.getInstance().clearAllMessages());
|
||||
}
|
||||
|
||||
private String getMessages(List msgs) {
|
||||
if (msgs == null) return "";
|
||||
int num = msgs.size();
|
||||
switch (num) {
|
||||
case 0: return "";
|
||||
case 1: return (String)msgs.get(0);
|
||||
default:
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("<ul>");
|
||||
for (int i = 0; i < num; i++)
|
||||
buf.append("<li>").append((String)msgs.get(i)).append("</li>\n");
|
||||
buf.append("</ul>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
16
apps/i2ptunnel/jsp/edit.jsp
Normal file
16
apps/i2ptunnel/jsp/edit.jsp
Normal file
@ -0,0 +1,16 @@
|
||||
<%@page contentType="text/html" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2PTunnel edit</title>
|
||||
</head><body>
|
||||
|
||||
<a href="index.jsp">Back</a>
|
||||
|
||||
<jsp:useBean class="net.i2p.i2ptunnel.WebEditPageHelper" id="helper" scope="request" />
|
||||
<jsp:setProperty name="helper" property="*" />
|
||||
<b><jsp:getProperty name="helper" property="actionResults" /></b>
|
||||
|
||||
<jsp:getProperty name="helper" property="editForm" />
|
||||
</body>
|
||||
</html>
|
2
apps/i2ptunnel/jsp/index.html
Normal file
2
apps/i2ptunnel/jsp/index.html
Normal file
@ -0,0 +1,2 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>I2P Router Console</title></head>
|
||||
<body><meta http-equiv="refresh" content="0;url=index.jsp" /><a href="index.jsp">Enter</a></body></html>
|
31
apps/i2ptunnel/jsp/index.jsp
Normal file
31
apps/i2ptunnel/jsp/index.jsp
Normal file
@ -0,0 +1,31 @@
|
||||
<%@page contentType="text/html" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2PTunnel status</title>
|
||||
</head><body>
|
||||
|
||||
<jsp:useBean class="net.i2p.i2ptunnel.WebStatusPageHelper" id="helper" scope="request" />
|
||||
<jsp:setProperty name="helper" property="*" />
|
||||
<b><jsp:getProperty name="helper" property="actionResults" /></b>
|
||||
|
||||
<jsp:getProperty name="helper" property="summaryList" />
|
||||
<hr />
|
||||
<form action="index.jsp" method="GET">
|
||||
<input type="submit" name="action" value="Stop all" />
|
||||
<input type="submit" name="action" value="Start all" />
|
||||
<input type="submit" name="action" value="Restart all" />
|
||||
<input type="submit" name="action" value="Reload config" />
|
||||
</form>
|
||||
|
||||
<form action="edit.jsp">
|
||||
<b>Add new:</b>
|
||||
<select name="type">
|
||||
<option value="httpclient">HTTP proxy</option>
|
||||
<option value="client">Client tunnel</option>
|
||||
<option value="server">Server tunnel</option>
|
||||
</select> <input type="submit" value="GO" />
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
17
apps/i2ptunnel/jsp/web.xml
Normal file
17
apps/i2ptunnel/jsp/web.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE web-app
|
||||
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
|
||||
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
|
||||
|
||||
<web-app>
|
||||
<!-- precompiled servlets -->
|
||||
<session-config>
|
||||
<session-timeout>
|
||||
30
|
||||
</session-timeout>
|
||||
</session-config>
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
</web-app>
|
28
apps/jetty/build.xml
Normal file
28
apps/jetty/build.xml
Normal file
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="jetty">
|
||||
|
||||
<target name="all" depends="build" />
|
||||
<target name="fetchJettylib" >
|
||||
<available property="jetty.available" file="jettylib" />
|
||||
<ant target="doFetchJettylib" />
|
||||
</target>
|
||||
<target name="doFetchJettylib" unless="jetty.available" >
|
||||
<echo message="The libraries contained within the fetched file are from Jetty's 4.2.21 " />
|
||||
<echo message="distribution (http://jetty.mortbay.org/) which we have copied to our website since" />
|
||||
<echo message="theirs doesn't have direct HTTP access to the libs. These are not " />
|
||||
<echo message="necessary for using I2P, but are used by some applications on top of I2P," />
|
||||
<echo message="such as the routerconsole." />
|
||||
<get src="http://dev.i2p.net/jettylib.tar.bz2" verbose="true" dest="jettylib.tar.bz2" />
|
||||
<untar src="jettylib.tar.bz2" compression="bzip2" dest="." />
|
||||
<delete file="jettylib.tar.bz2" />
|
||||
</target>
|
||||
<target name="build" depends="fetchJettylib" />
|
||||
<target name="builddep" />
|
||||
<target name="compile" />
|
||||
<target name="jar" />
|
||||
<target name="clean" />
|
||||
<target name="cleandep" depends="clean" />
|
||||
<target name="distclean" depends="clean">
|
||||
<echo message="Not actually deleting the jetty libs (since they're so large)" />
|
||||
</target>
|
||||
</project>
|
@ -32,8 +32,18 @@ public interface I2PSocket {
|
||||
*/
|
||||
public OutputStream getOutputStream() throws IOException;
|
||||
|
||||
/**
|
||||
* Retrieve this socket's configuration
|
||||
*/
|
||||
public I2PSocketOptions getOptions();
|
||||
/**
|
||||
* Configure the socket
|
||||
*/
|
||||
public void setOptions(I2PSocketOptions options);
|
||||
|
||||
/**
|
||||
* How long we will wait blocked on a read() operation.
|
||||
* How long we will wait blocked on a read() operation. This is simply a
|
||||
* helper to query the I2PSocketOptions
|
||||
*
|
||||
* @return milliseconds to wait, or -1 if we will wait indefinitely
|
||||
*/
|
||||
@ -41,7 +51,8 @@ public interface I2PSocket {
|
||||
|
||||
/**
|
||||
* Define how long we will wait blocked on a read() operation (-1 will make
|
||||
* the socket wait forever).
|
||||
* the socket wait forever). This is simply a helper to adjust the
|
||||
* I2PSocketOptions
|
||||
*
|
||||
*/
|
||||
public void setReadTimeout(long ms);
|
||||
|
@ -9,6 +9,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@ -40,6 +41,7 @@ class I2PSocketImpl implements I2PSocket {
|
||||
private long _createdOn;
|
||||
private long _closedOn;
|
||||
private long _remoteIdSetTime;
|
||||
private I2PSocketOptions _options;
|
||||
private Object flagLock = new Object();
|
||||
|
||||
/**
|
||||
@ -73,14 +75,17 @@ class I2PSocketImpl implements I2PSocket {
|
||||
remote = peer;
|
||||
_socketId = ++__socketId;
|
||||
local = mgr.getSession().getMyDestination();
|
||||
in = new I2PInputStream();
|
||||
I2PInputStream pin = new I2PInputStream();
|
||||
String us = mgr.getSession().getMyDestination().calculateHash().toBase64().substring(0,4);
|
||||
String name = us + (outgoing ? "->" : "<-") + peer.calculateHash().toBase64().subSequence(0,4);
|
||||
in = new I2PInputStream(name + " in");
|
||||
I2PInputStream pin = new I2PInputStream(name + " out");
|
||||
out = new I2POutputStream(pin);
|
||||
new I2PSocketRunner(pin);
|
||||
this.localID = localID;
|
||||
_createdOn = I2PAppContext.getGlobalContext().clock().now();
|
||||
_remoteIdSetTime = -1;
|
||||
_closedOn = -1;
|
||||
_options = mgr.getDefaultOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -176,7 +181,11 @@ class I2PSocketImpl implements I2PSocket {
|
||||
*/
|
||||
public void queueData(byte[] data) {
|
||||
_bytesRead += data.length;
|
||||
in.queueData(data);
|
||||
try {
|
||||
in.queueData(data, false);
|
||||
} catch (IOException ioe) {
|
||||
_log.log(Log.CRIT, "wtf, we said DONT block, how can we timeout?", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -245,19 +254,36 @@ class I2PSocketImpl implements I2PSocket {
|
||||
return (byte)(I2PSocketManager.DATA_OUT + (byte)add);
|
||||
}
|
||||
|
||||
public void setOptions(I2PSocketOptions options) {
|
||||
_options = options;
|
||||
in.setReadTimeout(options.getReadTimeout());
|
||||
}
|
||||
|
||||
public I2PSocketOptions getOptions() {
|
||||
return _options;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data? If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
* How long we will wait blocked on a read() operation. This is simply a
|
||||
* helper to query the I2PSocketOptions
|
||||
*
|
||||
* @return milliseconds to wait, or -1 if we will wait indefinitely
|
||||
*/
|
||||
public long getReadTimeout() {
|
||||
return in.getReadTimeout();
|
||||
return _options.getReadTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define how long we will wait blocked on a read() operation (-1 will make
|
||||
* the socket wait forever). This is simply a helper to adjust the
|
||||
* I2PSocketOptions
|
||||
*
|
||||
*/
|
||||
public void setReadTimeout(long ms) {
|
||||
_options.setReadTimeout(ms);
|
||||
in.setReadTimeout(ms);
|
||||
}
|
||||
|
||||
|
||||
public void setSocketErrorListener(SocketErrorListener lsnr) {
|
||||
_socketErrorListener = lsnr;
|
||||
}
|
||||
@ -277,15 +303,24 @@ class I2PSocketImpl implements I2PSocket {
|
||||
|
||||
//--------------------------------------------------
|
||||
private class I2PInputStream extends InputStream {
|
||||
|
||||
private String streamName;
|
||||
private ByteCollector bc = new ByteCollector();
|
||||
private boolean inStreamClosed = false;
|
||||
|
||||
private long readTimeout = -1;
|
||||
|
||||
public I2PInputStream(String name) {
|
||||
streamName = name;
|
||||
}
|
||||
|
||||
public long getReadTimeout() {
|
||||
return readTimeout;
|
||||
}
|
||||
|
||||
private String getStreamPrefix() {
|
||||
return getPrefix() + streamName + ": ";
|
||||
}
|
||||
|
||||
public void setReadTimeout(long ms) {
|
||||
readTimeout = ms;
|
||||
}
|
||||
@ -300,12 +335,14 @@ class I2PSocketImpl implements I2PSocket {
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Read called for " + len + " bytes (avail=" + bc.getCurrentSize() + "): " + this.hashCode());
|
||||
_log.debug(getStreamPrefix() + "Read called for " + len + " bytes (avail="
|
||||
+ bc.getCurrentSize() + "): " + this.hashCode());
|
||||
if (len == 0) return 0;
|
||||
long dieAfter = System.currentTimeMillis() + readTimeout;
|
||||
byte[] read = null;
|
||||
synchronized (bc) {
|
||||
read = bc.startToByteArray(len);
|
||||
bc.notifyAll();
|
||||
}
|
||||
boolean timedOut = false;
|
||||
|
||||
@ -313,7 +350,9 @@ class I2PSocketImpl implements I2PSocket {
|
||||
synchronized (flagLock) {
|
||||
if (closed) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Closed is set after reading " + _bytesRead + " and writing " + _bytesWritten + ", so closing stream: " + hashCode());
|
||||
_log.debug(getStreamPrefix() + "Closed is set after reading "
|
||||
+ _bytesRead + " and writing " + _bytesWritten
|
||||
+ ", so closing stream: " + hashCode());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -329,18 +368,20 @@ class I2PSocketImpl implements I2PSocket {
|
||||
|
||||
if ((readTimeout >= 0)
|
||||
&& (System.currentTimeMillis() >= dieAfter)) {
|
||||
throw new InterruptedIOException(getPrefix() + "Timeout reading from I2PSocket (" + readTimeout + " msecs)");
|
||||
throw new InterruptedIOException(getStreamPrefix() + "Timeout reading from I2PSocket ("
|
||||
+ readTimeout + " msecs)");
|
||||
}
|
||||
|
||||
synchronized (bc) {
|
||||
read = bc.startToByteArray(len);
|
||||
bc.notifyAll();
|
||||
}
|
||||
}
|
||||
if (read.length > len) throw new RuntimeException("BUG");
|
||||
System.arraycopy(read, 0, b, off, read.length);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG)) {
|
||||
_log.debug(getPrefix() + "Read from I2PInputStream " + hashCode() + " returned "
|
||||
_log.debug(getStreamPrefix() + "Read from I2PInputStream " + hashCode() + " returned "
|
||||
+ read.length + " bytes");
|
||||
}
|
||||
//if (_log.shouldLog(Log.DEBUG)) {
|
||||
@ -357,14 +398,59 @@ class I2PSocketImpl implements I2PSocket {
|
||||
}
|
||||
}
|
||||
|
||||
public void queueData(byte[] data) {
|
||||
queueData(data, 0, data.length);
|
||||
/**
|
||||
* Add the data to the queue
|
||||
*
|
||||
* @param allowBlock if true, we will block if the buffer and the socket options
|
||||
* say so, otherwise we simply take the data regardless.
|
||||
* @throws InterruptedIOException if the queue's buffer is full, the socket has
|
||||
* a write timeout, and that timeout is exceeded
|
||||
* @throws IOException if the connection was closed while queueing up the data
|
||||
*/
|
||||
void queueData(byte[] data, boolean allowBlock) throws InterruptedIOException, IOException {
|
||||
queueData(data, 0, data.length, allowBlock);
|
||||
}
|
||||
|
||||
public void queueData(byte[] data, int off, int len) {
|
||||
/**
|
||||
* Add the data to the queue
|
||||
*
|
||||
* @param allowBlock if true, we will block if the buffer and the socket options
|
||||
* say so, otherwise we simply take the data regardless.
|
||||
* @throws InterruptedIOException if the queue's buffer is full, the socket has
|
||||
* a write timeout, and that timeout is exceeded
|
||||
* @throws IOException if the connection was closed while queueing up the data
|
||||
*/
|
||||
public void queueData(byte[] data, int off, int len, boolean allowBlock) throws InterruptedIOException, IOException {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Insert " + len + " bytes into queue: " + hashCode());
|
||||
_log.debug(getStreamPrefix() + "Insert " + len + " bytes into queue: " + hashCode());
|
||||
Clock clock = I2PAppContext.getGlobalContext().clock();
|
||||
long endAfter = clock.now() + _options.getWriteTimeout();
|
||||
synchronized (bc) {
|
||||
if (allowBlock) {
|
||||
if (_options.getMaxBufferSize() > 0) {
|
||||
while (bc.getCurrentSize() > _options.getMaxBufferSize()) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getStreamPrefix() + "Buffer size exceeded: pending "
|
||||
+ bc.getCurrentSize() + " limit " + _options.getMaxBufferSize());
|
||||
if (_options.getWriteTimeout() > 0) {
|
||||
long timeLeft = endAfter - clock.now();
|
||||
if (timeLeft <= 0) {
|
||||
long waited = _options.getWriteTimeout() - timeLeft;
|
||||
throw new InterruptedIOException(getStreamPrefix() + "Waited too long ("
|
||||
+ waited + "ms) to write "
|
||||
+ len + " with a buffer at " + bc.getCurrentSize());
|
||||
}
|
||||
}
|
||||
if (inStreamClosed)
|
||||
throw new IOException(getStreamPrefix() + "Stream closed while writing");
|
||||
if (_closedOn > 0)
|
||||
throw new IOException(getStreamPrefix() + "I2PSocket closed while writing");
|
||||
try {
|
||||
bc.wait(1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
bc.append(data, off, len);
|
||||
}
|
||||
synchronized (I2PInputStream.this) {
|
||||
@ -381,6 +467,10 @@ class I2PSocketImpl implements I2PSocket {
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
notifyClosed();
|
||||
synchronized (bc) {
|
||||
inStreamClosed = true;
|
||||
bc.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -399,7 +489,7 @@ class I2PSocketImpl implements I2PSocket {
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
_bytesWritten += len;
|
||||
sendTo.queueData(b, off, len);
|
||||
sendTo.queueData(b, off, len, true);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
@ -458,6 +548,9 @@ class I2PSocketImpl implements I2PSocket {
|
||||
_log.warn(getPrefix() + "Error sending message to peer. Killing socket runner");
|
||||
errorOccurred();
|
||||
return false;
|
||||
} else {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getPrefix() + "Message sent to peer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ public class I2PSocketManager implements I2PSessionListener {
|
||||
*
|
||||
* @throws IllegalStateException if the socket isn't open or isn't known
|
||||
*/
|
||||
private void sendIncoming(String id, byte payload[]) {
|
||||
private void sendIncoming(String id, byte payload[]) throws IllegalStateException {
|
||||
I2PSocketImpl s = null;
|
||||
synchronized (lock) {
|
||||
s = (I2PSocketImpl) _inSockets.get(id);
|
||||
@ -469,7 +469,10 @@ public class I2PSocketManager implements I2PSessionListener {
|
||||
_context.statManager().addRateData("streaming.synNoAck", 1, 1);
|
||||
throw new I2PException("Error sending through I2P network");
|
||||
}
|
||||
remoteID = s.getRemoteID(true, options.getConnectTimeout());
|
||||
if (options != null)
|
||||
remoteID = s.getRemoteID(true, options.getConnectTimeout());
|
||||
else
|
||||
remoteID = s.getRemoteID(true, getDefaultOptions().getConnectTimeout());
|
||||
|
||||
if (remoteID == null) {
|
||||
_context.statManager().addRateData("streaming.nackReceived", 1, 1);
|
||||
|
@ -30,7 +30,18 @@ public class I2PSocketManagerFactory {
|
||||
* @return the newly created socket manager, or null if there were errors
|
||||
*/
|
||||
public static I2PSocketManager createManager() {
|
||||
return createManager("localhost", 7654, new Properties());
|
||||
String i2cpHost = System.getProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
||||
int i2cpPort = 7654;
|
||||
String i2cpPortStr = System.getProperty(I2PClient.PROP_TCP_PORT);
|
||||
if (i2cpPortStr != null) {
|
||||
try {
|
||||
i2cpPort = Integer.parseInt(i2cpPortStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// gobble gobble
|
||||
}
|
||||
}
|
||||
|
||||
return createManager(i2cpHost, i2cpPort, System.getProperties());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,6 +92,7 @@ public class I2PSocketManagerFactory {
|
||||
private static I2PSocketManager createManager(I2PSession session) {
|
||||
I2PSocketManager mgr = new I2PSocketManager();
|
||||
mgr.setSession(session);
|
||||
mgr.setDefaultOptions(new I2PSocketOptions());
|
||||
return mgr;
|
||||
}
|
||||
}
|
@ -6,9 +6,18 @@ package net.i2p.client.streaming;
|
||||
*/
|
||||
public class I2PSocketOptions {
|
||||
private long _connectTimeout;
|
||||
private long _readTimeout;
|
||||
private long _writeTimeout;
|
||||
private int _maxBufferSize;
|
||||
|
||||
public static final int DEFAULT_BUFFER_SIZE = 1024*64;
|
||||
public static final int DEFAULT_WRITE_TIMEOUT = 60*1000;
|
||||
|
||||
public I2PSocketOptions() {
|
||||
_connectTimeout = -1;
|
||||
_readTimeout = -1;
|
||||
_writeTimeout = DEFAULT_WRITE_TIMEOUT;
|
||||
_maxBufferSize = DEFAULT_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -27,4 +36,65 @@ public class I2PSocketOptions {
|
||||
public void setConnectTimeout(long ms) {
|
||||
_connectTimeout = ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public long getReadTimeout() {
|
||||
return _readTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the input stream while waiting
|
||||
* for more data. If this value is exceeded, the read() throws
|
||||
* InterruptedIOException
|
||||
*/
|
||||
public void setReadTimeout(long ms) {
|
||||
_readTimeout = ms;
|
||||
}
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
* this amount has been exceeded, subsequent .write calls will block until
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
* @return buffer size limit, in bytes
|
||||
*/
|
||||
public int getMaxBufferSize() {
|
||||
return _maxBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* How much data will we accept that hasn't been written out yet. After
|
||||
* this amount has been exceeded, subsequent .write calls will block until
|
||||
* either some data is removed or the connection is closed. If this is
|
||||
* less than or equal to zero, there is no limit (warning: can eat ram)
|
||||
*
|
||||
*/
|
||||
public void setMaxBufferSize(int numBytes) {
|
||||
_maxBufferSize = numBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public long getWriteTimeout() {
|
||||
return _writeTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* What is the longest we'll block on the output stream while waiting
|
||||
* for the data to flush. If this value is exceeded, the write() throws
|
||||
* InterruptedIOException. If this is less than or equal to zero, there
|
||||
* is no timeout.
|
||||
*/
|
||||
public void setWriteTimeout(long ms) {
|
||||
_writeTimeout = ms;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,135 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.NoRouteToHostException;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Simple streaming lib test app that connects to a given destination and sends
|
||||
* it a particular amount of random data, then disconnects. See the {@link #main}
|
||||
*
|
||||
*/
|
||||
public class StreamSinkClient {
|
||||
private Log _log;
|
||||
private int _sendSize;
|
||||
private int _writeDelay;
|
||||
private String _peerDestFile;
|
||||
|
||||
|
||||
/**
|
||||
* Build the client but don't fire it up.
|
||||
* @param sendSize how many KB to send
|
||||
* @param writeDelayMs how long to wait between each .write (0 for no delay)
|
||||
* @param serverDestFile file containing the StreamSinkServer's binary Destination
|
||||
*/
|
||||
public StreamSinkClient(int sendSize, int writeDelayMs, String serverDestFile) {
|
||||
_sendSize = sendSize;
|
||||
_writeDelay = writeDelayMs;
|
||||
_peerDestFile = serverDestFile;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkClient.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually connect and run the client - this call blocks until completion.
|
||||
*
|
||||
*/
|
||||
public void runClient() {
|
||||
I2PSocketManager mgr = I2PSocketManagerFactory.createManager();
|
||||
Destination peer = null;
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(_peerDestFile);
|
||||
peer = new Destination();
|
||||
peer.readBytes(fis);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error finding the peer destination to contact in " + _peerDestFile, ioe);
|
||||
return;
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.error("Peer destination is not valid in " + _peerDestFile, dfe);
|
||||
return;
|
||||
} finally {
|
||||
if (fis == null) try { fis.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
|
||||
System.out.println("Send " + _sendSize + "KB to " + peer.calculateHash().toBase64());
|
||||
|
||||
try {
|
||||
I2PSocket sock = mgr.connect(peer);
|
||||
byte buf[] = new byte[32*1024];
|
||||
Random rand = new Random();
|
||||
OutputStream out = sock.getOutputStream();
|
||||
long beforeSending = System.currentTimeMillis();
|
||||
for (int i = 0; i < _sendSize; i+= 32) {
|
||||
rand.nextBytes(buf);
|
||||
out.write(buf);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Wrote " + (i+32) + "/" + _sendSize + "KB");
|
||||
if (_writeDelay > 0) {
|
||||
try { Thread.sleep(_writeDelay); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
long afterSending = System.currentTimeMillis();
|
||||
System.out.println("Sent " + _sendSize + "KB in " + (afterSending-beforeSending) + "ms");
|
||||
sock.close();
|
||||
} catch (InterruptedIOException iie) {
|
||||
_log.error("Timeout connecting to the peer", iie);
|
||||
return;
|
||||
} catch (NoRouteToHostException nrthe) {
|
||||
_log.error("Unable to connect to the peer", nrthe);
|
||||
return;
|
||||
} catch (ConnectException ce) {
|
||||
_log.error("Connection already dropped", ce);
|
||||
return;
|
||||
} catch (I2PException ie) {
|
||||
_log.error("Error connecting to the peer", ie);
|
||||
return;
|
||||
} catch (IOException ioe) {
|
||||
_log.error("IO error sending", ioe);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire up the client. <code>Usage: StreamSinkClient sendSizeKB writeDelayMs serverDestFile</code> <br />
|
||||
* <ul>
|
||||
* <li><b>sendSizeKB</b>: how many KB to send</li>
|
||||
* <li><b>writeDelayMs</b>: how long to wait between each .write (0 for no delay)</li>
|
||||
* <li><b>serverDestFile</b>: file containing the StreamSinkServer's binary Destination</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
if (args.length != 3) {
|
||||
System.out.println("Usage: StreamSinkClient sendSizeKB writeDelayMs serverDestFile");
|
||||
} else {
|
||||
int sendSizeKB = -1;
|
||||
int writeDelayMs = -1;
|
||||
try {
|
||||
sendSizeKB = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.err.println("Send size invalid [" + args[0] + "]");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
writeDelayMs = Integer.parseInt(args[1]);
|
||||
} catch (NumberFormatException nfe) {
|
||||
System.err.println("Write delay ms invalid [" + args[1] + "]");
|
||||
return;
|
||||
}
|
||||
StreamSinkClient client = new StreamSinkClient(sendSizeKB, writeDelayMs, args[2]);
|
||||
client.runClient();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
package net.i2p.client.streaming;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import java.net.ConnectException;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Listen to a destination, receiving any sockets and writing anything they
|
||||
* send to a new file. See the {@link #main}
|
||||
*
|
||||
*/
|
||||
public class StreamSinkServer {
|
||||
private Log _log;
|
||||
private String _sinkDir;
|
||||
private String _destFile;
|
||||
|
||||
/**
|
||||
* Create but do not start the streaming server.
|
||||
*
|
||||
* @param sinkDir Directory to store received files in
|
||||
* @param ourDestFile filename to write our binary destination to
|
||||
*/
|
||||
public StreamSinkServer(String sinkDir, String ourDestFile) {
|
||||
_sinkDir = sinkDir;
|
||||
_destFile = ourDestFile;
|
||||
_log = I2PAppContext.getGlobalContext().logManager().getLog(StreamSinkServer.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually fire up the server - this call blocks forever (or until the server
|
||||
* socket closes)
|
||||
*
|
||||
*/
|
||||
public void runServer() {
|
||||
I2PSocketManager mgr = I2PSocketManagerFactory.createManager();
|
||||
Destination dest = mgr.getSession().getMyDestination();
|
||||
System.out.println("Listening for connections on: " + dest.calculateHash().toBase64());
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(_destFile);
|
||||
dest.writeBytes(fos);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing out our destination to " + _destFile, ioe);
|
||||
return;
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.error("Error formatting the destination", dfe);
|
||||
return;
|
||||
} finally {
|
||||
if (fos != null) try { fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
|
||||
I2PServerSocket sock = mgr.getServerSocket();
|
||||
while (true) {
|
||||
try {
|
||||
I2PSocket curSock = sock.accept();
|
||||
handle(curSock);
|
||||
} catch (I2PException ie) {
|
||||
_log.error("Error accepting connection", ie);
|
||||
return;
|
||||
} catch (ConnectException ce) {
|
||||
_log.error("Connection already dropped", ce);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handle(I2PSocket socket) {
|
||||
I2PThread t = new I2PThread(new ClientRunner(socket));
|
||||
t.setName("Handle " + socket.getPeerDestination().calculateHash().toBase64().substring(0,4));
|
||||
t.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually deal with a client - pull anything they send us and write it to a file.
|
||||
*
|
||||
*/
|
||||
private class ClientRunner implements Runnable {
|
||||
private I2PSocket _sock;
|
||||
private FileOutputStream _fos;
|
||||
public ClientRunner(I2PSocket socket) {
|
||||
_sock = socket;
|
||||
try {
|
||||
File sink = new File(_sinkDir);
|
||||
if (!sink.exists())
|
||||
sink.mkdirs();
|
||||
File cur = File.createTempFile("clientSink", ".dat", sink);
|
||||
_fos = new FileOutputStream(cur);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error creating sink", ioe);
|
||||
_fos = null;
|
||||
}
|
||||
}
|
||||
public void run() {
|
||||
if (_fos == null) return;
|
||||
try {
|
||||
InputStream in = _sock.getInputStream();
|
||||
byte buf[] = new byte[4096];
|
||||
int read = 0;
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
_fos.write(buf, 0, read);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error writing the sink", ioe);
|
||||
} finally {
|
||||
if (_fos != null) try { _fos.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire up the streaming server. <code>Usage: StreamSinkServer sinkDir ourDestFile</code><br />
|
||||
* <ul>
|
||||
* <li><b>sinkDir</b>: Directory to store received files in</li>
|
||||
* <li><b>ourDestFile</b>: filename to write our binary destination to</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
if (args.length != 2) {
|
||||
System.out.println("Usage: StreamSinkServer sinkDir ourDestFile");
|
||||
} else {
|
||||
StreamSinkServer server = new StreamSinkServer(args[0], args[1]);
|
||||
server.runServer();
|
||||
}
|
||||
}
|
||||
}
|
@ -15,4 +15,10 @@ net.i2p.client.streaming.I2PServerSocket#accept} method, which will provide an
|
||||
{@link net.i2p.client.streaming.I2PSocket} when a new one is available. If an
|
||||
application wants to create a new stream to a peer, it should do so with the
|
||||
appropriate {@link net.i2p.client.streaming.I2PSocketManager#connect} call.</p>
|
||||
|
||||
<p>There is a simple pair of demo applications available as well - {@link
|
||||
net.i2p.client.streaming.StreamSinkServer} listens to a destination and dumps
|
||||
the data from all sockets it accepts to individual files, while {@link
|
||||
net.i2p.client.streaming.StreamSinkClient} connects to a particular destination
|
||||
and sends a specific amount of random data then disconnects.</p>
|
||||
</body></html>
|
||||
|
@ -72,7 +72,7 @@ class DataHarvester {
|
||||
*/
|
||||
private void harvestRankAs(NetMonitor monitor, RouterInfo peer) {
|
||||
int numFast = 0;
|
||||
int numReliable = 0;
|
||||
int numHighCapacity = 0;
|
||||
int numNotFailing = 0;
|
||||
int numFailing = 0;
|
||||
|
||||
@ -81,10 +81,10 @@ class DataHarvester {
|
||||
String key = (String)iter.next();
|
||||
if (key.startsWith("profile.")) {
|
||||
String val = (String)props.get(key);
|
||||
if (val.indexOf("fastReliable") != -1)
|
||||
if (val.indexOf("fast") != -1)
|
||||
numFast++;
|
||||
else if (val.indexOf("reliable") != -1)
|
||||
numReliable++;
|
||||
else if (val.indexOf("highCapacity") != -1)
|
||||
numHighCapacity++;
|
||||
else if (val.indexOf("notFailing") != -1)
|
||||
numNotFailing++;
|
||||
else if (val.indexOf("failing") != -1)
|
||||
@ -94,13 +94,13 @@ class DataHarvester {
|
||||
|
||||
long rankAs[] = new long[4];
|
||||
rankAs[0] = numFast;
|
||||
rankAs[1] = numReliable;
|
||||
rankAs[1] = numHighCapacity;
|
||||
rankAs[2] = numNotFailing;
|
||||
rankAs[3] = numFailing;
|
||||
String description = "how we rank peers";
|
||||
String valDescr[] = new String[4];
|
||||
valDescr[0] = "# peers we rank as fast";
|
||||
valDescr[1] = "# peers we rank as reliable";
|
||||
valDescr[1] = "# peers we rank as high capacity";
|
||||
valDescr[2] = "# peers we rank as not failing";
|
||||
valDescr[3] = "# peers we rank as failing";
|
||||
monitor.addData(peer.getIdentity().getHash().toBase64(), "rankAs", description, valDescr, peer.getPublished(), rankAs);
|
||||
@ -115,7 +115,7 @@ class DataHarvester {
|
||||
*/
|
||||
private void harvestRank(NetMonitor monitor, RouterInfo peer, List peers) {
|
||||
int numFast = 0;
|
||||
int numReliable = 0;
|
||||
int numHighCapacity = 0;
|
||||
int numNotFailing = 0;
|
||||
int numFailing = 0;
|
||||
|
||||
@ -126,10 +126,10 @@ class DataHarvester {
|
||||
String prop = "profile." + peer.getIdentity().getHash().toBase64().replace('=', '_');
|
||||
String val = cur.getOptions().getProperty(prop);
|
||||
if ( (val == null) || (val.length() <= 0) ) continue;
|
||||
if (val.indexOf("fastReliable") != -1)
|
||||
if (val.indexOf("fast") != -1)
|
||||
numFast++;
|
||||
else if (val.indexOf("reliable") != -1)
|
||||
numReliable++;
|
||||
else if (val.indexOf("highCapacity") != -1)
|
||||
numHighCapacity++;
|
||||
else if (val.indexOf("notFailing") != -1)
|
||||
numNotFailing++;
|
||||
else if (val.indexOf("failing") != -1)
|
||||
@ -138,13 +138,13 @@ class DataHarvester {
|
||||
|
||||
long rank[] = new long[4];
|
||||
rank[0] = numFast;
|
||||
rank[1] = numReliable;
|
||||
rank[1] = numHighCapacity;
|
||||
rank[2] = numNotFailing;
|
||||
rank[3] = numFailing;
|
||||
String description = "how peers rank us";
|
||||
String valDescr[] = new String[4];
|
||||
valDescr[0] = "# peers ranking us as fast";
|
||||
valDescr[1] = "# peers ranking us as reliable";
|
||||
valDescr[1] = "# peers ranking us as high capacity";
|
||||
valDescr[2] = "# peers ranking us as not failing";
|
||||
valDescr[3] = "# peers ranking us as failing";
|
||||
// we use the current date, not the published date, since this sample doesnt come from them
|
||||
@ -173,7 +173,6 @@ class DataHarvester {
|
||||
double values[] = harvestGroupValues(peer, group);
|
||||
if (values == null) return;
|
||||
|
||||
String description = "how long it takes to do an ElGamal encryption";
|
||||
String valDescr[] = new String[group.getStatCount()];
|
||||
for (int i = 0; i < group.getStatCount(); i++)
|
||||
valDescr[i] = group.getStat(i).getStatDescription();
|
||||
|
@ -18,7 +18,7 @@ import net.i2p.util.Log;
|
||||
* Main driver for the app that harvests data about the performance of the network,
|
||||
* building summaries for each peer that change over time. <p />
|
||||
*
|
||||
* Usage: <code>NetMonitor [configFilename] [--routers filename[,filename]*]</code> <p />
|
||||
* Usage: <code>NetMonitor [configFilename] [--routers filename[,filename]*] [--netDbURL url] </code> <p />
|
||||
*
|
||||
*
|
||||
*
|
||||
@ -27,7 +27,7 @@ public class NetMonitor {
|
||||
private static final Log _log = new Log(NetMonitor.class);
|
||||
public static final String CONFIG_LOCATION_DEFAULT = "netmonitor.config";
|
||||
public static final String HARVEST_DELAY_PROP = "harvestDelaySeconds";
|
||||
public static final int HARVEST_DELAY_DEFAULT = 60;
|
||||
public static final int HARVEST_DELAY_DEFAULT = 5*60;
|
||||
public static final String EXPORT_DELAY_PROP = "exportDelaySeconds";
|
||||
public static final int EXPORT_DELAY_DEFAULT = 120;
|
||||
public static final String SUMMARY_DURATION_PROP = "summaryDurationHours";
|
||||
@ -41,20 +41,22 @@ public class NetMonitor {
|
||||
private int _exportDelay;
|
||||
private String _exportDir;
|
||||
private String _netDbDir;
|
||||
private String _netDbURL;
|
||||
private String _explicitRouters;
|
||||
private int _summaryDurationHours;
|
||||
private boolean _isRunning;
|
||||
private Map _peerSummaries;
|
||||
|
||||
public NetMonitor() {
|
||||
this(CONFIG_LOCATION_DEFAULT, null);
|
||||
this(CONFIG_LOCATION_DEFAULT, null, null);
|
||||
}
|
||||
public NetMonitor(String configLocation) {
|
||||
this(configLocation, null);
|
||||
this(configLocation, null, null);
|
||||
}
|
||||
public NetMonitor(String configLocation, String explicitFilenames) {
|
||||
public NetMonitor(String configLocation, String explicitFilenames, String url) {
|
||||
_configLocation = configLocation;
|
||||
_explicitRouters = explicitFilenames;
|
||||
_netDbURL = url;
|
||||
_peerSummaries = new HashMap(32);
|
||||
loadConfig();
|
||||
}
|
||||
@ -127,6 +129,9 @@ public class NetMonitor {
|
||||
public String getNetDbDir() { return _netDbDir; }
|
||||
/** if specified, contains a set of filenames we want to harvest routerInfo data from */
|
||||
public String getExplicitRouters() { return _explicitRouters; }
|
||||
/** if specified, contains a URL to fetch references from */
|
||||
public String getNetDbURL() { return _netDbURL; }
|
||||
|
||||
/**
|
||||
* what peers are we keeping track of?
|
||||
*
|
||||
@ -212,11 +217,12 @@ public class NetMonitor {
|
||||
|
||||
/**
|
||||
* main driver for the netMonitor. the usage is:
|
||||
* <code>NetMonitor [configFilename] [--routers filename[,filename]*]</code>
|
||||
* <code>NetMonitor [configFilename] [--routers filename[,filename]*] [--netDbURL url]</code>
|
||||
*/
|
||||
public static final void main(String args[]) {
|
||||
String cfgLocation = CONFIG_LOCATION_DEFAULT;
|
||||
String explicitFilenames = null;
|
||||
String explicitURL = null;
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
break;
|
||||
@ -224,16 +230,22 @@ public class NetMonitor {
|
||||
cfgLocation = args[0];
|
||||
break;
|
||||
case 2:
|
||||
explicitFilenames = args[1];
|
||||
if ("--routers".equalsIgnoreCase(args[0]))
|
||||
explicitFilenames = args[1];
|
||||
else
|
||||
explicitURL = args[1];
|
||||
break;
|
||||
case 3:
|
||||
cfgLocation = args[0];
|
||||
explicitFilenames = args[2];
|
||||
if ("--routers".equalsIgnoreCase(args[1]))
|
||||
explicitFilenames = args[2];
|
||||
else
|
||||
explicitURL = args[2];
|
||||
break;
|
||||
default:
|
||||
System.err.println("Usage: NetMonitor [configFilename] [--routers filename[,filename]*]");
|
||||
System.err.println("Usage: NetMonitor [configFilename] [--routers filename[,filename]*] [--netDbURL url]");
|
||||
return;
|
||||
}
|
||||
new NetMonitor(cfgLocation, explicitFilenames).startMonitor();
|
||||
new NetMonitor(cfgLocation, explicitFilenames, explicitURL).startMonitor();
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,19 @@
|
||||
package net.i2p.netmonitor;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
@ -64,6 +71,9 @@ class NetMonitorRunner implements Runnable {
|
||||
*
|
||||
*/
|
||||
private List getRouters() {
|
||||
if (_monitor.getNetDbURL() != null)
|
||||
return fetchRouters(_monitor.getNetDbURL());
|
||||
|
||||
File routers[] = listRouters();
|
||||
List rv = new ArrayList(64);
|
||||
if (routers != null) {
|
||||
@ -86,6 +96,65 @@ class NetMonitorRunner implements Runnable {
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
private List fetchRouters(String seedURL) {
|
||||
List rv = new ArrayList();
|
||||
try {
|
||||
URL dir = new URL(seedURL);
|
||||
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(); ) {
|
||||
rv.add(fetchSeed((String)iter.next()));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
_log.error("Error fetching routers from " + seedURL, t);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
private RouterInfo fetchSeed(String peer) throws Exception {
|
||||
URL url = new URL("http://i2p.net/i2pdb/routerInfo-" + peer + ".dat");
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Fetching seed from " + url.toExternalForm());
|
||||
|
||||
byte data[] = readURL(url);
|
||||
RouterInfo info = new RouterInfo();
|
||||
try {
|
||||
info.fromByteArray(data);
|
||||
return info;
|
||||
} catch (DataFormatException dfe) {
|
||||
_log.error("Router data at " + url.toExternalForm() + " was corrupt", dfe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* dump the data to the filesystem
|
||||
*/
|
||||
|
@ -2,22 +2,21 @@
|
||||
<project basedir="." default="all" name="routerconsole">
|
||||
<target name="all" depends="clean, build" />
|
||||
<target name="build" depends="builddep, jar" />
|
||||
<target name="builddep" depends="jetty" >
|
||||
<target name="builddep">
|
||||
<ant dir="../../../router/java/" target="build" />
|
||||
<!-- router will build core -->
|
||||
</target>
|
||||
<target name="jetty">
|
||||
<untar src="jetty-4.2.21-min.tar.bz2" compression="bzip2" dest="." />
|
||||
<ant dir="jetty-4.2.21-min/extra/jdk1.2/" target="all" />
|
||||
<target name="prepare">
|
||||
<ant dir="../../jetty/" target="build" />
|
||||
</target>
|
||||
<target name="compile">
|
||||
<target name="compile" depends="prepare">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac
|
||||
srcdir="./src"
|
||||
debug="true" deprecation="on" source="1.3" target="1.3"
|
||||
destdir="./build/obj"
|
||||
classpath="../../../core/java/build/i2p.jar:../../../router/java/build/router.jar:jetty-4.2.21-min/extra/lib/org.mortbay.jetty-jdk1.2.jar" />
|
||||
classpath="../../../core/java/build/i2p.jar:../../../router/java/build/router.jar:../../jetty/jettylib/org.mortbay.jetty-jdk1.2.jar" />
|
||||
</target>
|
||||
<target name="jar" depends="compile">
|
||||
<jar destfile="./build/routerconsole.jar" basedir="./build/obj" includes="**/*.class">
|
||||
@ -27,11 +26,46 @@
|
||||
</jar>
|
||||
<ant target="war" />
|
||||
</target>
|
||||
<target name="war">
|
||||
<war destfile="build/routerconsole.war" webxml="../jsp/web.xml"
|
||||
basedir="../jsp/" excludes="web.xml">
|
||||
<target name="war" depends="precompilejsp">
|
||||
<war destfile="build/routerconsole.war" webxml="../jsp/web-out.xml"
|
||||
basedir="../jsp/" excludes="web.xml, *.java, *.jsp">
|
||||
</war>
|
||||
</target>
|
||||
<target name="precompilejsp">
|
||||
<mkdir dir="../jsp/WEB-INF/" />
|
||||
<mkdir dir="../jsp/WEB-INF/classes" />
|
||||
<!-- there are various jspc ant tasks, but they all seem a bit flakey -->
|
||||
<java classname="org.apache.jasper.JspC" fork="true" >
|
||||
<classpath>
|
||||
<pathelement location="../../jetty/jettylib/jasper-compiler.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="../../jetty/jettylib/ant.jar" />
|
||||
<pathelement location="build/routerconsole.jar" />
|
||||
</classpath>
|
||||
<arg value="-d" />
|
||||
<arg value="../jsp/WEB-INF/classes" />
|
||||
<arg value="-v9" />
|
||||
<arg value="-p" />
|
||||
<arg value="net.i2p.router.web.jsp" />
|
||||
<arg value="-webinc" />
|
||||
<arg value="../jsp/web-fragment.xml" />
|
||||
<arg value="-webapp" />
|
||||
<arg value="../jsp/" />
|
||||
</java>
|
||||
<javac destdir="../jsp/WEB-INF/classes/" srcdir="../jsp/WEB-INF/classes" includes="*.java">
|
||||
<classpath>
|
||||
<pathelement location="../../jetty/jettylib/jasper-runtime.jar" />
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<pathelement location="build/routerconsole.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
<copy file="../jsp/web.xml" tofile="../jsp/web-out.xml" />
|
||||
<loadfile property="jspc.web.fragment" srcfile="../jsp/web-fragment.xml" />
|
||||
<replace file="../jsp/web-out.xml">
|
||||
<replacefilter token="<!-- precompiled servlets -->" value="${jspc.web.fragment}" />
|
||||
</replace>
|
||||
</target>
|
||||
<target name="javadoc">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/javadoc" />
|
||||
@ -52,6 +86,5 @@
|
||||
<target name="distclean" depends="clean">
|
||||
<!-- router will clean core -->
|
||||
<ant dir="../../../router/java/" target="distclean" />
|
||||
<delete dir="./jetty-4.2.21-min" />
|
||||
</target>
|
||||
</project>
|
||||
|
@ -0,0 +1,74 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the advanced config form and act
|
||||
* upon the values.
|
||||
*
|
||||
*/
|
||||
public class ConfigAdvancedHandler extends FormHandler {
|
||||
private boolean _forceRestart;
|
||||
private boolean _shouldSave;
|
||||
private String _config;
|
||||
|
||||
public void ConfigNetHandler() {
|
||||
_shouldSave = false;
|
||||
_forceRestart = false;
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
if (_shouldSave) {
|
||||
saveChanges();
|
||||
} else {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
public void setShouldsave(String moo) { _shouldSave = true; }
|
||||
public void setRestart(String moo) { _forceRestart = true; }
|
||||
|
||||
public void setConfig(String val) {
|
||||
_config = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user made changes to the config and wants to save them, so
|
||||
* lets go ahead and do so.
|
||||
*
|
||||
*/
|
||||
private void saveChanges() {
|
||||
if (_config != null) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(_config.getBytes())));
|
||||
String line = null;
|
||||
try {
|
||||
while ( (line = reader.readLine()) != null) {
|
||||
int eq = line.indexOf('=');
|
||||
if (eq == -1) continue;
|
||||
if (eq >= line.length() - 1) continue;
|
||||
String key = line.substring(0, eq).trim();
|
||||
String val = line.substring(eq + 1).trim();
|
||||
_context.router().setConfigSetting(key, val);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
addFormError("Error updating the configuration (IOERROR) - please see the error logs");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean saved = _context.router().saveConfig();
|
||||
if (saved)
|
||||
addFormNotice("Configuration saved successfully");
|
||||
else
|
||||
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
|
||||
|
||||
if (_forceRestart) {
|
||||
addFormNotice("Performing a soft restart");
|
||||
_context.router().restart();
|
||||
addFormNotice("Soft restart complete");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import net.i2p.router.ClientTunnelSettings;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the client config form and act
|
||||
* upon the values.
|
||||
*
|
||||
*/
|
||||
public class ConfigClientsHandler extends FormHandler {
|
||||
private String _numClients;
|
||||
private String _numTunnels;
|
||||
private String _numHops;
|
||||
private boolean _shouldSave;
|
||||
|
||||
public void ConfigNetHandler() {
|
||||
_shouldSave = false;
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
if (_shouldSave) {
|
||||
saveChanges();
|
||||
} else {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
public void setShouldsave(String moo) { _shouldSave = true; }
|
||||
|
||||
public void setClientcount(String num) {
|
||||
_numClients = (num != null ? num.trim(): null);
|
||||
}
|
||||
public void setTunnelcount(String num) {
|
||||
_numTunnels = (num != null ? num.trim() : null);
|
||||
}
|
||||
public void setTunneldepth(String num) {
|
||||
_numHops = (num != null ? num.trim() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The user made changes to the network config and wants to save them, so
|
||||
* lets go ahead and do so.
|
||||
*
|
||||
*/
|
||||
private void saveChanges() {
|
||||
boolean saveRequired = false;
|
||||
|
||||
if ( (_numClients != null) && (_numClients.length() > 0) ) {
|
||||
_context.router().setConfigSetting("router.targetClients", _numClients);
|
||||
addFormNotice("Updating estimated number of clients to " + _numClients);
|
||||
saveRequired = true;
|
||||
}
|
||||
|
||||
if ( (_numTunnels != null) && (_numTunnels.length() > 0) ) {
|
||||
_context.router().setConfigSetting(ClientTunnelSettings.PROP_NUM_INBOUND, _numTunnels);
|
||||
addFormNotice("Updating default number of tunnels per client to " + _numTunnels);
|
||||
saveRequired = true;
|
||||
}
|
||||
|
||||
if ( (_numHops != null) && (_numHops.length() > 0) ) {
|
||||
_context.router().setConfigSetting(ClientTunnelSettings.PROP_DEPTH_INBOUND, _numHops);
|
||||
addFormNotice("Updating default tunnel length to " + _numHops);
|
||||
saveRequired = true;
|
||||
}
|
||||
|
||||
if (saveRequired) {
|
||||
boolean saved = _context.router().saveConfig();
|
||||
if (saved)
|
||||
addFormNotice("Configuration saved successfully");
|
||||
else
|
||||
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Properties;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the logging config form and act
|
||||
* upon the values.
|
||||
*
|
||||
*/
|
||||
public class ConfigLoggingHandler extends FormHandler {
|
||||
private boolean _shouldSave;
|
||||
private String _levels;
|
||||
private String _defaultLevel;
|
||||
private String _filename;
|
||||
private String _recordFormat;
|
||||
private String _dateFormat;
|
||||
private String _fileSize;
|
||||
|
||||
public void ConfigNetHandler() {
|
||||
_shouldSave = false;
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
if (_shouldSave) {
|
||||
saveChanges();
|
||||
} else {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
public void setShouldsave(String moo) { _shouldSave = true; }
|
||||
|
||||
public void setLevels(String levels) {
|
||||
_levels = (levels != null ? levels.trim() : null);
|
||||
}
|
||||
public void setDefaultloglevel(String level) {
|
||||
_defaultLevel = (level != null ? level.trim() : null);
|
||||
}
|
||||
public void setLogfilename(String filename) {
|
||||
_filename = (filename != null ? filename.trim() : null);
|
||||
}
|
||||
public void setLogformat(String format) {
|
||||
_recordFormat = (format != null ? format.trim() : null);
|
||||
}
|
||||
public void setLogdateformat(String format) {
|
||||
_dateFormat = (format != null ? format.trim() : null);
|
||||
}
|
||||
public void setLogfilesize(String size) {
|
||||
_fileSize = (size != null ? size.trim() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The user made changes to the config and wants to save them, so
|
||||
* lets go ahead and do so.
|
||||
*
|
||||
*/
|
||||
private void saveChanges() {
|
||||
boolean shouldSave = false;
|
||||
|
||||
if (_levels != null) {
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
props.load(new ByteArrayInputStream(_levels.getBytes()));
|
||||
_context.logManager().setLimits(props);
|
||||
shouldSave = true;
|
||||
addFormNotice("Log limits updated");
|
||||
} catch (IOException ioe) {
|
||||
_context.logManager().getLog(ConfigLoggingHandler.class).error("Error reading from the props?", ioe);
|
||||
addFormError("Error updating the log limits - levels not valid");
|
||||
}
|
||||
} else {
|
||||
_context.logManager().setLimits(null);
|
||||
addFormNotice("Log limits cleared");
|
||||
}
|
||||
|
||||
if (_defaultLevel != null) {
|
||||
String oldDefault = _context.logManager().getDefaultLimit();
|
||||
if (_defaultLevel.equals(oldDefault)) {
|
||||
// noop
|
||||
} else {
|
||||
shouldSave = true;
|
||||
_context.logManager().setDefaultLimit(_defaultLevel);
|
||||
addFormNotice("Default log level updated from " + oldDefault + " to " + _defaultLevel);
|
||||
}
|
||||
}
|
||||
|
||||
if (_dateFormat != null) {
|
||||
boolean valid = _context.logManager().setDateFormat(_dateFormat);
|
||||
if (valid) {
|
||||
shouldSave = true;
|
||||
addFormNotice("Date format updated");
|
||||
} else {
|
||||
addFormError("Specified date format is not valid (" + _dateFormat + ") - not updated");
|
||||
}
|
||||
}
|
||||
|
||||
if (_fileSize != null) {
|
||||
int newBytes = _context.logManager().getFileSize(_fileSize);
|
||||
int oldBytes = _context.logManager().getFileSize();
|
||||
if (newBytes > 0) {
|
||||
if (oldBytes != newBytes) {
|
||||
_context.logManager().setFileSize(newBytes);
|
||||
shouldSave = true;
|
||||
addFormNotice("File size updated");
|
||||
}
|
||||
} else {
|
||||
addFormError("Specified file size limit is not valid (" + _fileSize + ") - not updated");
|
||||
}
|
||||
}
|
||||
|
||||
if ( (_filename != null) && (_filename.trim().length() > 0) ) {
|
||||
_filename = _filename.trim();
|
||||
String old = _context.logManager().getBaseLogfilename();
|
||||
if ( (old != null) && (_filename.equals(old)) ) {
|
||||
// noop - don't update since its the same
|
||||
} else {
|
||||
shouldSave = true;
|
||||
_context.logManager().setBaseLogfilename(_filename);
|
||||
addFormNotice("Log file name pattern updated to " + _filename
|
||||
+ " (note: will not take effect until next rotation)");
|
||||
}
|
||||
}
|
||||
|
||||
if ( (_recordFormat != null) && (_recordFormat.trim().length() > 0) ) {
|
||||
_recordFormat = _recordFormat.trim();
|
||||
String old = new String(_context.logManager().getFormat());
|
||||
if (_recordFormat.equalsIgnoreCase(old)) {
|
||||
// noop - no change
|
||||
} else {
|
||||
char fmt[] = new char[_recordFormat.length()];
|
||||
for (int i = 0; i < fmt.length; i++)
|
||||
fmt[i] = _recordFormat.charAt(i);
|
||||
_context.logManager().setFormat(fmt);
|
||||
shouldSave = true;
|
||||
addFormNotice("Log record format updated");
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSave) {
|
||||
boolean saved = _context.logManager().saveConfig();
|
||||
|
||||
if (saved)
|
||||
addFormNotice("Log configuration saved and applied successfully");
|
||||
else
|
||||
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
|
||||
}
|
||||
}
|
||||
}
|
@ -2,9 +2,10 @@ package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeMap;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@ -49,65 +50,49 @@ public class ConfigLoggingHelper {
|
||||
}
|
||||
public String getLogLevelTable() {
|
||||
StringBuffer buf = new StringBuffer(32*1024);
|
||||
buf.append("<textarea rows=\"20\" cols=\"80\">");
|
||||
List logs = _context.logManager().getLogs();
|
||||
TreeMap sortedLogs = new TreeMap();
|
||||
for (int i = 0; i < logs.size(); i++) {
|
||||
Log l = (Log)logs.get(i);
|
||||
sortedLogs.put(l.getName(), l);
|
||||
Properties limits = _context.logManager().getLimits();
|
||||
TreeSet sortedLogs = new TreeSet();
|
||||
for (Iterator iter = limits.keySet().iterator(); iter.hasNext(); ) {
|
||||
String prefix = (String)iter.next();
|
||||
sortedLogs.add(prefix);
|
||||
}
|
||||
int i = 0;
|
||||
for (Iterator iter = sortedLogs.values().iterator(); iter.hasNext(); i++) {
|
||||
Log l = (Log)iter.next();
|
||||
buf.append(l.getName()).append('=');
|
||||
buf.append(Log.toLevelString(l.getMinimumPriority()));
|
||||
buf.append("\n");
|
||||
|
||||
buf.append("<textarea name=\"levels\" rows=\"20\" cols=\"70\">");
|
||||
for (Iterator iter = sortedLogs.iterator(); iter.hasNext(); ) {
|
||||
String prefix = (String)iter.next();
|
||||
String level = limits.getProperty(prefix);
|
||||
buf.append(prefix).append('=').append(level).append('\n');
|
||||
}
|
||||
buf.append("</textarea><br />\n");
|
||||
buf.append("<i>Valid levels are DEBUG, INFO, WARN, ERROR, CRIT</i>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
public String getLogLevelTableDetail() {
|
||||
StringBuffer buf = new StringBuffer(8*1024);
|
||||
buf.append("<table border=\"1\">\n");
|
||||
buf.append("<tr><td>Package/class</td><td>Level</td></tr>\n");
|
||||
List logs = _context.logManager().getLogs();
|
||||
TreeMap sortedLogs = new TreeMap();
|
||||
for (int i = 0; i < logs.size(); i++) {
|
||||
Log l = (Log)logs.get(i);
|
||||
sortedLogs.put(l.getName(), l);
|
||||
}
|
||||
int i = 0;
|
||||
for (Iterator iter = sortedLogs.values().iterator(); iter.hasNext(); i++) {
|
||||
Log l = (Log)iter.next();
|
||||
buf.append("<tr>\n <td><input size=\"50\" type=\"text\" name=\"logrecord.");
|
||||
buf.append(i).append(".package\" value=\"").append(l.getName());
|
||||
buf.append("\" /></td>\n");
|
||||
buf.append("<td><select name=\"logrecord.").append(i);
|
||||
buf.append(".level\">\n\t");
|
||||
buf.append("<option value=\"DEBUG\" ");
|
||||
if (l.getMinimumPriority() == Log.DEBUG)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Debug</option>\n\t");
|
||||
buf.append("<option value=\"INFO\" ");
|
||||
if (l.getMinimumPriority() == Log.INFO)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Info</option>\n\t");
|
||||
buf.append("<option value=\"WARN\" ");
|
||||
if (l.getMinimumPriority() == Log.WARN)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Warn</option>\n\t");
|
||||
buf.append("<option value=\"ERROR\" ");
|
||||
if (l.getMinimumPriority() == Log.ERROR)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Error</option>\n\t");
|
||||
buf.append("<option value=\"CRIT\" ");
|
||||
if (l.getMinimumPriority() == Log.CRIT)
|
||||
buf.append("selected=\"true\" ");
|
||||
buf.append(">Critical</option>\n\t");
|
||||
buf.append("</select></td>\n</tr>\n");
|
||||
}
|
||||
buf.append("</table>\n");
|
||||
public String getDefaultLogLevelBox() {
|
||||
String cur = _context.logManager().getDefaultLimit();
|
||||
StringBuffer buf = new StringBuffer(128);
|
||||
buf.append("<select name=\"defaultloglevel\">\n");
|
||||
|
||||
buf.append("<option value=\"DEBUG\" ");
|
||||
if ("DEBUG".equals(cur)) buf.append(" selected=\"true\" ");
|
||||
buf.append(">DEBUG</option>\n");
|
||||
|
||||
buf.append("<option value=\"INFO\" ");
|
||||
if ("INFO".equals(cur)) buf.append(" selected=\"true\" ");
|
||||
buf.append(">INFO</option>\n");
|
||||
|
||||
buf.append("<option value=\"WARN\" ");
|
||||
if ("WARN".equals(cur)) buf.append(" selected=\"true\" ");
|
||||
buf.append(">WARN</option>\n");
|
||||
|
||||
buf.append("<option value=\"ERROR\" ");
|
||||
if ("ERROR".equals(cur)) buf.append(" selected=\"true\" ");
|
||||
buf.append(">ERROR</option>\n");
|
||||
|
||||
buf.append("<option value=\"CRIT\" ");
|
||||
if ("CRIT".equals(cur)) buf.append(" selected=\"true\" ");
|
||||
buf.append(">CRIT</option>\n");
|
||||
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,292 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.ClientTunnelSettings;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the main config form and act
|
||||
* upon the values.
|
||||
*
|
||||
*/
|
||||
public class ConfigNetHandler extends FormHandler {
|
||||
private String _hostname;
|
||||
private boolean _guessRequested;
|
||||
private boolean _reseedRequested;
|
||||
private boolean _saveRequested;
|
||||
private boolean _timeSyncEnabled;
|
||||
private String _port;
|
||||
private String _inboundRate;
|
||||
private String _inboundBurst;
|
||||
private String _outboundRate;
|
||||
private String _outboundBurst;
|
||||
private String _reseedFrom;
|
||||
|
||||
public void ConfigNetHandler() {
|
||||
_guessRequested = false;
|
||||
_reseedRequested = false;
|
||||
_saveRequested = false;
|
||||
_timeSyncEnabled = false;
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
if (_guessRequested) {
|
||||
guessHostname();
|
||||
} else if (_reseedRequested) {
|
||||
reseed();
|
||||
} else if (_saveRequested) {
|
||||
saveChanges();
|
||||
} else {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
public void setGuesshost(String moo) { _guessRequested = true; }
|
||||
public void setReseed(String moo) { _reseedRequested = true; }
|
||||
public void setSave(String moo) { _saveRequested = true; }
|
||||
public void setEnabletimesync(String moo) { _timeSyncEnabled = true; }
|
||||
|
||||
public void setHostname(String hostname) {
|
||||
_hostname = (hostname != null ? hostname.trim() : null);
|
||||
}
|
||||
public void setPort(String port) {
|
||||
_port = (port != null ? port.trim() : null);
|
||||
}
|
||||
public void setInboundrate(String rate) {
|
||||
_inboundRate = (rate != null ? rate.trim() : null);
|
||||
}
|
||||
public void setInboundburstfactor(String factor) {
|
||||
_inboundBurst = (factor != null ? factor.trim() : null);
|
||||
}
|
||||
public void setOutboundrate(String rate) {
|
||||
_outboundRate = (rate != null ? rate.trim() : null);
|
||||
}
|
||||
public void setOutboundburstfactor(String factor) {
|
||||
_outboundBurst = (factor != null ? factor.trim() : null);
|
||||
}
|
||||
public void setReseedfrom(String url) {
|
||||
_reseedFrom = (url != null ? url.trim() : null);
|
||||
}
|
||||
|
||||
private static final String IP_PREFIX = "<h1>Your IP is ";
|
||||
private static final String IP_SUFFIX = " <br></h1>";
|
||||
private void guessHostname() {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
URL url = new URL("http://www.whatismyip.com/");
|
||||
URLConnection con = url.openConnection();
|
||||
con.connect();
|
||||
reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
|
||||
String line = null;
|
||||
while ( (line = reader.readLine()) != null) {
|
||||
if (line.startsWith(IP_PREFIX)) {
|
||||
int end = line.indexOf(IP_SUFFIX);
|
||||
if (end == -1) {
|
||||
addFormError("Unable to guess the host (BAD_SUFFIX)");
|
||||
return;
|
||||
}
|
||||
String ip = line.substring(IP_PREFIX.length(), end);
|
||||
addFormNotice("Host guess: " + ip);
|
||||
return;
|
||||
}
|
||||
}
|
||||
addFormError("Unable to guess the host (NO_PREFIX)");
|
||||
} catch (IOException ioe) {
|
||||
addFormError("Unable to guess the host (IO_ERROR)");
|
||||
_context.logManager().getLog(ConfigNetHandler.class).error("Unable to guess the host", ioe);
|
||||
} finally {
|
||||
if (reader != null) try { reader.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
private static final String DEFAULT_SEED_URL = "http://dev.i2p.net/i2pdb/";
|
||||
/**
|
||||
* Reseed has been requested, so lets go ahead and do it. Fetch all of
|
||||
* the routerInfo-*.dat files from the specified URL (or the default) and
|
||||
* save them into this router's netDb dir.
|
||||
*
|
||||
*/
|
||||
private void reseed() {
|
||||
String seedURL = DEFAULT_SEED_URL;
|
||||
if (_reseedFrom != null)
|
||||
seedURL = _reseedFrom;
|
||||
try {
|
||||
URL dir = new URL(seedURL);
|
||||
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;
|
||||
}
|
||||
|
||||
int fetched = 0;
|
||||
for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
|
||||
fetchSeed(seedURL, (String)iter.next());
|
||||
fetched++;
|
||||
}
|
||||
addFormNotice("Reseeded with " + fetched + " peers");
|
||||
} catch (Throwable t) {
|
||||
_context.logManager().getLog(ConfigNetHandler.class).error("Error reseeding", t);
|
||||
addFormError("Error reseeding (RESEED_EXCEPTION)");
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchSeed(String seedURL, String peer) throws Exception {
|
||||
URL url = new URL(seedURL + "/routerInfo-" + peer + ".dat");
|
||||
|
||||
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 {
|
||||
// props taken from KademliaNetworkDatabaseFacade...
|
||||
String dirName = _context.getProperty("router.networkDatabase.dbDir", "netDb");
|
||||
File netDbDir = new File(dirName);
|
||||
if (!netDbDir.exists()) {
|
||||
boolean ok = netDbDir.mkdirs();
|
||||
if (ok)
|
||||
addFormNotice("Network database directory created: " + dirName);
|
||||
else
|
||||
addFormNotice("Error creating network database directory: " + dirName);
|
||||
}
|
||||
FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat"));
|
||||
fos.write(data);
|
||||
fos.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* The user made changes to the network config and wants to save them, so
|
||||
* lets go ahead and do so.
|
||||
*
|
||||
*/
|
||||
private void saveChanges() {
|
||||
boolean restartRequired = false;
|
||||
|
||||
if ( (_hostname != null) && (_hostname.length() > 0) ) {
|
||||
String oldHost = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_HOSTNAME);
|
||||
if ( (oldHost == null) || (!oldHost.equalsIgnoreCase(_hostname)) ) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_HOSTNAME, _hostname);
|
||||
addFormNotice("Updating hostname from " + oldHost + " to " + _hostname);
|
||||
restartRequired = true;
|
||||
}
|
||||
}
|
||||
if ( (_port != null) && (_port.length() > 0) ) {
|
||||
String oldPort = _context.router().getConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT);
|
||||
if ( (oldPort == null) || (!oldPort.equalsIgnoreCase(_port)) ) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_I2NP_TCP_PORT, _port);
|
||||
addFormNotice("Updating TCP port from " + oldPort + " to " + _port);
|
||||
restartRequired = true;
|
||||
}
|
||||
}
|
||||
|
||||
updateRates();
|
||||
|
||||
if (_timeSyncEnabled) {
|
||||
System.setProperty("timestamper.enabled", "true");
|
||||
} else {
|
||||
System.setProperty("timestamper.enabled", "false");
|
||||
}
|
||||
|
||||
boolean saved = _context.router().saveConfig();
|
||||
if (saved)
|
||||
addFormNotice("Configuration saved successfully");
|
||||
else
|
||||
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
|
||||
|
||||
if (restartRequired) {
|
||||
addFormNotice("Performing a soft restart");
|
||||
_context.router().restart();
|
||||
addFormNotice("Soft restart complete");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRates() {
|
||||
boolean updated = false;
|
||||
if ( (_inboundRate != null) && (_inboundRate.length() > 0) ) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_INBOUND_KBPS, _inboundRate);
|
||||
updated = true;
|
||||
}
|
||||
if ( (_outboundRate != null) && (_outboundRate.length() > 0) ) {
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_OUTBOUND_KBPS, _outboundRate);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
String inRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_INBOUND_KBPS);
|
||||
|
||||
if (_inboundBurst != null) {
|
||||
int rateKBps = 0;
|
||||
int burstSeconds = 0;
|
||||
try {
|
||||
rateKBps = Integer.parseInt(inRate);
|
||||
burstSeconds = Integer.parseInt(_inboundBurst);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ignore
|
||||
}
|
||||
if ( (rateKBps > 0) && (burstSeconds > 0) ) {
|
||||
int kb = rateKBps * burstSeconds;
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_INBOUND_BURST, "" + kb);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
String outRate = _context.router().getConfigSetting(ConfigNetHelper.PROP_OUTBOUND_KBPS);
|
||||
|
||||
if (_outboundBurst != null) {
|
||||
int rateKBps = 0;
|
||||
int burstSeconds = 0;
|
||||
try {
|
||||
rateKBps = Integer.parseInt(outRate);
|
||||
burstSeconds = Integer.parseInt(_outboundBurst);
|
||||
} catch (NumberFormatException nfe) {
|
||||
// ignore
|
||||
}
|
||||
if ( (rateKBps > 0) && (burstSeconds > 0) ) {
|
||||
int kb = rateKBps * burstSeconds;
|
||||
_context.router().setConfigSetting(ConfigNetHelper.PROP_OUTBOUND_BURST, "" + kb);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updated)
|
||||
addFormNotice("Updated bandwidth limits");
|
||||
}
|
||||
}
|
@ -30,8 +30,8 @@ public class ConfigNetHelper {
|
||||
public ConfigNetHelper() {}
|
||||
|
||||
/** copied from various private TCP components */
|
||||
private final static String PROP_I2NP_TCP_HOSTNAME = "i2np.tcp.hostname";
|
||||
private final static String PROP_I2NP_TCP_PORT = "i2np.tcp.port";
|
||||
public final static String PROP_I2NP_TCP_HOSTNAME = "i2np.tcp.hostname";
|
||||
public final static String PROP_I2NP_TCP_PORT = "i2np.tcp.port";
|
||||
|
||||
public String getHostname() {
|
||||
return _context.getProperty(PROP_I2NP_TCP_HOSTNAME);
|
||||
@ -74,7 +74,7 @@ public class ConfigNetHelper {
|
||||
if (rate != null)
|
||||
return rate;
|
||||
else
|
||||
return "Unlimited";
|
||||
return "-1";
|
||||
}
|
||||
public String getInboundBurstFactorBox() {
|
||||
String rate = _context.getProperty(PROP_INBOUND_KBPS);
|
||||
|
109
apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java
Normal file
109
apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java
Normal file
@ -0,0 +1,109 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.ClientTunnelSettings;
|
||||
|
||||
/**
|
||||
* Simple form handler base class - does not depend on servlets or jsp,
|
||||
* but instead the subclasses are populated with javabean properties. e.g.
|
||||
* <jsp:setProperty name="handler" property="*" />
|
||||
*
|
||||
* The form is "processed" after the properties are set and the first output
|
||||
* property is retrieved - either getNotices() or getErrors().
|
||||
*
|
||||
*/
|
||||
public class FormHandler {
|
||||
protected RouterContext _context;
|
||||
private List _errors;
|
||||
private List _notices;
|
||||
private boolean _processed;
|
||||
|
||||
public FormHandler() {
|
||||
_errors = new ArrayList();
|
||||
_notices = new ArrayList();
|
||||
_processed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure this bean to query a particular router context
|
||||
*
|
||||
* @param contextId begging few characters of the routerHash, or null to pick
|
||||
* the first one we come across.
|
||||
*/
|
||||
public void setContextId(String contextId) {
|
||||
try {
|
||||
_context = ContextHelper.getContext(contextId);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this to perform the final processing (in turn, adding formNotice
|
||||
* and formError messages, etc)
|
||||
*
|
||||
*/
|
||||
protected void processForm() {}
|
||||
|
||||
/**
|
||||
* Add an error message to display
|
||||
*/
|
||||
protected void addFormError(String errorMsg) {
|
||||
if (errorMsg == null) return;
|
||||
_errors.add(errorMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a non-error message to display
|
||||
*/
|
||||
protected void addFormNotice(String msg) {
|
||||
if (msg == null) return;
|
||||
_notices.add(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display any error messages (processing the form if it hasn't
|
||||
* been yet)
|
||||
*
|
||||
*/
|
||||
public String getErrors() {
|
||||
return render(_errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display any non-error messages (processing the form if it hasn't
|
||||
* been yet)
|
||||
*
|
||||
*/
|
||||
public String getNotices() {
|
||||
return render(_notices);
|
||||
}
|
||||
|
||||
private String render(List source) {
|
||||
if (!_processed) {
|
||||
processForm();
|
||||
_processed = true;
|
||||
}
|
||||
if (source.size() <= 0) {
|
||||
return "";
|
||||
} else if (source.size() == 1) {
|
||||
return (String)source.get(0);
|
||||
} else {
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("<ul>\n");
|
||||
for (int i = 0; i < source.size(); i++) {
|
||||
buf.append("<li>");
|
||||
buf.append((String)source.get(i));
|
||||
buf.append("</li>\n");
|
||||
}
|
||||
buf.append("</ul>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
@ -301,11 +303,25 @@ public class SummaryHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* How many client destinations are connected locally.
|
||||
*
|
||||
* @return html section summary
|
||||
*/
|
||||
public String getDestinations() {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
|
||||
try {
|
||||
_context.clientManager().renderStatusHTML(baos);
|
||||
return new String(baos.toByteArray());
|
||||
} catch (IOException ioe) {
|
||||
_context.logManager().getLog(SummaryHelper.class).error("Error rendering client info", ioe);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* How many free inbound tunnels we have.
|
||||
*
|
||||
* @param contextId begging few characters of the routerHash, or null to pick
|
||||
* the first one we come across.
|
||||
*/
|
||||
public int getInboundTunnels() {
|
||||
if (_context == null)
|
||||
|
@ -1,5 +1,4 @@
|
||||
<%@page contentType="text/html"%>
|
||||
<%@page pageEncoding="UTF-8"%>
|
||||
<%@page contentType="text/html" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
@ -9,13 +8,19 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigNetHelper" id="nethelper" scope="request" />
|
||||
<jsp:setProperty name="nethelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<div class="main" id="main">
|
||||
<%@include file="confignav.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigNetHandler" id="formhandler" scope="request" />
|
||||
<jsp:setProperty name="formhandler" property="*" />
|
||||
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
|
||||
<i><jsp:getProperty name="formhandler" property="notices" /></i>
|
||||
|
||||
<form action="config.jsp" method="POST">
|
||||
<b>External hostname/IP address:</b>
|
||||
<input name="hostname" type="text" size="32" value="<jsp:getProperty name="nethelper" property="hostname" />" />
|
||||
@ -29,7 +34,10 @@
|
||||
to <a href="http://www.whatismyip.com/">www.whatismyip.com</a>.</i>
|
||||
<hr />
|
||||
<b>Enable internal time synchronization?</b> <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br />
|
||||
<i>If disabled, your machine <b>must</b> be NTP synchronized</i>
|
||||
<i>If disabled, your machine <b>must</b> be NTP synchronized. This option only
|
||||
takes effect for the current run - if your machine is always synchronized within
|
||||
(a few seconds), you can update your configuration so that it doesn't start the
|
||||
"Timestamper" app (which would make this option irrelevent)</i>
|
||||
<hr />
|
||||
<b>Bandwidth limiter</b><br />
|
||||
<b>Inbound rate</b>:
|
||||
@ -44,8 +52,12 @@
|
||||
<hr />
|
||||
<b>Reseed</b> (from <input name="reseedfrom" type="text" size="40" value="http://dev.i2p.net/i2pdb/" />):
|
||||
<input type="submit" name="reseed" value="now" /><br />
|
||||
<i>May take some time to download the peer references</i>
|
||||
<hr />
|
||||
<input type="submit" value="Save changes" /> <input type="reset" value="Cancel" />
|
||||
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
|
||||
<i>Changing the hostname or TCP port will force a 'soft restart' - dropping your connections
|
||||
and clients as if the router was stopped and restarted. <b>Please be patient</b> - it may take
|
||||
a few seconds to complete.</i>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -9,16 +9,25 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigAdvancedHelper" id="advancedhelper" scope="request" />
|
||||
<jsp:setProperty name="advancedhelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<div class="main" id="main">
|
||||
<%@include file="confignav.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigAdvancedHandler" id="formhandler" scope="request" />
|
||||
<jsp:setProperty name="formhandler" property="*" />
|
||||
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
|
||||
<i><jsp:getProperty name="formhandler" property="notices" /></i>
|
||||
|
||||
<form action="configadvanced.jsp" method="POST">
|
||||
<textarea rows="20" cols="80" name="config"><jsp:getProperty name="advancedhelper" property="settings" /></textarea><br />
|
||||
<input type="submit" value="Apply" /> <input type="reset" value="Cancel" />
|
||||
<input type="submit" name="shouldsave" value="Apply" /> <input type="reset" value="Cancel" /> <br />
|
||||
<b>Force restart:</b> <input type="checkbox" name="restart" value="force" /> <i>(specify this
|
||||
if the changes made above require the router to reset itself - e.g. you are updating TCP ports
|
||||
or hostnames, etc)</i>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -9,13 +9,19 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigClientsHelper" id="clientshelper" scope="request" />
|
||||
<jsp:setProperty name="clientshelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<div class="main" id="main">
|
||||
<%@include file="confignav.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigClientsHandler" id="formhandler" scope="request" />
|
||||
<jsp:setProperty name="formhandler" property="*" />
|
||||
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
|
||||
<i><jsp:getProperty name="formhandler" property="notices" /></i>
|
||||
|
||||
<form action="configclients.jsp" method="POST">
|
||||
<b>Estimated number of clients/destinations:</b>
|
||||
<jsp:getProperty name="clientshelper" property="clientCountSelectBox" /><br />
|
||||
@ -24,7 +30,7 @@
|
||||
<b>Default number of hops per tunnel:</b>
|
||||
<jsp:getProperty name="clientshelper" property="tunnelDepthSelectBox" /><br />
|
||||
<hr />
|
||||
<input type="submit" value="Save changes" /> <input type="reset" value="Cancel" />
|
||||
<input type="submit" name="shouldsave" value="Save changes" /> <input type="reset" value="Cancel" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -11,10 +11,16 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<%@include file="confignav.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigLoggingHandler" id="formhandler" scope="request" />
|
||||
<jsp:setProperty name="formhandler" property="*" />
|
||||
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
|
||||
<i><jsp:getProperty name="formhandler" property="notices" /></i>
|
||||
|
||||
<form action="configlogging.jsp" method="POST">
|
||||
<b>Logging filename:</b>
|
||||
<input type="text" name="logfilename" size="40" value="<jsp:getProperty name="logginghelper" property="logFilePattern" />" /><br />
|
||||
@ -29,9 +35,12 @@
|
||||
<input type="text" name="logfilesize" size="4" value="<jsp:getProperty name="logginghelper" property="maxFileSize" />" /><br />
|
||||
<hr />
|
||||
<b>Log levels:</b> <br />
|
||||
<b>Default log level:</b>
|
||||
<jsp:getProperty name="logginghelper" property="defaultLogLevelBox" /><br />
|
||||
<jsp:getProperty name="logginghelper" property="logLevelTable" />
|
||||
<hr />
|
||||
<input type="submit" value="Apply changes" /> <input type="submit" value="Apply and Save" /> <input type="reset" value="Cancel" />
|
||||
<input type="submit" name="shouldsave" value="Save changes" />
|
||||
<input type="reset" value="Cancel" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
hmm. we should probably have some help text here.<br />
|
||||
|
2
apps/routerconsole/jsp/index.html
Normal file
2
apps/routerconsole/jsp/index.html
Normal file
@ -0,0 +1,2 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>I2P Router Console</title></head>
|
||||
<body><meta http-equiv="refresh" content="0;url=index.jsp" /><a href="index.jsp">Enter</a></body></html>
|
@ -9,7 +9,6 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<h2>Welcome to your router console</h2>
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<jsp:useBean class="net.i2p.router.web.LogsHelper" id="logsHelper" scope="request" />
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<jsp:useBean class="net.i2p.router.web.NetDbHelper" id="netdbHelper" scope="request" />
|
||||
|
@ -1 +0,0 @@
|
||||
<%=(null != request.getParameter("i2p.console.notice") ? request.getParameter("i2p.console.notice") : "")%>
|
@ -9,7 +9,6 @@
|
||||
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
<%@include file="notice.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<jsp:useBean class="net.i2p.router.web.ProfilesHelper" id="profilesHelper" scope="request" />
|
||||
|
@ -25,6 +25,8 @@
|
||||
<b>Used:</b> <jsp:getProperty name="helper" property="inboundTransferred" />/<jsp:getProperty name="helper" property="outboundTransferred" /><br />
|
||||
<hr />
|
||||
|
||||
<jsp:getProperty name="helper" property="destinations" />
|
||||
|
||||
<u><b>Tunnels</b></u><br />
|
||||
<b>Inbound:</b> <jsp:getProperty name="helper" property="inboundTunnels" /><br />
|
||||
<b>Outbound:</b> <jsp:getProperty name="helper" property="outboundTunnels" /><br />
|
||||
|
@ -4,14 +4,14 @@
|
||||
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
|
||||
|
||||
<web-app>
|
||||
<!-- precompiled servlets -->
|
||||
<session-config>
|
||||
<session-timeout>
|
||||
30
|
||||
</session-timeout>
|
||||
</session-config>
|
||||
<welcome-file-list>
|
||||
<welcome-file>
|
||||
index.jsp
|
||||
</welcome-file>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
</welcome-file-list>
|
||||
</web-app>
|
@ -1,27 +0,0 @@
|
||||
Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of any contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
apps/sam/c/Makefile.common
Normal file
26
apps/sam/c/Makefile.common
Normal file
@ -0,0 +1,26 @@
|
||||
#
|
||||
# This Makefile contains instructions common to all platforms
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Build rules
|
||||
#
|
||||
|
||||
all: clean depend libsam
|
||||
|
||||
depend:
|
||||
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
libsam: $(OBJS)
|
||||
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-$(RM) -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
#
|
||||
# Your operating environment
|
||||
# Your operating system
|
||||
#
|
||||
|
||||
OS = CYGWIN
|
||||
@ -23,12 +23,13 @@ SRCDIR = src
|
||||
|
||||
AR = ar
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -DOS=$(OS)
|
||||
CFLAGS += -I$(INCDIR)
|
||||
|
||||
@ -41,25 +42,7 @@ OBJS = $(OBJDIR)/sam.o \
|
||||
$(OBJDIR)/strl.o
|
||||
|
||||
#
|
||||
# Build rules
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
all: depend libsam
|
||||
|
||||
depend:
|
||||
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
libsam: $(OBJS)
|
||||
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
|
||||
|
||||
tidy: clean
|
||||
include Makefile.common
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make (gmake) and should work on FreeBSD
|
||||
# This Makefile is compatible with GNU Make and should work on FreeBSD
|
||||
#
|
||||
|
||||
#
|
||||
@ -23,6 +23,7 @@ SRCDIR = src
|
||||
|
||||
AR = ar
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
@ -39,25 +40,7 @@ CFLAGS += -I$(INCDIR)
|
||||
OBJS = $(OBJDIR)/sam.o
|
||||
|
||||
#
|
||||
# Build rules
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
all: depend libsam
|
||||
|
||||
depend:
|
||||
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
libsam: $(OBJS)
|
||||
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
|
||||
|
||||
tidy: clean
|
||||
include Makefile.common
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make and should work on Linux (generic)
|
||||
# This Makefile is compatible with GNU Make and should work on Linux
|
||||
#
|
||||
|
||||
#
|
||||
@ -23,6 +23,7 @@ SRCDIR = src
|
||||
|
||||
AR = ar
|
||||
CC = gcc
|
||||
RM = rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
@ -40,25 +41,7 @@ OBJS = $(OBJDIR)/sam.o \
|
||||
$(OBJDIR)/strl.o
|
||||
|
||||
#
|
||||
# Build rules
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
all: depend libsam
|
||||
|
||||
depend:
|
||||
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
libsam: $(OBJS)
|
||||
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
|
||||
|
||||
tidy: clean
|
||||
include Makefile.common
|
||||
|
@ -23,12 +23,13 @@ SRCDIR = src
|
||||
|
||||
AR = C:\Dev-Cpp\bin\ar
|
||||
CC = C:\Dev-Cpp\bin\gcc
|
||||
RM = C:\Dev-Cpp\bin\rm
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -march=i486 -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -DOS=$(OS)
|
||||
CFLAGS += -I$(INCDIR)
|
||||
|
||||
@ -40,25 +41,7 @@ OBJS = $(OBJDIR)/sam.o \
|
||||
$(OBJDIR)/strl.o
|
||||
|
||||
#
|
||||
# Build rules
|
||||
# Include the make instructions common to all platforms
|
||||
#
|
||||
|
||||
all: depend libsam
|
||||
|
||||
depend:
|
||||
$(CC) $(CFLAGS) -MM $(SRCDIR)/*.c > .depend
|
||||
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $<
|
||||
|
||||
libsam: $(OBJS)
|
||||
$(AR) rcs $(LIBDIR)/libsam.a $(OBJS)
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f $(LIBDIR)/libsam.a $(OBJDIR)/* .depend
|
||||
|
||||
tidy: clean
|
||||
include Makefile.common
|
||||
|
1
apps/sam/c/README
Normal file
1
apps/sam/c/README
Normal file
@ -0,0 +1 @@
|
||||
See the `docs' directory for documentation and license.
|
3
apps/sam/c/doc/contact.txt
Normal file
3
apps/sam/c/doc/contact.txt
Normal file
@ -0,0 +1,3 @@
|
||||
The author is Matthew P. Cashdollar, who can be reached via email at
|
||||
mpc@innographx.com. The official LibSAM distribution site is at
|
||||
http://www.cashdollar.org/libsam
|
@ -5,5 +5,4 @@ I need to do these things:
|
||||
|
||||
Anyone can help with these things:
|
||||
|
||||
* Fix the example dgram-client.c
|
||||
* Compile on as many platforms as possible
|
||||
|
@ -1,3 +1,15 @@
|
||||
/* vi:set ts=4: */
|
||||
|
||||
v1.25 2004-07-31
|
||||
* Created I2P-Ping, a new example program (it's a clone of I2Ping). Works
|
||||
on Posix only, because it uses getopt().
|
||||
* Removed the old broken examples and added more comments to
|
||||
warhammer-dgram.c
|
||||
* Added support for sessions - now LibSAM can have multiple SAM sessions
|
||||
going at once (with different destinations)
|
||||
* Rewrote sendq functions to automatically send big packets, for better
|
||||
network performance (still considered experimental)
|
||||
|
||||
v1.20 2004-07-11
|
||||
* Ported to FreeBSD (Makefile.freebsd)
|
||||
* Full winsock compatibility - all Windows functions now return appropriate
|
||||
|
@ -20,17 +20,9 @@ LIBS = -lsam
|
||||
# Build rules
|
||||
#
|
||||
|
||||
all: dgram-client dgram-server warhammer-dgram
|
||||
all: warhammer-dgram
|
||||
|
||||
dgram-client:
|
||||
$(CC) $(CFLAGS) -o dgram-client.o -c dgram-client.c
|
||||
$(CC) $(CFLAGS) -o dgram-client dgram-client.o $(LIBS)
|
||||
|
||||
dgram-server:
|
||||
$(CC) $(CFLAGS) -o dgram-server.o -c dgram-server.c
|
||||
$(CC) $(CFLAGS) -o dgram-server dgram-server.o $(LIBS)
|
||||
|
||||
warhammer-dgram:
|
||||
warhammer-dgram: warhammer-dgram.c
|
||||
$(CC) $(CFLAGS) -o warhammer-dgram.o -c warhammer-dgram.c
|
||||
$(CC) $(CFLAGS) -o warhammer-dgram warhammer-dgram.o $(LIBS)
|
||||
|
||||
@ -39,4 +31,4 @@ warhammer-dgram:
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f *.o *.exe dgram-client dgram-server warhammer-dgram
|
||||
-rm -f *.o *.exe warhammer-dgram
|
||||
|
@ -13,6 +13,7 @@ CC = C:\Dev-Cpp\bin\gcc
|
||||
#
|
||||
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -DWINSOCK
|
||||
CFLAGS += -I../inc -L../lib
|
||||
LIBS = -lsam -lwsock32
|
||||
|
||||
@ -20,15 +21,7 @@ LIBS = -lsam -lwsock32
|
||||
# Build rules
|
||||
#
|
||||
|
||||
all: dgram-client dgram-server warhammer-dgram
|
||||
|
||||
dgram-client:
|
||||
$(CC) $(CFLAGS) -o dgram-client.o -c dgram-client.c
|
||||
$(CC) $(CFLAGS) -o dgram-client dgram-client.o $(LIBS)
|
||||
|
||||
dgram-server:
|
||||
$(CC) $(CFLAGS) -o dgram-server.o -c dgram-server.c
|
||||
$(CC) $(CFLAGS) -o dgram-server dgram-server.o $(LIBS)
|
||||
all: warhammer-dgram
|
||||
|
||||
warhammer-dgram:
|
||||
$(CC) $(CFLAGS) -o warhammer-dgram.o -c warhammer-dgram.c
|
||||
@ -39,4 +32,4 @@ warhammer-dgram:
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f *.o *.exe dgram-client dgram-server warhammer-dgram
|
||||
-rm -f *.o *.exe warhammer-dgram
|
||||
|
@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the author nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sam.h"
|
||||
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size);
|
||||
static void diedback(void);
|
||||
static void logback(char *s);
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
|
||||
/*
|
||||
* This is an extremely simple echo client which shows you how LibSAM
|
||||
* datagrams work. We lookup the name 'dgram-server' then send some data to
|
||||
* him. If everything works, we should get the same data back.
|
||||
*/
|
||||
|
||||
/*
|
||||
* NOTE!!!!!!!! This is currently broken!
|
||||
*/
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
samerr_t rc;
|
||||
|
||||
/* Hook up the callback functions */
|
||||
sam_dgramback = &dgramback;
|
||||
sam_diedback = &diedback;
|
||||
sam_logback = &logback;
|
||||
sam_namingback = &namingback;
|
||||
|
||||
/* Connect to the SAM server -- you can use either an IP or DNS name */
|
||||
rc = sam_connect("localhost", 7656, "dgram-client", SAM_DGRAM, 0);
|
||||
if (rc != SAM_OK) {
|
||||
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
|
||||
exit(1);
|
||||
}
|
||||
/*
|
||||
* This is equivalent to doing a DNS lookup on the normal internet. Note
|
||||
* that the dgram-server must already be running for this to work.
|
||||
* When the lookup completes, we send them some data (see namingback()).
|
||||
*/
|
||||
sam_naming_lookup("dgram-server");
|
||||
|
||||
/*
|
||||
* Wait for something to happen, then invoke the appropriate callback
|
||||
*/
|
||||
while (true)
|
||||
sam_read_buffer();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we receive some data, print it out to the screen
|
||||
*/
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size)
|
||||
{
|
||||
printf("Datagram received: %s\n", (char *)data);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called whenever the SAM connection fails (like if the I2P router is
|
||||
* shut down)
|
||||
*/
|
||||
static void diedback(void)
|
||||
{
|
||||
fprintf(stderr, "Lost SAM connection!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The logging callback prints any logging messages from LibSAM
|
||||
*/
|
||||
static void logback(char *s)
|
||||
{
|
||||
fprintf(stderr, "LibSAM: %s\n", s);
|
||||
}
|
||||
|
||||
/*
|
||||
* When a name is resolved, send data to that destination address
|
||||
*/
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
|
||||
{
|
||||
char *data = "Hello, invisible world!";
|
||||
|
||||
printf("I got %s's base 64 destination, so now I will send him some " \
|
||||
"data...\n", name);
|
||||
sam_dgram_send(pubkey, data, strlen(data));
|
||||
/*
|
||||
* ^^^ An extra NUL is appended to the data by LibSAM, so it is not
|
||||
* necessary to send trailing NULs over the wire for strings. This doesn't
|
||||
* cause problems with binary data, because the NUL isn't included in `size'
|
||||
* in sam_dgramback().
|
||||
* That is why we use strlen(data) instead of strlen(data) + 1.
|
||||
*/
|
||||
puts("Datagram sent");
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the author nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "sam.h"
|
||||
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size);
|
||||
static void diedback(void);
|
||||
static void logback(char *s);
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
|
||||
/*
|
||||
* This is an extremely simple echo server which shows you how LibSAM
|
||||
* datagrams work. We echo back every datagram that is sent to us.
|
||||
*/
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
samerr_t rc;
|
||||
|
||||
/* Hook up the callback functions */
|
||||
sam_dgramback = &dgramback;
|
||||
sam_diedback = &diedback;
|
||||
sam_logback = &logback;
|
||||
sam_namingback = &namingback;
|
||||
|
||||
/* Connect to the SAM server -- you can use either an IP or DNS name */
|
||||
rc = sam_connect("127.0.0.1", 7656, "dgram-server", SAM_DGRAM, 0);
|
||||
if (rc != SAM_OK) {
|
||||
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point we just keep polling the buffer, which causes the
|
||||
* appropriate callbacks to be called whenever something happens
|
||||
*/
|
||||
while (true)
|
||||
sam_read_buffer();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When we receive some data, we just ECHO the exact same data back to them
|
||||
*/
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size)
|
||||
{
|
||||
puts("Echoing datagram");
|
||||
sam_dgram_send(dest, data, size);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called whenever the SAM connection fails (like if the I2P router is
|
||||
* shut down)
|
||||
*/
|
||||
static void diedback(void)
|
||||
{
|
||||
fprintf(stderr, "Lost SAM connection!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The logging callback prints any logging messages from LibSAM
|
||||
*/
|
||||
static void logback(char *s)
|
||||
{
|
||||
fprintf(stderr, "LibSAM: %s\n", s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Not used, but the function has to be in the program anyway
|
||||
*/
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
|
||||
{
|
||||
assert(false); /* we don't do any naming lookups in this program */
|
||||
}
|
38
apps/sam/c/examples/i2p-ping/Makefile
Normal file
38
apps/sam/c/examples/i2p-ping/Makefile
Normal file
@ -0,0 +1,38 @@
|
||||
#
|
||||
# This Makefile is compatible with GNU Make
|
||||
#
|
||||
|
||||
#
|
||||
# Programs
|
||||
#
|
||||
|
||||
CC = gcc
|
||||
INSTALL = install
|
||||
|
||||
#
|
||||
# Flags
|
||||
#
|
||||
|
||||
CFLAGS = -g -O2 -pipe -std=c99 -Wall
|
||||
CFLAGS += -I../../inc -L../../lib
|
||||
LIBS = -lsam
|
||||
|
||||
#
|
||||
# Build rules
|
||||
#
|
||||
|
||||
all: i2p-ping
|
||||
|
||||
i2p-ping: i2p-ping.c
|
||||
$(CC) $(CFLAGS) -o i2p-ping.o -c i2p-ping.c
|
||||
$(CC) $(CFLAGS) -o i2p-ping i2p-ping.o $(LIBS)
|
||||
|
||||
install: i2p-ping
|
||||
$(INSTALL) i2p-ping $(HOME)/bin
|
||||
|
||||
#
|
||||
# Cleanup rules
|
||||
#
|
||||
|
||||
clean:
|
||||
-rm -f *.o *.exe i2p-ping
|
245
apps/sam/c/examples/i2p-ping/i2p-ping.c
Normal file
245
apps/sam/c/examples/i2p-ping/i2p-ping.c
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the author nor the names of any contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include "sam.h"
|
||||
|
||||
static void usage();
|
||||
|
||||
static void closeback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason);
|
||||
static void connectback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
static void databack(sam_sess_t *session, sam_sid_t stream_id, void *data,
|
||||
size_t size);
|
||||
static void diedback(sam_sess_t *session);
|
||||
static void logback(char *s);
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
static void statusback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
|
||||
bool gotdest = false;
|
||||
sam_pubkey_t dest;
|
||||
bool quiet = false;
|
||||
samerr_t laststatus = SAM_NULL;
|
||||
sam_sid_t laststream = 0;
|
||||
bool mihi = false;
|
||||
bool bell = false;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int ch;
|
||||
int count = INT_MAX; /* number of times to ping */
|
||||
char *samhost = "localhost";
|
||||
uint16_t samport = 7656;
|
||||
|
||||
while ((ch = getopt(argc, argv, "ac:h:mp:qv")) != -1) {
|
||||
switch (ch) {
|
||||
case 'a': /* bell */
|
||||
bell = true;
|
||||
break;
|
||||
case 'c': /* packet count */
|
||||
count = atoi(optarg);
|
||||
break;
|
||||
case 'h': /* SAM host */
|
||||
samhost = optarg;
|
||||
break;
|
||||
case 'm': /* I2Ping emulation mode */
|
||||
count = 3;
|
||||
mihi = true;
|
||||
quiet = true;
|
||||
break;
|
||||
case 'p': /* SAM port */
|
||||
samport = atoi(optarg);
|
||||
break;
|
||||
case 'q': /* quiet mode */
|
||||
quiet = true;
|
||||
break;
|
||||
case 'v': /* version */
|
||||
puts("$Id: i2p-ping.c,v 1.2 2004/07/31 22:20:22 mpc Exp $");
|
||||
puts("Copyright (c) 2004, Matthew P. Cashdollar <mpc@innographx.com>");
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc == 0) { /* they forgot to specify a ping target */
|
||||
fprintf(stderr, "Ping who?\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Hook up the callback functions - required by LibSAM */
|
||||
sam_closeback = &closeback;
|
||||
sam_connectback = &connectback;
|
||||
sam_databack = &databack;
|
||||
sam_diedback = &diedback;
|
||||
sam_logback = &logback;
|
||||
sam_namingback = &namingback;
|
||||
sam_statusback = &statusback;
|
||||
|
||||
sam_sess_t *session = NULL; /* set to NULL to have LibSAM do the malloc */
|
||||
session = sam_session_init(session); /* malloc and set defaults */
|
||||
samerr_t rc = sam_connect(session, samhost, samport, "TRANSIENT",
|
||||
SAM_STREAM, 0);
|
||||
if (rc != SAM_OK) {
|
||||
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
|
||||
sam_session_free(&session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int j = 0; j < argc; j++) {
|
||||
if (strlen(argv[j]) == 516) {
|
||||
memcpy(dest, argv[j], SAM_PUBKEY_LEN);
|
||||
gotdest = true;
|
||||
} else
|
||||
sam_naming_lookup(session, argv[j]);
|
||||
|
||||
while (!gotdest) /* just wait for the naming lookup to complete */
|
||||
sam_read_buffer(session);
|
||||
gotdest = false;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
time_t start = time(0);
|
||||
sam_sid_t sid = sam_stream_connect(session, dest);
|
||||
while (laststream != sid && laststatus == SAM_NULL)
|
||||
sam_read_buffer(session); /* wait for the connect */
|
||||
if (laststatus == SAM_OK)
|
||||
sam_stream_close(session, laststream);
|
||||
time_t finish = time(0);
|
||||
laststream = 0;
|
||||
if (laststatus == SAM_OK) {
|
||||
if (bell)
|
||||
printf("\a"); /* putchar() doesn't work for some reason */
|
||||
if (!mihi)
|
||||
printf("%s: %.0fs\n", argv[j], difftime(finish, start));
|
||||
else
|
||||
printf("+ ");
|
||||
} else {
|
||||
if (!mihi)
|
||||
printf("%s: %s\n", argv[j], sam_strerror(laststatus));
|
||||
else
|
||||
printf("- ");
|
||||
}
|
||||
laststatus = SAM_NULL;
|
||||
}
|
||||
if (mihi)
|
||||
printf(" %s\n", argv[j]);
|
||||
}
|
||||
|
||||
sam_close(session);
|
||||
sam_session_free(&session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usage()
|
||||
{
|
||||
puts("usage: i2p-ping [-amqv?] [-c count] [-h samhost] [-p samport] " \
|
||||
"<b64dest|name>\n\t[b64dest|name] [b64dest|name] ...");
|
||||
}
|
||||
|
||||
/*
|
||||
* Connection closed
|
||||
*/
|
||||
static void closeback(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
|
||||
{
|
||||
fprintf(stderr, "Connection closed to stream %d: %s\n", stream_id,
|
||||
sam_strerror(reason));
|
||||
}
|
||||
|
||||
/*
|
||||
* Someone connected to us - how dare they!
|
||||
*/
|
||||
static void connectback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest)
|
||||
{
|
||||
sam_stream_close(session, stream_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* A peer sent us some data - just ignore it
|
||||
*/
|
||||
static void databack(sam_sess_t *session, sam_sid_t stream_id, void *data,
|
||||
size_t size)
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called whenever the SAM connection fails (like if the I2P router is
|
||||
* shut down)
|
||||
*/
|
||||
static void diedback(sam_sess_t *session)
|
||||
{
|
||||
fprintf(stderr, "Lost SAM connection!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The logging callback prints any logging messages from LibSAM (typically
|
||||
* errors)
|
||||
*/
|
||||
static void logback(char *s)
|
||||
{
|
||||
if (!quiet)
|
||||
fprintf(stderr, "LibSAM: %s\n", s);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is really hackish, but we know that we are only doing one lookup, so
|
||||
* what the hell
|
||||
*/
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
|
||||
{
|
||||
if (result != SAM_OK) {
|
||||
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
|
||||
exit(1);
|
||||
}
|
||||
memcpy(dest, pubkey, SAM_PUBKEY_LEN);
|
||||
gotdest = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Our connection attempt returned a result
|
||||
*/
|
||||
static void statusback(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result)
|
||||
{
|
||||
laststatus = result;
|
||||
laststream = stream_id;
|
||||
}
|
@ -34,88 +34,149 @@
|
||||
* Use only with the utmost courtesy.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "sam.h"
|
||||
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size);
|
||||
static void diedback(void);
|
||||
/*
|
||||
* LibSAM callbacks - functions in our code that are called by LibSAM when
|
||||
* something happens
|
||||
*/
|
||||
static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size);
|
||||
static void diedback(sam_sess_t *session);
|
||||
static void logback(char *s);
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result);
|
||||
|
||||
/*
|
||||
* Just some ugly global variables. Don't do this in your program.
|
||||
*/
|
||||
bool gotdest = false;
|
||||
sam_pubkey_t dest;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
/*
|
||||
* The target of our attack is specified on the command line
|
||||
*/
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Syntax: %s <b64dest|name>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Hook up the callback functions - required by LibSAM */
|
||||
sam_dgramback = &dgramback;
|
||||
sam_diedback = &diedback;
|
||||
sam_logback = &logback;
|
||||
sam_namingback = &namingback;
|
||||
|
||||
/* a tunnel length of 2 is the default - adjust to your preference vv */
|
||||
samerr_t rc = sam_connect("localhost", 7656, "TRANSIENT", SAM_DGRAM, 2);
|
||||
/*
|
||||
* This tool would be more destructive if multiple SAM session were used,
|
||||
* but they aren't - at least for now.
|
||||
*/
|
||||
sam_sess_t *session = NULL; /* set to NULL to have LibSAM do the malloc */
|
||||
session = sam_session_init(session); /* malloc and set defaults */
|
||||
|
||||
/* Connect to the SAM server -- you can use either an IP or DNS name */
|
||||
samerr_t rc = sam_connect(session, "localhost", 7656, "TRANSIENT",
|
||||
SAM_DGRAM, 2); /* the tunnel length of 2 can be adjusted to whatever */
|
||||
if (rc != SAM_OK) {
|
||||
fprintf(stderr, "SAM connection failed: %s\n", sam_strerror(rc));
|
||||
exit(1);
|
||||
sam_session_free(&session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether they've supplied a name or a base 64 destination
|
||||
*
|
||||
* Note that this is a hack. Jrandom says that once certificates are added,
|
||||
* the length could be different depending on the certificate's size.
|
||||
*/
|
||||
if (strlen(argv[1]) == 516) {
|
||||
memcpy(dest, argv[1], SAM_PUBKEY_LEN);
|
||||
gotdest = true;
|
||||
} else {
|
||||
/*
|
||||
* If they supplied a name, we have to do a lookup on it. This is
|
||||
* equivalent to doing a DNS lookup on the normal internet. When the
|
||||
* lookup completes, we send them some data.
|
||||
*/
|
||||
sam_naming_lookup(session, argv[1]);
|
||||
}
|
||||
else
|
||||
sam_naming_lookup(argv[1]);
|
||||
|
||||
while (!gotdest)
|
||||
sam_read_buffer();
|
||||
while (!gotdest) /* just wait for the naming lookup to complete */
|
||||
sam_read_buffer(session);
|
||||
|
||||
char data[SAM_DGRAM_PAYLOAD_MAX];
|
||||
memset(data, '#', SAM_DGRAM_PAYLOAD_MAX);
|
||||
memset(data, '$', SAM_DGRAM_PAYLOAD_MAX); /* We're sending them MONEY! */
|
||||
size_t sentbytes = 0;
|
||||
while (true) {
|
||||
rc = sam_dgram_send(dest, data, SAM_DGRAM_PAYLOAD_MAX);
|
||||
/*
|
||||
* Send them a flood of the largest sized datagrams possible in an
|
||||
* infinite loop!
|
||||
*/
|
||||
rc = sam_dgram_send(session, dest, data, SAM_DGRAM_PAYLOAD_MAX);
|
||||
if (rc != SAM_OK) {
|
||||
fprintf(stderr, "sam_dgram_send() failed: %s\n", sam_strerror(rc));
|
||||
sam_session_free(&session);
|
||||
return 1;
|
||||
}
|
||||
sentbytes += SAM_DGRAM_PAYLOAD_MAX;
|
||||
printf("Bombs away! (%u kbytes sent so far)\n", sentbytes / 1024);
|
||||
sam_read_buffer();
|
||||
/*
|
||||
* sam_read_buffer() just checks for incoming activity from the SAM
|
||||
* session, and invokes the appropriate callbacks. We aren't really
|
||||
* expecting any incoming activity here, but it is a good idea to check
|
||||
* anyway.
|
||||
*/
|
||||
sam_read_buffer(session);
|
||||
}
|
||||
|
||||
sam_session_free(&session); /* de-allocates memory used by the SAM session*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dgramback(sam_pubkey_t dest, void *data, size_t size)
|
||||
/*
|
||||
* When we receive some data from another peer, just ignore it. Denial of
|
||||
* service programs don't need input ;)
|
||||
*/
|
||||
static void dgramback(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size)
|
||||
{
|
||||
puts("Received a datagram (ignored)");
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void diedback(void)
|
||||
/*
|
||||
* This is called whenever the SAM connection fails (like if the I2P router is
|
||||
* shut down)
|
||||
*/
|
||||
static void diedback(sam_sess_t *session)
|
||||
{
|
||||
fprintf(stderr, "Lost SAM connection!\n");
|
||||
/* high quality code would do a sam_session_free() here */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* The logging callback prints any logging messages from LibSAM (typically
|
||||
* errors)
|
||||
*/
|
||||
static void logback(char *s)
|
||||
{
|
||||
fprintf(stderr, "LibSAM: %s\n", s);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is really hackish, but we know that we are only doing one lookup, so
|
||||
* what the hell
|
||||
*/
|
||||
static void namingback(char *name, sam_pubkey_t pubkey, samerr_t result)
|
||||
{
|
||||
if (result != SAM_OK) {
|
||||
fprintf(stderr, "Naming lookup failed: %s\n", sam_strerror(result));
|
||||
/* high quality code would do a sam_session_free() here */
|
||||
exit(1);
|
||||
}
|
||||
memcpy(dest, pubkey, SAM_PUBKEY_LEN);
|
||||
|
@ -112,11 +112,6 @@
|
||||
/*
|
||||
* Platform-dependent variable types
|
||||
*/
|
||||
#ifdef WINSOCK
|
||||
typedef SOCKET socket_t;
|
||||
#else
|
||||
typedef int socket_t;
|
||||
#endif
|
||||
#ifdef NO_SSIZE_T
|
||||
typedef signed long ssize_t;
|
||||
#endif
|
||||
|
@ -34,18 +34,28 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Lengths
|
||||
*/
|
||||
#define SAM_CMD_LEN 128 /* the maximum length a SAM command can be */
|
||||
#define SAM_DGRAM_PAYLOAD_MAX ((31 * 1024) - 30) /* max size of a single datagram packet (-30 temporary bug fix for SAM) */
|
||||
#define SAM_ERRMSG_LEN 23 /* the longest message returned from sam_strerror */
|
||||
#define SAM_LOGMSG_LEN 256 /* the longest log message */
|
||||
#define SAM_NAME_LEN 256 /* the longest `name' arg for naming lookup callback*/
|
||||
#define SAM_STREAM_PAYLOAD_MAX 32768 /* max size of a single stream packet */
|
||||
#define SAM_PKCMD_LEN (SAM_PUBKEY_LEN + SAM_CMD_LEN)/*a public key SAM command*/
|
||||
#define SAM_PUBKEY_LEN 517 /* it's actually 516, but +1 for '\0' */
|
||||
#define SAM_REPLY_LEN 1024 /* the maximum length a SAM non-data reply can be */
|
||||
/* The maximum length a SAM command can be */
|
||||
#define SAM_CMD_LEN 128
|
||||
/*The maximum size of a single datagram packet (-30 temporary bug fix for SAM)*/
|
||||
#define SAM_DGRAM_PAYLOAD_MAX ((31 * 1024) - 30)
|
||||
/* The longest log message */
|
||||
#define SAM_LOGMSG_LEN 256
|
||||
/* The longest `name' arg for the naming lookup callback */
|
||||
#define SAM_NAME_LEN 256
|
||||
/* The max size of a single stream packet */
|
||||
#define SAM_STREAM_PAYLOAD_MAX (32 * 1024)
|
||||
/* The length of a base 64 public key - it's actually 516, but +1 for '\0' */
|
||||
#define SAM_PUBKEY_LEN 517
|
||||
/* A public key SAM command's length */
|
||||
#define SAM_PKCMD_LEN (SAM_PUBKEY_LEN + SAM_CMD_LEN)
|
||||
/* The maximum length a SAM non-data reply can be */
|
||||
#define SAM_REPLY_LEN 1024
|
||||
|
||||
/*
|
||||
* Shorten some standard variable types
|
||||
@ -56,6 +66,12 @@ typedef unsigned int uint_t;
|
||||
typedef unsigned long ulong_t;
|
||||
typedef unsigned short ushort_t;
|
||||
|
||||
#ifdef WINSOCK
|
||||
typedef SOCKET socket_t;
|
||||
#else
|
||||
typedef int socket_t;
|
||||
#endif
|
||||
|
||||
typedef enum {SAM_STREAM, SAM_DGRAM, SAM_RAW} sam_conn_t; /* SAM connection */
|
||||
|
||||
typedef char sam_pubkey_t[SAM_PUBKEY_LEN]; /* base 64 public key */
|
||||
@ -67,12 +83,20 @@ typedef struct {
|
||||
|
||||
typedef int_fast32_t sam_sid_t; /* stream id number */
|
||||
|
||||
typedef struct {
|
||||
socket_t sock; /* the socket used for communications with SAM */
|
||||
bool connected; /* whether the socket is connected */
|
||||
sam_sid_t prev_id; /* the last stream id number we used */
|
||||
} sam_sess_t; /* a SAM session */
|
||||
|
||||
typedef enum { /* see sam_strerror() for detailed descriptions of these */
|
||||
/* no error code - not used by me (you can use it in your program) */
|
||||
SAM_NULL = 0,
|
||||
/* error codes from SAM itself (SAM_OK is not an actual "error") */
|
||||
SAM_OK, SAM_CANT_REACH_PEER, SAM_DUPLICATED_DEST, SAM_I2P_ERROR,
|
||||
SAM_INVALID_KEY, SAM_KEY_NOT_FOUND, SAM_PEER_NOT_FOUND, SAM_TIMEOUT,
|
||||
SAM_UNKNOWN,
|
||||
/* error codes from libsam */
|
||||
/* error codes from LibSAM */
|
||||
SAM_BAD_VERSION, SAM_CALLBACKS_UNSET, SAM_SOCKET_ERROR, SAM_TOO_BIG
|
||||
} samerr_t;
|
||||
|
||||
@ -80,42 +104,53 @@ typedef enum { /* see sam_strerror() for detailed descriptions of these */
|
||||
* Public functions
|
||||
*/
|
||||
|
||||
/* SAM controls */
|
||||
extern bool sam_close(void);
|
||||
extern samerr_t sam_connect(const char *samhost, uint16_t samport,
|
||||
const char *destname, sam_conn_t style, uint_t tunneldepth);
|
||||
extern void sam_naming_lookup(const char *name);
|
||||
extern bool sam_read_buffer(void);
|
||||
/* SAM controls - sessions */
|
||||
extern sam_sess_t *sam_session_init(sam_sess_t *session);
|
||||
extern void sam_session_free(sam_sess_t **session);
|
||||
/* SAM controls - connection */
|
||||
extern bool sam_close(sam_sess_t *session);
|
||||
extern samerr_t sam_connect(sam_sess_t *session, const char *samhost,
|
||||
uint16_t samport, const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth);
|
||||
/* SAM controls - utilities */
|
||||
extern void sam_naming_lookup(sam_sess_t *session, const char *name);
|
||||
extern bool sam_read_buffer(sam_sess_t *session);
|
||||
extern const char *sam_strerror(samerr_t code);
|
||||
/* SAM controls - callbacks */
|
||||
extern void (*sam_diedback)(void);
|
||||
extern void (*sam_diedback)(sam_sess_t *session);
|
||||
extern void (*sam_logback)(char *str);
|
||||
extern void (*sam_namingback)(char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result);
|
||||
|
||||
/* Stream commands */
|
||||
extern void sam_stream_close(sam_sid_t stream_id);
|
||||
extern sam_sid_t sam_stream_connect(const sam_pubkey_t dest);
|
||||
extern samerr_t sam_stream_send(sam_sid_t stream_id, const void *data,
|
||||
size_t size);
|
||||
extern void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id);
|
||||
extern sam_sid_t sam_stream_connect(sam_sess_t *session,
|
||||
const sam_pubkey_t dest);
|
||||
extern samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
const void *data, size_t size);
|
||||
/* Stream commands - callbacks */
|
||||
extern void (*sam_closeback)(sam_sid_t stream_id, samerr_t reason);
|
||||
extern void (*sam_connectback)(sam_sid_t stream_id, sam_pubkey_t dest);
|
||||
extern void (*sam_databack)(sam_sid_t stream_id, void *data, size_t size);
|
||||
extern void (*sam_statusback)(sam_sid_t stream_id, samerr_t result);
|
||||
extern void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t reason);
|
||||
extern void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest);
|
||||
extern void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
void *data, size_t size);
|
||||
extern void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result);
|
||||
|
||||
/* Stream send queue */
|
||||
extern samerr_t sam_sendq_add(sam_sendq_t *sendq, const void *data,
|
||||
size_t dsize);
|
||||
extern sam_sendq_t *sam_sendq_create(void);
|
||||
extern void sam_sendq_send(sam_sendq_t *sendq, sam_sid_t stream_id);
|
||||
/* Stream send queue (experimental) */
|
||||
extern void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq, const void *data, size_t dsize);
|
||||
extern void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq);
|
||||
|
||||
/* Datagram commands */
|
||||
extern samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data,
|
||||
size_t size);
|
||||
extern samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size);
|
||||
|
||||
/* Datagram commands - callbacks */
|
||||
extern void (*sam_dgramback)(sam_pubkey_t dest, void *data, size_t size);
|
||||
extern void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest,
|
||||
void *data, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -31,73 +31,77 @@
|
||||
#include "platform.h"
|
||||
#include "sam.h"
|
||||
|
||||
static bool sam_hello(void);
|
||||
static bool sam_hello(sam_sess_t *session);
|
||||
static void sam_log(const char *format, ...);
|
||||
static void sam_parse(char *s);
|
||||
static ssize_t sam_read1(char *buf, size_t n);
|
||||
static ssize_t sam_read2(void *buf, size_t n);
|
||||
static bool sam_readable(void);
|
||||
static samerr_t sam_session_create(const char *destname, sam_conn_t style,
|
||||
static void sam_parse(sam_sess_t *session, char *s);
|
||||
static ssize_t sam_read1(sam_sess_t *session, char *buf, size_t n);
|
||||
static ssize_t sam_read2(sam_sess_t *session, void *buf, size_t n);
|
||||
static bool sam_readable(sam_sess_t *session);
|
||||
static sam_sendq_t *sam_sendq_create();
|
||||
static samerr_t sam_session_create(sam_sess_t *session,
|
||||
const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth);
|
||||
static bool sam_socket_connect(const char *host, uint16_t port);
|
||||
static bool sam_socket_connect(sam_sess_t *session, const char *host,
|
||||
uint16_t port);
|
||||
static bool sam_socket_resolve(const char *hostname, char *ipaddr);
|
||||
#ifdef WINSOCK
|
||||
static samerr_t sam_winsock_cleanup(void);
|
||||
static samerr_t sam_winsock_startup(void);
|
||||
static samerr_t sam_winsock_cleanup();
|
||||
static samerr_t sam_winsock_startup();
|
||||
static const char *sam_winsock_strerror(int code);
|
||||
#endif
|
||||
static ssize_t sam_write(const void *buf, size_t n);
|
||||
static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n);
|
||||
|
||||
/*
|
||||
* Callback functions
|
||||
* Note: if you add a new callback be sure to check for non-NULL in sam_connect
|
||||
*/
|
||||
/* a peer closed the connection */
|
||||
void (*sam_closeback)(sam_sid_t stream_id, samerr_t reason) = NULL;
|
||||
void (*sam_closeback)(sam_sess_t *session, sam_sid_t stream_id, samerr_t reason)
|
||||
= NULL;
|
||||
/* a peer connected to us */
|
||||
void (*sam_connectback)(sam_sid_t stream_id, sam_pubkey_t dest) = NULL;
|
||||
void (*sam_connectback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_pubkey_t dest) = NULL;
|
||||
/* a peer sent some stream data (`data' MUST be freed) */
|
||||
void (*sam_databack)(sam_sid_t stream_id, void *data, size_t size) = NULL;
|
||||
void (*sam_databack)(sam_sess_t *session, sam_sid_t stream_id, void *data,
|
||||
size_t size) = NULL;
|
||||
/* a peer sent some datagram data (`data' MUST be freed) */
|
||||
void (*sam_dgramback)(sam_pubkey_t dest, void *data, size_t size) = NULL;
|
||||
void (*sam_dgramback)(sam_sess_t *session, sam_pubkey_t dest, void *data,
|
||||
size_t size) = NULL;
|
||||
/* we lost the connection to the SAM host */
|
||||
void (*sam_diedback)(void) = NULL;
|
||||
void (*sam_diedback)(sam_sess_t *session) = NULL;
|
||||
/* logging callback */
|
||||
void (*sam_logback)(char *str) = NULL;
|
||||
/* naming lookup reply - `pubkey' will be NULL if `result' isn't SAM_OK */
|
||||
void (*sam_namingback)(char *name, sam_pubkey_t pubkey,
|
||||
samerr_t result) = NULL;
|
||||
void (*sam_namingback)(char *name, sam_pubkey_t pubkey, samerr_t result) = NULL;
|
||||
/* our connection to a peer has completed */
|
||||
void (*sam_statusback)(sam_sid_t stream_id, samerr_t result) = NULL;
|
||||
|
||||
static socket_t samd; /* The socket descriptor we're using for
|
||||
communications with SAM */
|
||||
static bool samd_connected = false; /* Whether we're connected with SAM */
|
||||
void (*sam_statusback)(sam_sess_t *session, sam_sid_t stream_id,
|
||||
samerr_t result) = NULL;
|
||||
|
||||
/*
|
||||
* Closes the connection to the SAM host
|
||||
*
|
||||
* Returns: true on success, false on failure
|
||||
*/
|
||||
bool sam_close(void)
|
||||
bool sam_close(sam_sess_t *session)
|
||||
{
|
||||
if (!samd_connected)
|
||||
assert(session != NULL);
|
||||
if (!session->connected)
|
||||
return true;
|
||||
|
||||
#ifdef WINSOCK
|
||||
if (closesocket(samd) == SOCKET_ERROR) {
|
||||
if (closesocket(session->sock) == SOCKET_ERROR) {
|
||||
SAMLOG("Failed closing the SAM connection (%s)",
|
||||
sam_winsock_strerror(WSAGetLastError()));
|
||||
return false;
|
||||
}
|
||||
samd_connected = false;
|
||||
session->connected = false;
|
||||
if (sam_winsock_cleanup() == SAM_OK)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
#else
|
||||
if (close(samd) == 0) {
|
||||
samd_connected = false;
|
||||
if (close(session->sock) == 0) {
|
||||
session->connected = false;
|
||||
return true;
|
||||
} else {
|
||||
SAMLOG("Failed closing the SAM connection (%s)", strerror(errno));
|
||||
@ -109,6 +113,7 @@ bool sam_close(void)
|
||||
/*
|
||||
* Connects to the SAM host
|
||||
*
|
||||
* session - an unused SAM session created by sam_session_init()
|
||||
* samhost - SAM host
|
||||
* samport - SAM port
|
||||
* destname - destination name for this program, or "TRANSIENT" for a random
|
||||
@ -116,11 +121,13 @@ bool sam_close(void)
|
||||
* tunneldepth - length of the I2P tunnels created by this program (longer is
|
||||
* more anonymous, but slower)
|
||||
*
|
||||
* Returns: true on success, false on failure
|
||||
* Returns: True on success, false on failure. If true, `session' will be ready
|
||||
* for use.
|
||||
*/
|
||||
samerr_t sam_connect(const char *samhost, uint16_t samport,
|
||||
const char *destname, sam_conn_t style, uint_t tunneldepth)
|
||||
samerr_t sam_connect(sam_sess_t *session, const char *samhost, uint16_t samport,
|
||||
const char *destname, sam_conn_t style, uint_t tunneldepth)
|
||||
{
|
||||
assert(session != NULL);
|
||||
samerr_t rc;
|
||||
|
||||
if (style == SAM_STREAM) {
|
||||
@ -149,7 +156,7 @@ samerr_t sam_connect(const char *samhost, uint16_t samport,
|
||||
return rc;
|
||||
#endif
|
||||
|
||||
if (!sam_socket_connect(samhost, samport)) {
|
||||
if (!sam_socket_connect(session, samhost, samport)) {
|
||||
#ifdef WINSOCK
|
||||
SAMLOG("Couldn't connect to SAM at %s:%u (%s)",
|
||||
samhost, samport, sam_winsock_strerror(WSAGetLastError()));
|
||||
@ -161,10 +168,10 @@ samerr_t sam_connect(const char *samhost, uint16_t samport,
|
||||
return SAM_SOCKET_ERROR;
|
||||
}
|
||||
|
||||
if (!sam_hello())
|
||||
if (!sam_hello(session))
|
||||
return SAM_BAD_VERSION;
|
||||
|
||||
rc = sam_session_create(destname, style, tunneldepth);
|
||||
rc = sam_session_create(session, destname, style, tunneldepth);
|
||||
if (rc != SAM_OK)
|
||||
return rc;
|
||||
|
||||
@ -181,15 +188,17 @@ samerr_t sam_connect(const char *samhost, uint16_t samport,
|
||||
*
|
||||
* Returns: true on success, false on failure
|
||||
*/
|
||||
samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, size_t size)
|
||||
samerr_t sam_dgram_send(sam_sess_t *session, const sam_pubkey_t dest,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_PKCMD_LEN];
|
||||
|
||||
if (size < 1 || size > SAM_DGRAM_PAYLOAD_MAX) {
|
||||
#ifdef NO_Z_FORMAT
|
||||
SAMLOG("Invalid data send size (%u bytes)", size);
|
||||
#else
|
||||
SAMLOG("Invalid data send size (%dz bytes)", size);
|
||||
SAMLOG("Invalid data send size (%zu bytes)", size);
|
||||
#endif
|
||||
return SAM_TOO_BIG;
|
||||
}
|
||||
@ -197,11 +206,11 @@ samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, size_t size)
|
||||
snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%u\n",
|
||||
dest, size);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%dz\n",
|
||||
snprintf(cmd, sizeof cmd, "DATAGRAM SEND DESTINATION=%s SIZE=%zu\n",
|
||||
dest, size);
|
||||
#endif
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_write(data, size);
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
sam_write(session, data, size);
|
||||
|
||||
return SAM_OK;
|
||||
}
|
||||
@ -212,14 +221,15 @@ samerr_t sam_dgram_send(const sam_pubkey_t dest, const void *data, size_t size)
|
||||
*
|
||||
* Returns: true on success, false on reply failure
|
||||
*/
|
||||
static bool sam_hello(void)
|
||||
static bool sam_hello(sam_sess_t *session)
|
||||
{
|
||||
assert(session != NULL);
|
||||
#define SAM_HELLO_CMD "HELLO VERSION MIN=1.0 MAX=1.0\n"
|
||||
#define SAM_HELLO_REPLY "HELLO REPLY RESULT=OK VERSION=1.0"
|
||||
char reply[SAM_REPLY_LEN];
|
||||
|
||||
sam_write(SAM_HELLO_CMD, strlen(SAM_HELLO_CMD));
|
||||
sam_read1(reply, SAM_REPLY_LEN);
|
||||
sam_write(session, SAM_HELLO_CMD, strlen(SAM_HELLO_CMD));
|
||||
sam_read1(session, reply, SAM_REPLY_LEN);
|
||||
if (strncmp(reply, SAM_HELLO_REPLY, strlen(SAM_HELLO_REPLY)) == 0)
|
||||
return true;
|
||||
else {
|
||||
@ -250,12 +260,13 @@ static void sam_log(const char *format, ...)
|
||||
*
|
||||
* name - name to lookup, or ME to lookup our own name
|
||||
*/
|
||||
void sam_naming_lookup(const char *name)
|
||||
void sam_naming_lookup(sam_sess_t *session, const char *name)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_CMD_LEN];
|
||||
|
||||
snprintf(cmd, sizeof cmd, "NAMING LOOKUP NAME=%s\n", name);
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -265,8 +276,9 @@ void sam_naming_lookup(const char *name)
|
||||
*
|
||||
* s - string of data that we read (read past tense)
|
||||
*/
|
||||
static void sam_parse(char *s)
|
||||
static void sam_parse(sam_sess_t *session, char *s)
|
||||
{
|
||||
assert(session != NULL);
|
||||
#define SAM_DGRAM_RECEIVED_REPLY "DATAGRAM RECEIVED"
|
||||
#define SAM_NAMING_REPLY "NAMING REPLY"
|
||||
#define SAM_NAMING_REPLY_OK "NAMING REPLY RESULT=OK"
|
||||
@ -304,10 +316,10 @@ static void sam_parse(char *s)
|
||||
data is sent, the extra NUL character will
|
||||
just be ignored by the client program,
|
||||
because it is not added to the size */
|
||||
if (sam_read2(data, size) != -1) {
|
||||
if (sam_read2(session, data, size) != -1) {
|
||||
p = data + size;
|
||||
*p = '\0'; /* see above NUL note */
|
||||
sam_dgramback(dest, data, size); /* `data' must be freed */
|
||||
sam_dgramback(session, dest, data, size); /* `data' must be freed */
|
||||
} else
|
||||
free(data);
|
||||
|
||||
@ -380,17 +392,17 @@ static void sam_parse(char *s)
|
||||
assert(p != NULL);
|
||||
p++;
|
||||
if (strncmp(p, "OK", strlen("OK")) == 0)
|
||||
sam_closeback(stream_id, SAM_OK);
|
||||
sam_closeback(session, stream_id, SAM_OK);
|
||||
else if (strncmp(p, "CANT_REACH_PEER", strlen("CANT_REACH_PEER")) == 0)
|
||||
sam_closeback(stream_id, SAM_CANT_REACH_PEER);
|
||||
sam_closeback(session, stream_id, SAM_CANT_REACH_PEER);
|
||||
else if (strncmp(p, "I2P_ERROR", strlen("I2P_ERROR")) == 0)
|
||||
sam_closeback(stream_id, SAM_I2P_ERROR);
|
||||
sam_closeback(session, stream_id, SAM_I2P_ERROR);
|
||||
else if (strncmp(p, "PEER_NOT_FOUND", strlen("PEER_NOT_FOUND")) == 0)
|
||||
sam_closeback(stream_id, SAM_PEER_NOT_FOUND);
|
||||
sam_closeback(session, stream_id, SAM_PEER_NOT_FOUND);
|
||||
else if (strncmp(p, "TIMEOUT", strlen("TIMEOUT")) == 0)
|
||||
sam_closeback(stream_id, SAM_TIMEOUT);
|
||||
sam_closeback(session, stream_id, SAM_TIMEOUT);
|
||||
else
|
||||
sam_closeback(stream_id, SAM_UNKNOWN);
|
||||
sam_closeback(session, stream_id, SAM_UNKNOWN);
|
||||
|
||||
return;
|
||||
|
||||
@ -409,7 +421,7 @@ static void sam_parse(char *s)
|
||||
p = strstr(s, "N="); /* DESTINATION= */
|
||||
p += 2;
|
||||
strlcpy(dest, p, sizeof dest);
|
||||
sam_connectback(stream_id, dest);
|
||||
sam_connectback(session, stream_id, dest);
|
||||
|
||||
return;
|
||||
|
||||
@ -438,10 +450,11 @@ static void sam_parse(char *s)
|
||||
data is sent, the extra NUL character will
|
||||
just be ignored by the client program,
|
||||
because it is not added to the size */
|
||||
if (sam_read2(data, size) != -1) {
|
||||
if (sam_read2(session, data, size) != -1) {
|
||||
p = data + size;
|
||||
*p = '\0'; /* see above NUL note */
|
||||
sam_databack(stream_id, data, size); /* `data' must be freed */
|
||||
sam_databack(session, stream_id, data, size);
|
||||
/* ^^^ `data' must be freed ^^^*/
|
||||
} else
|
||||
free(data);
|
||||
|
||||
@ -462,21 +475,21 @@ static void sam_parse(char *s)
|
||||
assert(stream_id != 0);
|
||||
if (strncmp(s, SAM_STREAM_STATUS_REPLY_OK,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_OK)) == 0)
|
||||
sam_statusback(stream_id, SAM_OK);
|
||||
sam_statusback(session, stream_id, SAM_OK);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_CRP,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_CRP)) == 0)
|
||||
sam_statusback(stream_id, SAM_CANT_REACH_PEER);
|
||||
sam_statusback(session, stream_id, SAM_CANT_REACH_PEER);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_I2E,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_I2E)) == 0)
|
||||
sam_statusback(stream_id, SAM_I2P_ERROR);
|
||||
sam_statusback(session, stream_id, SAM_I2P_ERROR);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_IK,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_IK)) == 0)
|
||||
sam_statusback(stream_id, SAM_INVALID_KEY);
|
||||
sam_statusback(session, stream_id, SAM_INVALID_KEY);
|
||||
else if (strncmp(s, SAM_STREAM_STATUS_REPLY_TO,
|
||||
strlen(SAM_STREAM_STATUS_REPLY_TO)) == 0)
|
||||
sam_statusback(stream_id, SAM_TIMEOUT);
|
||||
sam_statusback(session, stream_id, SAM_TIMEOUT);
|
||||
else
|
||||
sam_statusback(stream_id, SAM_UNKNOWN);
|
||||
sam_statusback(session, stream_id, SAM_UNKNOWN);
|
||||
|
||||
return;
|
||||
|
||||
@ -491,17 +504,18 @@ static void sam_parse(char *s)
|
||||
*
|
||||
* Returns: true if we read anything, or false if nothing was there
|
||||
*/
|
||||
bool sam_read_buffer(void)
|
||||
bool sam_read_buffer(sam_sess_t *session)
|
||||
{
|
||||
assert(session != NULL);
|
||||
bool read_something = false;
|
||||
char reply[SAM_REPLY_LEN];
|
||||
|
||||
if (sam_readable()) {
|
||||
if (sam_readable(session)) {
|
||||
do {
|
||||
sam_read1(reply, SAM_REPLY_LEN);
|
||||
sam_read1(session, reply, SAM_REPLY_LEN);
|
||||
read_something = true;
|
||||
sam_parse(reply);
|
||||
} while (sam_readable());
|
||||
sam_parse(session, reply);
|
||||
} while (sam_readable(session));
|
||||
}
|
||||
|
||||
return read_something;
|
||||
@ -521,24 +535,25 @@ bool sam_read_buffer(void)
|
||||
*
|
||||
* Returns: number of bytes read, or -1 on error
|
||||
*/
|
||||
static ssize_t sam_read1(char *buf, size_t n)
|
||||
static ssize_t sam_read1(sam_sess_t *session, char *buf, size_t n)
|
||||
{
|
||||
assert(session != NULL);
|
||||
size_t nleft;
|
||||
ssize_t nread;
|
||||
char *p;
|
||||
|
||||
*buf = '\0'; /* this forces `buf' to be a string even if there is a
|
||||
sam_read1 error return */
|
||||
if (!samd_connected) {
|
||||
if (!session->connected) {
|
||||
SAMLOGS("Cannot read from SAM because the SAM connection is closed");
|
||||
sam_diedback();
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
assert(n > 0);
|
||||
p = buf;
|
||||
nleft = n;
|
||||
while (nleft > 0) {
|
||||
nread = recv(samd, p, 1, 0);
|
||||
nread = recv(session->sock, p, 1, 0);
|
||||
if (nread == -1) {
|
||||
if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */
|
||||
continue;
|
||||
@ -549,14 +564,14 @@ static ssize_t sam_read1(char *buf, size_t n)
|
||||
#else
|
||||
SAMLOG("recv() failed: %s", strerror(errno));
|
||||
#endif
|
||||
sam_close();
|
||||
sam_diedback();
|
||||
sam_close(session);
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
} else if (nread == 0) { /* EOF */
|
||||
SAMLOGS("Connection closed by the SAM host");
|
||||
sam_close();
|
||||
sam_diedback();
|
||||
sam_close(session);
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
assert(nread == 1);
|
||||
@ -588,22 +603,23 @@ static ssize_t sam_read1(char *buf, size_t n)
|
||||
*
|
||||
* Returns: number of bytes read, or -1 on error
|
||||
*/
|
||||
static ssize_t sam_read2(void *buf, size_t n)
|
||||
static ssize_t sam_read2(sam_sess_t *session, void *buf, size_t n)
|
||||
{
|
||||
assert(session != NULL);
|
||||
size_t nleft;
|
||||
ssize_t nread;
|
||||
void *p;
|
||||
|
||||
if (!samd_connected) {
|
||||
if (!session->connected) {
|
||||
SAMLOGS("Cannot read from SAM because the SAM connection is closed");
|
||||
sam_diedback();
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
assert(n > 0);
|
||||
p = buf;
|
||||
nleft = n;
|
||||
while (nleft > 0) {
|
||||
nread = recv(samd, p, nleft, 0);
|
||||
nread = recv(session->sock, p, nleft, 0);
|
||||
if (nread == -1) {
|
||||
if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */
|
||||
continue;
|
||||
@ -614,14 +630,14 @@ static ssize_t sam_read2(void *buf, size_t n)
|
||||
#else
|
||||
SAMLOG("recv() failed: %s", strerror(errno));
|
||||
#endif
|
||||
sam_close();
|
||||
sam_diedback();
|
||||
sam_close(session);
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
} else if (nread == 0) { /* EOF */
|
||||
SAMLOGS("Connection closed by the SAM host");
|
||||
sam_close();
|
||||
sam_diedback();
|
||||
sam_close(session);
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
nleft -= nread;
|
||||
@ -639,23 +655,24 @@ static ssize_t sam_read2(void *buf, size_t n)
|
||||
*
|
||||
* Returns: true if data is waiting, false otherwise
|
||||
*/
|
||||
static bool sam_readable(void)
|
||||
static bool sam_readable(sam_sess_t *session)
|
||||
{
|
||||
assert(session != NULL);
|
||||
fd_set rset; /* set of readable descriptors */
|
||||
struct timeval tv;
|
||||
int rc;
|
||||
|
||||
if (!samd_connected) {
|
||||
if (!session->connected) {
|
||||
SAMLOGS("Cannot read from SAM because the SAM connection is closed");
|
||||
sam_diedback();
|
||||
sam_diedback(session);
|
||||
return false;
|
||||
}
|
||||
/* it seems like there should be a better way to do this (i.e. not select)*/
|
||||
FD_ZERO(&rset);
|
||||
FD_SET(samd, &rset);
|
||||
FD_SET(session->sock, &rset);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10;
|
||||
rc = select(samd + 1, &rset, NULL, NULL, &tv);
|
||||
rc = select(session->sock + 1, &rset, NULL, NULL, &tv);
|
||||
if (rc == 0)
|
||||
return false;
|
||||
else if (rc > 0)
|
||||
@ -673,42 +690,66 @@ static bool sam_readable(void)
|
||||
/*
|
||||
* Adds data to the send queue
|
||||
*
|
||||
* stream_id - stream number to send to if the queue is full
|
||||
* sendq - the send queue
|
||||
* data - data to add
|
||||
* dsize - the size of the data
|
||||
*
|
||||
* Returns: true on success, false on error
|
||||
*/
|
||||
samerr_t sam_sendq_add(sam_sendq_t *sendq, const void *data, size_t dsize)
|
||||
void sam_sendq_add(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq, const void *data, size_t dsize)
|
||||
{
|
||||
assert(session != NULL);
|
||||
assert(dsize >= 0);
|
||||
if (dsize == 0) {
|
||||
SAMLOGS("dsize is 0 - adding nothing");
|
||||
return SAM_OK;
|
||||
} else if (sendq->size + dsize > SAM_STREAM_PAYLOAD_MAX) {
|
||||
SAMLOGS("The queue size would exceed the maximum SAM payload size -" \
|
||||
" will not add to the queue");
|
||||
return SAM_TOO_BIG;
|
||||
SAMLOGS("dsize is 0 - doing nothing");
|
||||
return;
|
||||
}
|
||||
|
||||
sendq->data = realloc(sendq->data, sendq->size + dsize);
|
||||
memcpy(sendq->data + sendq->size, data, dsize);
|
||||
sendq->size += dsize;
|
||||
/* if the sendq pointer is set to NULL, create a sendq */
|
||||
if (*sendq == NULL)
|
||||
*sendq = sam_sendq_create();
|
||||
|
||||
return SAM_OK;
|
||||
/* the added data doesn't fill the queue - add but don't send */
|
||||
if ((*sendq)->size + dsize < SAM_STREAM_PAYLOAD_MAX) {
|
||||
memcpy((*sendq)->data + (*sendq)->size, data, dsize);
|
||||
(*sendq)->size += dsize;
|
||||
return;
|
||||
}
|
||||
|
||||
/* what luck! - an exact fit - send the packet */
|
||||
if ((*sendq)->size + dsize == SAM_STREAM_PAYLOAD_MAX) {
|
||||
memcpy((*sendq)->data + (*sendq)->size, data, dsize);
|
||||
(*sendq)->size = SAM_STREAM_PAYLOAD_MAX;
|
||||
sam_sendq_flush(session, stream_id, sendq);
|
||||
return;
|
||||
}
|
||||
|
||||
/* they have more data than the queue can hold, so we'll have to send some*/
|
||||
size_t s = SAM_STREAM_PAYLOAD_MAX - (*sendq)->size; // space free in packet
|
||||
memcpy((*sendq)->data + (*sendq)->size, data, s); //append as much as we can
|
||||
dsize -= s; /* update dsize to the size of whatever data hasn't been sent*/
|
||||
(*sendq)->size = SAM_STREAM_PAYLOAD_MAX; /* it's a full packet */
|
||||
sam_sendq_flush(session, stream_id, sendq); /* send the queued data */
|
||||
sam_sendq_add(session, stream_id, sendq, data + s, dsize); /* recurse */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a data queue for use with sam_stream_send
|
||||
* Creates a data queue for use with sam_sendq_add()
|
||||
*
|
||||
* Returns: pointer to the newly created send queue
|
||||
*/
|
||||
sam_sendq_t *sam_sendq_create(void)
|
||||
static sam_sendq_t *sam_sendq_create()
|
||||
{
|
||||
sam_sendq_t *sendq;
|
||||
|
||||
sendq = malloc(sizeof(sam_sendq_t));
|
||||
sendq->data = NULL;
|
||||
sendq->data = malloc(SAM_STREAM_PAYLOAD_MAX);
|
||||
/* ^^ a waste of memory perhaps, but more efficient than realloc'ing every
|
||||
* time data is added the to queue */
|
||||
sendq->size = 0;
|
||||
|
||||
return sendq;
|
||||
@ -717,29 +758,64 @@ sam_sendq_t *sam_sendq_create(void)
|
||||
/*
|
||||
* Sends the data in the send queue to the specified stream
|
||||
*
|
||||
* sendq - the send queue
|
||||
* stream_id - stream number to send to
|
||||
* sendq - the send queue
|
||||
*/
|
||||
void sam_sendq_send(sam_sendq_t *sendq, sam_sid_t stream_id)
|
||||
void sam_sendq_flush(sam_sess_t *session, sam_sid_t stream_id,
|
||||
sam_sendq_t **sendq)
|
||||
{
|
||||
sam_stream_send(stream_id, sendq->data, sendq->size);
|
||||
free(sendq);
|
||||
assert(session != NULL);
|
||||
sam_stream_send(session, stream_id, (*sendq)->data, (*sendq)->size);
|
||||
/* we now free it in case they aren't going to use it anymore */
|
||||
free((*sendq)->data);
|
||||
free(*sendq);
|
||||
*sendq = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates memory for the session and sets its default values
|
||||
*
|
||||
* session - pointer to a previously allocated sam_sess_t to initalise, or NULL
|
||||
* if you want memory to be allocated by this function
|
||||
*/
|
||||
sam_sess_t *sam_session_init(sam_sess_t *session)
|
||||
{
|
||||
if (session == NULL)
|
||||
session = malloc(sizeof(sam_sess_t));
|
||||
session->connected = false;
|
||||
session->prev_id = 0;
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees memory used by the session and sets the pointer to NULL
|
||||
*
|
||||
* session - pointer to a pointer to a sam_sess_t
|
||||
*/
|
||||
void sam_session_free(sam_sess_t **session)
|
||||
{
|
||||
assert(*session != NULL);
|
||||
free(*session);
|
||||
*session = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends the second SAM handshake command and checks the reply
|
||||
*
|
||||
* destname - destination name for this program, or "TRANSIENT" to create a
|
||||
* random temporary destination
|
||||
* style - type of connection to use (SAM_STREAM, SAM_DGRAM, or SAM_RAW)
|
||||
* tunneldepth - length of the I2P tunnels created by this program
|
||||
*
|
||||
* Returns: SAM error code
|
||||
*/
|
||||
static samerr_t sam_session_create(const char *destname, sam_conn_t style,
|
||||
uint_t tunneldepth)
|
||||
static samerr_t sam_session_create(sam_sess_t *session, const char *destname,
|
||||
sam_conn_t style, uint_t tunneldepth)
|
||||
{
|
||||
assert(session != NULL);
|
||||
#define SAM_SESSTATUS_REPLY_OK "SESSION STATUS RESULT=OK"
|
||||
#define SAM_SESSTATUS_REPLY_DD "SESSION STATUS RESULT=DUPLICATED_DEST"
|
||||
#define SAM_SESSTATUS_REPLY_I2E "SESSION STATUS RESULT=I2P_ERROR"
|
||||
@ -764,8 +840,8 @@ static samerr_t sam_session_create(const char *destname, sam_conn_t style,
|
||||
assert(false); /* unimplemented */
|
||||
}
|
||||
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_read1(reply, SAM_REPLY_LEN);
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
sam_read1(session, reply, SAM_REPLY_LEN);
|
||||
if (strncmp(reply, SAM_SESSTATUS_REPLY_OK,
|
||||
strlen(SAM_SESSTATUS_REPLY_OK)) == 0)
|
||||
return SAM_OK;
|
||||
@ -790,20 +866,21 @@ static samerr_t sam_session_create(const char *destname, sam_conn_t style,
|
||||
*
|
||||
* Returns: true on sucess, false on error, with errno set
|
||||
*/
|
||||
bool sam_socket_connect(const char *host, uint16_t port)
|
||||
bool sam_socket_connect(sam_sess_t *session, const char *host, uint16_t port)
|
||||
{
|
||||
assert(session != NULL);
|
||||
struct sockaddr_in hostaddr;
|
||||
int rc;
|
||||
char ipaddr[INET_ADDRSTRLEN];
|
||||
|
||||
samd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
session->sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
#ifdef WINSOCK
|
||||
if (samd == INVALID_SOCKET) {
|
||||
if (session->sock == INVALID_SOCKET) {
|
||||
SAMLOG("socket() failed: %s", sam_winsock_strerror(WSAGetLastError()));
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (samd == -1) {
|
||||
if (session->sock == -1) {
|
||||
SAMLOG("socket() failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
@ -826,7 +903,7 @@ bool sam_socket_connect(const char *host, uint16_t port)
|
||||
} else if (rc == -1)
|
||||
return false;
|
||||
|
||||
rc = connect(samd, (struct sockaddr *)&hostaddr, sizeof hostaddr);
|
||||
rc = connect(session->sock, (struct sockaddr *)&hostaddr, sizeof hostaddr);
|
||||
if (rc == -1) {
|
||||
#ifdef WINSOCK
|
||||
SAMLOG("connect() failed: %s", sam_winsock_strerror(WSAGetLastError()));
|
||||
@ -836,7 +913,7 @@ bool sam_socket_connect(const char *host, uint16_t port)
|
||||
return false;
|
||||
}
|
||||
|
||||
samd_connected = true;
|
||||
session->connected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -900,8 +977,9 @@ retry:
|
||||
*
|
||||
* stream_id - stream number to close
|
||||
*/
|
||||
void sam_stream_close(sam_sid_t stream_id)
|
||||
void sam_stream_close(sam_sess_t *session, sam_sid_t stream_id)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_CMD_LEN];
|
||||
|
||||
#ifdef FAST32_IS_LONG
|
||||
@ -909,7 +987,7 @@ void sam_stream_close(sam_sid_t stream_id)
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM CLOSE ID=%d\n", stream_id);
|
||||
#endif
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -921,22 +999,22 @@ void sam_stream_close(sam_sid_t stream_id)
|
||||
*
|
||||
* Returns: stream id number
|
||||
*/
|
||||
sam_sid_t sam_stream_connect(const sam_pubkey_t dest)
|
||||
sam_sid_t sam_stream_connect(sam_sess_t *session, const sam_pubkey_t dest)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_PKCMD_LEN];
|
||||
static sam_sid_t id = 0;
|
||||
|
||||
id++; /* increment the id for the connection */
|
||||
session->prev_id++; /* increment the id for the connection */
|
||||
#ifdef FAST32_IS_LONG
|
||||
snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%ld DESTINATION=%s\n",
|
||||
id, dest);
|
||||
session->prev_id, dest);
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM CONNECT ID=%d DESTINATION=%s\n",
|
||||
id, dest);
|
||||
session->prev_id, dest);
|
||||
#endif
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
|
||||
return id;
|
||||
return session->prev_id;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -948,8 +1026,10 @@ sam_sid_t sam_stream_connect(const sam_pubkey_t dest)
|
||||
*
|
||||
* Returns: true on success, false on failure
|
||||
*/
|
||||
samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, size_t size)
|
||||
samerr_t sam_stream_send(sam_sess_t *session, sam_sid_t stream_id,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
assert(session != NULL);
|
||||
char cmd[SAM_CMD_LEN];
|
||||
|
||||
if (size < 1 || size > SAM_STREAM_PAYLOAD_MAX) {
|
||||
@ -957,7 +1037,7 @@ samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, size_t size)
|
||||
SAMLOG("Invalid data send size (%u bytes) for stream %d",
|
||||
size, stream_id);
|
||||
#else
|
||||
SAMLOG("Invalid data send size (%dz bytes) for stream %d",
|
||||
SAMLOG("Invalid data send size (%zu bytes) for stream %d",
|
||||
size, stream_id);
|
||||
#endif
|
||||
return SAM_TOO_BIG;
|
||||
@ -971,11 +1051,11 @@ samerr_t sam_stream_send(sam_sid_t stream_id, const void *data, size_t size)
|
||||
stream_id, size);
|
||||
#endif
|
||||
#else
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%dz\n",
|
||||
snprintf(cmd, sizeof cmd, "STREAM SEND ID=%d SIZE=%zu\n",
|
||||
stream_id, size);
|
||||
#endif
|
||||
sam_write(cmd, strlen(cmd));
|
||||
sam_write(data, size);
|
||||
sam_write(session, cmd, strlen(cmd));
|
||||
sam_write(session, data, size);
|
||||
|
||||
return SAM_OK;
|
||||
}
|
||||
@ -1034,7 +1114,7 @@ const char *sam_strerror(samerr_t code)
|
||||
*
|
||||
* Returns: SAM error code
|
||||
*/
|
||||
samerr_t sam_winsock_cleanup(void)
|
||||
samerr_t sam_winsock_cleanup()
|
||||
{
|
||||
if (WSACleanup() == SOCKET_ERROR) {
|
||||
SAMLOG("WSACleanup() failed: %s",
|
||||
@ -1050,7 +1130,7 @@ samerr_t sam_winsock_cleanup(void)
|
||||
*
|
||||
* Returns: SAM error code
|
||||
*/
|
||||
samerr_t sam_winsock_startup(void)
|
||||
samerr_t sam_winsock_startup()
|
||||
{
|
||||
/*
|
||||
* Is Windows retarded or what?
|
||||
@ -1207,15 +1287,16 @@ const char *sam_winsock_strerror(int code)
|
||||
*
|
||||
* Returns: `n', or -1 on error
|
||||
*/
|
||||
static ssize_t sam_write(const void *buf, size_t n)
|
||||
static ssize_t sam_write(sam_sess_t *session, const void *buf, size_t n)
|
||||
{
|
||||
assert(session != NULL);
|
||||
size_t nleft;
|
||||
ssize_t nwritten;
|
||||
const char *p;
|
||||
|
||||
if (!samd_connected) {
|
||||
if (!session->connected) {
|
||||
SAMLOGS("Cannot write to SAM because the SAM connection is closed");
|
||||
sam_diedback();
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
#if SAM_WIRETAP
|
||||
@ -1231,7 +1312,7 @@ static ssize_t sam_write(const void *buf, size_t n)
|
||||
p = buf;
|
||||
nleft = n;
|
||||
while (nleft > 0) {
|
||||
nwritten = send(samd, p, nleft, 0);
|
||||
nwritten = send(session->sock, p, nleft, 0);
|
||||
if (nwritten <= 0) {
|
||||
if (errno == EINTR) /* see Unix Network Pgming vol 1, Sec. 5.9 */
|
||||
continue;
|
||||
@ -1242,8 +1323,8 @@ static ssize_t sam_write(const void *buf, size_t n)
|
||||
#else
|
||||
SAMLOG("send() failed: %s", strerror(errno));
|
||||
#endif
|
||||
sam_close();
|
||||
sam_diedback();
|
||||
sam_close(session);
|
||||
sam_diedback(session);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,16 @@
|
||||
|
||||
Known Bugs:
|
||||
* TunnelServer may crash the I2P router in the following
|
||||
ways when a large file is downloaded:
|
||||
|
||||
* Out of memory exception (for large files)
|
||||
* Mysterious router death with no errors in the router logs
|
||||
(more recently)
|
||||
* BUG! in SAM proxy
|
||||
See http://oregonstate.edu/~barnesc/temp/sam_crash.txt
|
||||
* A small number of datagram packets sent are lost (even in a local
|
||||
loopback). This is apparently a bug in I2P.
|
||||
* tunnel.TunnelServer may crash the I2P router when a 20+ MB file
|
||||
is downloaded at 200+ KB/s (only possible with local downloads).
|
||||
* Errors raised for sockets are non entirely consistent.
|
||||
See todo.txt for how to fix this.
|
||||
* A session does not close until a program exits.
|
||||
This should be fine once I2P is patched to allow multiple
|
||||
programs to use a single session at once.
|
||||
* i2p.router.start() does not work.
|
||||
* i2p.router.start() does not work.
|
||||
|
||||
Fixed Bugs:
|
||||
* Large downloads are no longer corrupted (fixed by jrandom in I2P
|
||||
core).
|
||||
* Datagram packets are no longer lost, for a local server and
|
||||
client (fixed by jrandom in I2P core).
|
||||
|
18
apps/sam/python/doc/epydoc/calldoc.py
Normal file
18
apps/sam/python/doc/epydoc/calldoc.py
Normal file
@ -0,0 +1,18 @@
|
||||
#!/usr/local/bin/python
|
||||
|
||||
#
|
||||
# Call the command line interface for Epydoc.
|
||||
#
|
||||
|
||||
# We have to do some path magic to prevent Python from getting
|
||||
# confused about the difference between this epydoc module, and the
|
||||
# real epydoc package. So sys.path[0], which contains the directory
|
||||
# of the script.
|
||||
import sys, os.path
|
||||
script_path = os.path.abspath(sys.path[0])
|
||||
sys.path = [p for p in sys.path if
|
||||
os.path.abspath(p) != script_path]
|
||||
|
||||
from epydoc.cli import cli
|
||||
cli()
|
||||
|
35
apps/sam/python/doc/epydoc/makedoc.py
Normal file
35
apps/sam/python/doc/epydoc/makedoc.py
Normal file
@ -0,0 +1,35 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
"""
|
||||
Make epydoc HTML documentation in the 'html' subdirectory.
|
||||
"""
|
||||
|
||||
import epydoc as epydoc_
|
||||
import inspect
|
||||
import os, sys
|
||||
|
||||
def epydoc(args):
|
||||
"""Run epydoc (command line) with given argument string."""
|
||||
os.system('python calldoc.py ' + args)
|
||||
|
||||
def makedoc():
|
||||
"""Make all epydoc HTML documentation for Python I2P library."""
|
||||
modlist = [
|
||||
'i2p',
|
||||
'i2p.eep',
|
||||
'i2p.tunnel',
|
||||
'i2p.router',
|
||||
'i2p.socket',
|
||||
'i2p.select',
|
||||
'i2p.samclasses',
|
||||
'i2p.CGIHTTPServer',
|
||||
'i2p.SimpleHTTPServer',
|
||||
'i2p.BaseHTTPServer',
|
||||
'i2p.SocketServer',
|
||||
'i2p.pylib'
|
||||
]
|
||||
modlist.reverse()
|
||||
epydoc('--html ' + ' '.join(modlist))
|
||||
|
||||
if __name__ == '__main__':
|
||||
makedoc()
|
@ -4,9 +4,7 @@
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<script type="text/javascript" src="/~barnesc/wiki/stylesheets/wikibits.js"></script>
|
||||
<style type='text/css'><!--
|
||||
@import url("/~barnesc/wiki/stylesheets/wikiprintable.css");
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
|
91
apps/sam/python/doc/guide/i2p.BaseHTTPServer.html
Normal file
91
apps/sam/python/doc/guide/i2p.BaseHTTPServer.html
Normal file
@ -0,0 +1,91 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="en"><head><title>User's Guide:i2p.BaseHTTPServer - Wikipedia</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<style type='text/css'><!--
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
#article { margin-left: 152px; margin-right: 4px; }
|
||||
/* */
|
||||
//--></style>
|
||||
</head>
|
||||
|
||||
<body bgcolor='#FFFFFF' onload=''>
|
||||
<h1 class='pagetitle'>User's Guide:i2p.BaseHTTPServer</h1><p class='subtitle'>From Python-I2P.
|
||||
|
||||
|
||||
<div class='bodytext'>
|
||||
Emulates Python BaseHTTPServer module using I2P sockets.
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Overview"> Overview </a></h2>
|
||||
|
||||
<p>
|
||||
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-BaseHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-BaseHTTPServer.html">http://www.python.org/doc/current/lib/module-BaseHTTPServer.html</a>
|
||||
|
||||
<p>
|
||||
To get a server going, use:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import BaseHTTPServer
|
||||
>>> BaseHTTPServer.test().
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
Consult the documentation for function test() to change basic server settings, such as the session name.
|
||||
|
||||
<p>
|
||||
A fully customizable example:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import BaseHTTPServer
|
||||
>>> session = "mytestxxx.i2p" # SAM session name
|
||||
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
|
||||
>>> class MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): pass
|
||||
>>> httpd = MyServer(session, MyRequestHandler)
|
||||
>>> httpd.socket.dest
|
||||
(Base64 Destination of server)
|
||||
>>> httpd.serve_forever()
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Classes"> Classes </a></h2>
|
||||
|
||||
<p>
|
||||
class <strong>BaseHTTPRequestHandler</strong>
|
||||
<ul ><pre>
|
||||
Same interface as Python class BaseHTTPServer.BaseHTTPRequestHandler.
|
||||
</pre>
|
||||
</ul >
|
||||
class <strong>HTTPServer</strong>
|
||||
<ul ><pre>
|
||||
Same interface as Python class BaseHTTPServer.HTTPServer.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Functions"> Functions </a></h2>
|
||||
|
||||
<p>
|
||||
<strong>test</strong>(HandlerClass=BaseHTTPRequestHandler, ServerClass=HTTPServer, protocol='HTTP/1.0', session='mytestxxx.i2p')
|
||||
<ul ><pre>
|
||||
Test the HTTP request handler class.
|
||||
This runs an I2P TCP server under SAM session 'session'. If a single command
|
||||
line argument is given, the argument is used instead as the SAM session name.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
</div>
|
||||
</body></html>
|
90
apps/sam/python/doc/guide/i2p.CGIHTTPServer.html
Normal file
90
apps/sam/python/doc/guide/i2p.CGIHTTPServer.html
Normal file
@ -0,0 +1,90 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="en"><head><title>User's Guide:i2p.CGIHTTPServer - Wikipedia</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<style type='text/css'><!--
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
#article { margin-left: 152px; margin-right: 4px; }
|
||||
/* */
|
||||
//--></style>
|
||||
</head>
|
||||
|
||||
<body bgcolor='#FFFFFF' onload=''>
|
||||
<h1 class='pagetitle'>User's Guide:i2p.CGIHTTPServer</h1><p class='subtitle'>From Python-I2P.
|
||||
|
||||
|
||||
<div class='bodytext'>
|
||||
Module <code >i2p.CGIHTTPServer</code > emulates the Python CGIHTTPServer module using I2P sockets.
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Overview"> Overview </a></h2>
|
||||
|
||||
<p>
|
||||
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-CGIHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-CGIHTTPServer.html">http://www.python.org/doc/current/lib/module-CGIHTTPServer.html</a>
|
||||
|
||||
<p>
|
||||
To get a server going, use:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import CGIHTTPServer
|
||||
>>> CGIHTTPServer.test().
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
Consult the documentation for function test() to change basic server settings, such as the session name.
|
||||
|
||||
<p>
|
||||
A fully customizable example:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import BaseHTTPServer, CGIHTTPServer
|
||||
>>> session = "mytestxxx.i2p" # SAM session name
|
||||
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
|
||||
>>> class MyRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler): pass
|
||||
>>> httpd = MyServer(session, MyRequestHandler)
|
||||
>>> httpd.socket.dest
|
||||
(Base64 Destination of server)
|
||||
>>> httpd.serve_forever()
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Classes"> Classes </a></h2>
|
||||
|
||||
<p>
|
||||
class <strong>CGIHTTPRequestHandler</strong>
|
||||
<ul ><pre>
|
||||
Same interface as Python class CGIHTTPServer.CGIHTTPRequestHandler.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Functions"> Functions </a></h2>
|
||||
|
||||
<p>
|
||||
|
||||
<strong>test</strong>(HandlerClass=CGIHTTPRequestHandler,
|
||||
ServerClass=i2p.BaseHTTPServer.HTTPServer,
|
||||
session='mytestxxx.i2p')
|
||||
<ul ><pre>
|
||||
Test the HTTP CGI request handler class.
|
||||
This runs an I2P TCP server under SAM session 'session'. If a single
|
||||
command line argument is given, the argument is used instead as the SAM session
|
||||
name.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
</div>
|
||||
</body></html>
|
88
apps/sam/python/doc/guide/i2p.SimpleHTTPServer.html
Normal file
88
apps/sam/python/doc/guide/i2p.SimpleHTTPServer.html
Normal file
@ -0,0 +1,88 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="en"><head><title>User's Guide:i2p.SimpleHTTPServer - Wikipedia</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<style type='text/css'><!--
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
#article { margin-left: 152px; margin-right: 4px; }
|
||||
/* */
|
||||
//--></style>
|
||||
</head>
|
||||
|
||||
<body bgcolor='#FFFFFF' onload=''>
|
||||
<h1 class='pagetitle'>User's Guide:i2p.SimpleHTTPServer</h1><p class='subtitle'>From Python-I2P.
|
||||
|
||||
|
||||
<div class='bodytext'>
|
||||
Emulates Python SimpleHTTPServer module using I2P sockets.
|
||||
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Overview"> Overview </a></h2>
|
||||
|
||||
<p>
|
||||
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html">http://www.python.org/doc/current/lib/module-SimpleHTTPServer.html</a>
|
||||
|
||||
<p>
|
||||
To get a server going, use:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import SimpleHTTPServer
|
||||
>>> SimpleHTTPServer.test().
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
Consult the documentation for function test() to change basic server settings, such as the session name.
|
||||
|
||||
<p>
|
||||
A fully customizable example:
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
>>> from i2p import BaseHTTPServer, SimpleHTTPServer
|
||||
>>> session = "mytestxxx.i2p" # SAM session name
|
||||
>>> class MyServer(BaseHTTPServer.HTTPServer): pass
|
||||
>>> class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): pass
|
||||
>>> httpd = MyServer(session, MyRequestHandler)
|
||||
>>> httpd.socket.dest
|
||||
(Base64 Destination of server)
|
||||
>>> httpd.serve_forever()
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Classes"> Classes </a></h2>
|
||||
|
||||
<p>
|
||||
class <strong>SimpleHTTPRequestHandler</strong>
|
||||
<ul ><pre>
|
||||
Same interface as Python class SimpleHTTPServer.SimpleHTTPRequestHandler.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Functions"> Functions </a></h2>
|
||||
|
||||
<p>
|
||||
|
||||
<strong>test</strong>(HandlerClass=SimpleHTTPRequestHandler, ServerClass= i2p.BaseHTTPServer.HTTPServer, session='mytestxxx.i2p')
|
||||
<ul ><pre>
|
||||
Test the HTTP simple request handler class.
|
||||
This runs an I2P TCP server under SAM session 'session'. If a single command
|
||||
line argument is given, the argument is used instead as the SAM session name.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
</div>
|
||||
|
||||
</body></html>
|
50
apps/sam/python/doc/guide/i2p.SocketServer.html
Normal file
50
apps/sam/python/doc/guide/i2p.SocketServer.html
Normal file
@ -0,0 +1,50 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="en"><head><title>User's Guide:i2p.SocketServer - Wikipedia</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<style type='text/css'><!--
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
#article { margin-left: 152px; margin-right: 4px; }
|
||||
/* */
|
||||
//--></style>
|
||||
</head>
|
||||
|
||||
<body bgcolor='#FFFFFF' onload=''>
|
||||
<h1 class='pagetitle'>User's Guide:i2p.SocketServer</h1><p class='subtitle'>From Python-I2P.
|
||||
|
||||
|
||||
<div class='bodytext'>
|
||||
Emulates Python SocketServer module using I2P sockets.
|
||||
|
||||
|
||||
<p>
|
||||
The Python module is described at <a href="http://www.python.org/doc/current/lib/module-SocketServer.html" class='printable' title="http://www.python.org/doc/current/lib/module-SocketServer.html">http://www.python.org/doc/current/lib/module-SocketServer.html</a>
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Classes"> Classes </a></h2>
|
||||
|
||||
<p>
|
||||
<ul ><pre>
|
||||
BaseRequestHandler
|
||||
BaseServer
|
||||
DatagramRequestHandler
|
||||
ForkingMixIn
|
||||
ForkingTCPServer
|
||||
ForkingUDPServer
|
||||
StreamRequestHandler
|
||||
TCPServer
|
||||
ThreadingMixIn
|
||||
ThreadingTCPServer
|
||||
ThreadingUDPServer
|
||||
UDPServer
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
</div>
|
||||
</body></html>
|
@ -4,9 +4,7 @@
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<script type="text/javascript" src="/~barnesc/wiki/stylesheets/wikibits.js"></script>
|
||||
<style type='text/css'><!--
|
||||
@import url("/~barnesc/wiki/stylesheets/wikiprintable.css");
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
|
@ -4,9 +4,7 @@
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<script type="text/javascript" src="/~barnesc/wiki/stylesheets/wikibits.js"></script>
|
||||
<style type='text/css'><!--
|
||||
@import url("/~barnesc/wiki/stylesheets/wikiprintable.css");
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
@ -24,19 +22,23 @@ Package <code >i2p</code > is a container package for more specific modules.
|
||||
|
||||
|
||||
<p>
|
||||
It exports the following names:
|
||||
It contains the following modules:
|
||||
|
||||
<p>
|
||||
<ul >
|
||||
|
||||
<pre> <a href="./i2p.sam.html" class='printable' title ="User's Guide:i2p.sam">sam</a>
|
||||
<a href="./i2p.eep.html" class='printable' title ="User's Guide:i2p.eep">eep</a>
|
||||
<a href="./i2p.router.html" class='printable' title ="User's Guide:i2p.router">router</a>
|
||||
<a href="#Error" class='printable' title ="User's Guide:i2p">Error</a>
|
||||
<a href="#RouterError" class='printable' title ="User's Guide:i2p">RouterError</a>
|
||||
<pre> <a href="i2p.BaseHTTPServer.html" class='printable' title ="User's Guide:i2p.BaseHTTPServer">i2p.BaseHTTPServer</a> (Emulate Python BaseHTTPServer module)
|
||||
<a href="i2p.CGIHTTPServer.html" class='printable' title ="User's Guide:i2p.CGIHTTPServer">i2p.CGIHTTPServer</a> (Emulate Python CGIHTTPServer module)
|
||||
<a href="i2p.eep.html" class='printable' title ="User's Guide:i2p.eep">i2p.eep</a> (Retrieve eepsites)
|
||||
<a href="i2p.router.html" class='printable' title ="User's Guide:i2p.router">i2p.router</a> (Manage the I2P router)
|
||||
<a href="i2p.select.html" class='printable' title ="User's Guide:i2p.select">i2p.select</a> (Emulate Python select module)
|
||||
<a href="i2p.SimpleHTTPServer.html" class='printable' title ="User's Guide:i2p.SimpleHTTPServer">i2p.SimpleHTTPServer</a> (Emulate Python SimpleHTTPServer module)
|
||||
<a href="i2p.socket.html" class='printable' title ="User's Guide:i2p.socket">i2p.socket</a> (Send and receive across the I2P network)
|
||||
<a href="i2p.SocketServer.html" class='printable' title ="User's Guide:i2p.SocketServer">i2p.SocketServer</a> (Emulate Python SocketServer module)
|
||||
<a href="i2p.tunnel.html" class='printable' title ="User's Guide:i2p.tunnel">i2p.tunnel</a> (Exchange data between I2P and regular sockets)
|
||||
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
class <strong>Error</strong>(Exception):
|
||||
<ul >
|
||||
|
||||
@ -46,7 +48,6 @@ class <strong>Error</strong>(Exception):
|
||||
|
||||
<p>
|
||||
class <strong>RouterError</strong>(Error):
|
||||
|
||||
<ul >
|
||||
|
||||
<pre> Could not connect to router.
|
||||
@ -55,7 +56,4 @@ class <strong>RouterError</strong>(Error):
|
||||
|
||||
<p>
|
||||
</div>
|
||||
<p><em>
|
||||
</em><!-- Time since request: 0.85 secs. -->
|
||||
|
||||
</body></html>
|
@ -4,9 +4,7 @@
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<script type="text/javascript" src="/~barnesc/wiki/stylesheets/wikibits.js"></script>
|
||||
<style type='text/css'><!--
|
||||
@import url("/~barnesc/wiki/stylesheets/wikiprintable.css");
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
@ -45,8 +43,8 @@ Module <code >i2p.router</code > allows Python programs to control the I2P route
|
||||
Find the absolute path to a locally installed I2P router.
|
||||
|
||||
An I2P installation is located by looking in the
|
||||
environment I2P, then in PATH, then in the dir argument
|
||||
given to the function. It looks for startRouter.sh or
|
||||
dir argument given to the function, then in the environment
|
||||
I2P, then in PATH. It looks for startRouter.sh or
|
||||
startRouter.bat. Raises ValueError if an I2P installation
|
||||
could not be located.
|
||||
</pre>
|
||||
|
@ -1,308 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="en"><head><title>User's Guide:i2p.sam - Wikipedia</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<script type="text/javascript" src="/~barnesc/wiki/stylesheets/wikibits.js"></script>
|
||||
<style type='text/css'><!--
|
||||
@import url("/~barnesc/wiki/stylesheets/wikiprintable.css");
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
#article { margin-left: 152px; margin-right: 4px; }
|
||||
/* */
|
||||
//--></style>
|
||||
</head>
|
||||
|
||||
<body bgcolor='#FFFFFF' onload=''>
|
||||
<h1 class='pagetitle'>User's Guide:i2p.sam</h1><p class='subtitle'>From Python-I2P.
|
||||
|
||||
|
||||
<div class='bodytext'><a name="top"></a>
|
||||
Module <code >i2p.sam</code > allows Python programs to access the <a href="./samproxy.html" class='printable' title ="SAM proxy">SAM proxy</a>.
|
||||
|
||||
|
||||
<p>
|
||||
With this module, a program can send stream data, datagrams, and raw packets across the I2P network.
|
||||
|
||||
<p>
|
||||
|
||||
<p><table border="0" id="toc"><tr><td align="center">
|
||||
<b>Table of contents</b></td></tr><tr id='tocinside'><td>
|
||||
<div style="margin-bottom:0px;">
|
||||
<A CLASS="internal" HREF="#Sockets">1 Sockets</A><BR>
|
||||
</div>
|
||||
<div style="margin-bottom:0px;">
|
||||
<A CLASS="internal" HREF="#Tunnels">2 Tunnels</A><BR>
|
||||
</div>
|
||||
<div style="margin-left:2em;">
|
||||
<A CLASS="internal" HREF="#Tunnel_Server">2.1 Tunnel Server</A><BR>
|
||||
|
||||
<A CLASS="internal" HREF="#Tunnel_Client">2.2 Tunnel Client</A><BR>
|
||||
</div>
|
||||
<div style="margin-bottom:0px;">
|
||||
<A CLASS="internal" HREF="#Errors">3 Errors</A><BR>
|
||||
</div>
|
||||
<div style="margin-bottom:0px;">
|
||||
<A CLASS="internal" HREF="#Constants">4 Constants</A><BR>
|
||||
</div>
|
||||
</td></tr></table><P>
|
||||
<h2><a name="Sockets"> Sockets </a></h2>
|
||||
|
||||
<p>
|
||||
|
||||
<strong>socket</strong>(session, type, samaddr='127.0.0.1:7656', **kwargs)
|
||||
<ul ><pre>
|
||||
Create a new socket. Argument session should be a session
|
||||
name -- if the name has not yet been used, an I2P
|
||||
Destination will be created for it, otherwise, the
|
||||
existing Destination will be re-used. An empty session
|
||||
string causes a transient session to be created. Argument
|
||||
type is one of SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW.
|
||||
|
||||
I2P configuration keyword arguments:
|
||||
|
||||
* in_depth - depth of incoming tunnel (default 2)
|
||||
* out_depth - depth of outgoing tunnel (default 2)
|
||||
|
||||
A single session may be shared by more than one socket, if
|
||||
the sockets are the same type, and if the sockets are
|
||||
created within the same Python process. The socket
|
||||
objects are multithread-safe.
|
||||
|
||||
Examples:
|
||||
a = i2p.socket('Alice', i2p.SOCK_STREAM)
|
||||
b = i2p.socket('Bob', i2p.SOCK_DGRAM,
|
||||
in_depth=2, out_depth=5)
|
||||
|
||||
The created object behaves identically to a socket from
|
||||
module socket, with the following exceptions:
|
||||
|
||||
* I2P Destinations are used as address arguments [1].
|
||||
* bind is a no-op: sockets are always bound.
|
||||
* send* methods send all data and are non-blocking.
|
||||
|
||||
A given session name can only be open in a single Python
|
||||
program at a time. If you need to overcome this
|
||||
limitation, consider patching I2P.
|
||||
|
||||
[1]. Alternatively, a host name can be used as an address.
|
||||
It will be resolved using hosts.txt.
|
||||
|
||||
For details on how to use socket objects, see
|
||||
http://www.python.org/doc/current/lib/socket-objects.html
|
||||
|
||||
See the examples directory for code examples.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>socket()</strong> object properties:
|
||||
<ul >
|
||||
|
||||
<pre> dest - Local I2P Destination of socket
|
||||
session - Session name
|
||||
type - Socket type: SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>poll</strong>()
|
||||
|
||||
<ul >
|
||||
|
||||
<pre> Returns a polling object. Works on SAM sockets and Python sockets.
|
||||
See <a href='http://www.python.org/doc/current/lib/module-select.html' class='printable' title="http://www.python.org/doc/current/lib/module-select.html">select.poll()</a> in the Python library for more information.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
<strong>resolve</strong>(host, samaddr='127.0.0.1:7656')
|
||||
<ul >
|
||||
|
||||
<pre> Resolve I2P host name --> I2P Destination.
|
||||
Returns the same string if host is already a Destination.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
<strong>select</strong>(readlist, writelist, errlist, timeout=None)
|
||||
<ul >
|
||||
|
||||
<pre> Performs a select call. Works on SAM sockets and Python sockets.
|
||||
See <a href='http://www.python.org/doc/current/lib/module-select.html' class='printable' title="http://www.python.org/doc/current/lib/module-select.html">select.select()</a> in the Python library for more information.
|
||||
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Tunnels"> Tunnels </a></h2>
|
||||
|
||||
<p>
|
||||
Tunnels allow stream sockets to be joined, so that connections to a listening socket are relayed to one or more sending sockets. This allows an ordinary web server to be exposed as an I2P Destination, or an I2P Destination to be bound as a local port, and so on.
|
||||
|
||||
<p>
|
||||
class <strong>Tunnel</strong>(self, receive, make_send, nconnect=-1, timeout=60.0)
|
||||
<ul ><pre>
|
||||
A Tunnel relays connections from a 'receive' socket to one
|
||||
or more 'send' sockets. The receive socket must be bound
|
||||
and listening. For each incoming connection, a new send
|
||||
socket is created by calling make_send(). Data is then
|
||||
exchanged between the created streams until one socket is
|
||||
closed. nconnect is the maximum number of simultaneous
|
||||
connections (-1 for infinite), and timeout is the time that
|
||||
a single connection can last for (None allows a connection
|
||||
to last forever).
|
||||
|
||||
Sockets must accept stream traffic and support the Python
|
||||
socket interface. A separate daemonic thread is created to
|
||||
manage the tunnel. For high performance, make_send() should
|
||||
make a socket and connect in non-blocking mode (you should
|
||||
catch and discard the sam.BlockError or socket.error due to
|
||||
executing connect on a non-blocking socket).
|
||||
|
||||
Security Note:
|
||||
A firewall is needed to maintain the end user's anonymity.
|
||||
An attacker could keep a tunnel socket open by pinging it
|
||||
regularly. The accepted sockets from 'receive' must prevent
|
||||
this by closing down eventually.
|
||||
|
||||
Socket errors do not cause the Tunnel to shut down.
|
||||
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
<strong>close</strong>()
|
||||
<ul >
|
||||
|
||||
<pre> Close all connections made for this tunnel.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h3><a name="Tunnel_Server"> Tunnel Server </a></h3>
|
||||
|
||||
<p>
|
||||
class <strong>TunnelServer</strong>(session, port, samaddr='127.0.0.1:7656', nconnect=-1, timeout=None, **kwargs)
|
||||
<ul ><pre>
|
||||
Tunnels incoming SAM streams --> localhost:port.
|
||||
|
||||
nconnect and timeout are the maximum number of connections
|
||||
and maximum time per connection. All other arguments are
|
||||
passed to sam.socket(). This call blocks until the tunnel
|
||||
is ready.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
<strong>TunnelServer</strong> properties:
|
||||
|
||||
<ul ><pre>
|
||||
dest - I2P Destination of server.
|
||||
session - Session name for server.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h3><a name="Tunnel_Client"> Tunnel Client </a></h3>
|
||||
|
||||
<p>
|
||||
class <strong>TunnelClient</strong>(session, port, dest, samaddr='127.0.0.1:7656', nconnect=-1, timeout=None, **kwargs)
|
||||
<ul ><pre>
|
||||
|
||||
Derived from Tunnel.
|
||||
Tunnels localhost:port --> I2P Destination dest.
|
||||
|
||||
A session named 'session' is created locally, for purposes
|
||||
of routing to 'dest'. nconnect and timeout are the maximum
|
||||
number of connections and maximum time per connection. All
|
||||
other arguments are passed to sam.socket(). This call blocks
|
||||
until the tunnel is ready.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
<strong>TunnelClient</strong> properties:
|
||||
<ul ><pre>
|
||||
dest - Local Destination used for routing.
|
||||
remotedest - Remote Destination.
|
||||
session - Session name for local Destination.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Errors"> Errors </a></h2>
|
||||
|
||||
<p>
|
||||
class <strong>Error</strong>(i2p.Error)
|
||||
<ul ><pre>
|
||||
Base class for all SAM errors.
|
||||
</pre>
|
||||
</ul >
|
||||
class <strong>BlockError</strong>(Error)
|
||||
|
||||
<ul ><pre>
|
||||
Socket call would have blocked.
|
||||
</pre>
|
||||
</ul >
|
||||
class <strong>ClosedError</strong>(Error)
|
||||
<ul ><pre>
|
||||
A command was used on a socket that closed gracefully.
|
||||
</pre>
|
||||
</ul >
|
||||
class <strong>NetworkError</strong>(Error)
|
||||
<ul ><pre>
|
||||
|
||||
Network error occurred within I2P.
|
||||
|
||||
The error object is a 2-tuple: (errtag, errdesc).
|
||||
errtag is a SAM error string,
|
||||
errdesc is a human readable error description.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Constants"> Constants </a></h2>
|
||||
|
||||
<p>
|
||||
<strong>Socket types</strong>
|
||||
<ul ><pre>
|
||||
SOCK_STREAM = 1
|
||||
SOCK_DGRAM = 2
|
||||
SOCK_RAW = 3
|
||||
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>Packet sizes</strong>
|
||||
<ul ><pre>
|
||||
MAX_DGRAM = 31744 # Maximum size for datagram packet
|
||||
MAX_RAW = 32768 # Maximum size for raw packet
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>Flags for recv()</strong>
|
||||
<ul ><pre>
|
||||
MSG_DONTWAIT = 128 # Don't block
|
||||
MSG_PEEK = 2 # Peek at incoming data
|
||||
MSG_WAITALL = 64 # Wait for all data or error
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>Polling flags</strong>
|
||||
|
||||
<ul ><pre>
|
||||
POLLIN = 1
|
||||
POLLOUT = 4
|
||||
POLLERR = 8
|
||||
POLLHUP = 16
|
||||
POLLNVAL = 32
|
||||
POLLPRI = 1
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
</div>
|
||||
<p><em>
|
||||
</em><!-- Time since request: 0.86 secs. -->
|
||||
</body></html>
|
60
apps/sam/python/doc/guide/i2p.select.html
Normal file
60
apps/sam/python/doc/guide/i2p.select.html
Normal file
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="en"><head><title>User's Guide:i2p.select - Wikipedia</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<style type='text/css'><!--
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
#article { margin-left: 152px; margin-right: 4px; }
|
||||
/* */
|
||||
//--></style>
|
||||
</head>
|
||||
|
||||
<body bgcolor='#FFFFFF' onload=''>
|
||||
<h1 class='pagetitle'>User's Guide:i2p.select</h1><p class='subtitle'>From Python-I2P.
|
||||
|
||||
|
||||
<div class='bodytext'>
|
||||
Module <code >i2p.select</code > emulates the Python module <code >select</code >.
|
||||
|
||||
|
||||
<p>
|
||||
With this module, a program can perform select and poll commands on traditional and I2P sockets.
|
||||
|
||||
<p>
|
||||
<strong>poll</strong>()
|
||||
<ul >
|
||||
|
||||
<pre> Returns a polling object. Works on SAM sockets and Python sockets.
|
||||
See <a href='http://www.python.org/doc/current/lib/module-select.html' class='printable' title="http://www.python.org/doc/current/lib/module-select.html">select.poll()</a> in the Python library for more information.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
<strong>select</strong>(readlist, writelist, errlist, timeout=None)
|
||||
<ul >
|
||||
|
||||
<pre> Performs a select call. Works on SAM sockets and Python sockets.
|
||||
See <a href='http://www.python.org/doc/current/lib/module-select.html' class='printable' title="http://www.python.org/doc/current/lib/module-select.html">select.select()</a> in the Python library for more information.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
<strong>Polling flags</strong>
|
||||
|
||||
<ul ><pre>
|
||||
POLLIN = 1
|
||||
POLLOUT = 4
|
||||
POLLERR = 8
|
||||
POLLHUP = 16
|
||||
POLLNVAL = 32
|
||||
POLLPRI = 1
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
</div>
|
||||
</body></html>
|
376
apps/sam/python/doc/guide/i2p.socket.html
Normal file
376
apps/sam/python/doc/guide/i2p.socket.html
Normal file
@ -0,0 +1,376 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html lang="en"><head><title>User's Guide:i2p.socket - Wikipedia</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<meta name="robots" content="index,follow">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<style type='text/css'><!--
|
||||
/*/*/
|
||||
a.new, #quickbar a.new { color: #CC2200; }
|
||||
#quickbar { position: absolute; top: 4px; left: 4px; border-right: 1px solid gray; }
|
||||
#article { margin-left: 152px; margin-right: 4px; }
|
||||
/* */
|
||||
//--></style>
|
||||
</head>
|
||||
|
||||
<body bgcolor='#FFFFFF' onload=''>
|
||||
<h1 class='pagetitle'>User's Guide:i2p.socket</h1><p class='subtitle'>From Python-I2P.
|
||||
|
||||
|
||||
<div class='bodytext'><a name="top"></a>
|
||||
Module <code >i2p.socket</code > allows Python programs to access the <a href="samproxy.html" class='printable' title ="SAM proxy">SAM proxy</a>. It emulates the Python module <code >socket</code >.
|
||||
|
||||
|
||||
<p>
|
||||
With this module, a program can send stream data, datagrams, and raw packets across the I2P network.
|
||||
|
||||
<p>
|
||||
|
||||
<p><table border="0" id="toc"><tr><td align="center">
|
||||
<b>Table of contents</b></td></tr><tr id='tocinside'><td>
|
||||
<div style="margin-bottom:0px;">
|
||||
<A CLASS="internal" HREF="#Sockets">1 Sockets</A><BR>
|
||||
</div>
|
||||
<div style="margin-bottom:0px;">
|
||||
<A CLASS="internal" HREF="#Functions">2 Functions</A><BR>
|
||||
</div>
|
||||
<div style="margin-bottom:0px;">
|
||||
<A CLASS="internal" HREF="#Errors">3 Errors</A><BR>
|
||||
|
||||
</div>
|
||||
<div style="margin-bottom:0px;">
|
||||
<A CLASS="internal" HREF="#Constants">4 Constants</A><BR>
|
||||
</div>
|
||||
</td></tr></table><P>
|
||||
<h2><a name="Sockets"> Sockets </a></h2>
|
||||
|
||||
<p>
|
||||
class <strong>socket</strong>(session, type, samaddr='127.0.0.1:7656', **kwargs)
|
||||
<ul ><pre>
|
||||
Create a new socket. Argument session should be a session
|
||||
name -- if the name has not yet been used, an I2P
|
||||
Destination will be created for it, otherwise, the
|
||||
existing Destination will be re-used. An empty session
|
||||
string causes a transient session to be created. Argument
|
||||
type is one of SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW.
|
||||
|
||||
I2P configuration keyword arguments:
|
||||
|
||||
- in_depth - depth of incoming tunnel (default 2)
|
||||
- out_depth - depth of outgoing tunnel (default 2)
|
||||
|
||||
A single session may be shared by more than one socket, if
|
||||
the sockets are the same type, and if the sockets are
|
||||
created within the same Python process. The socket
|
||||
objects are multithread-safe.
|
||||
|
||||
Examples:
|
||||
>>> a = i2p.socket('Alice', i2p.SOCK_STREAM)
|
||||
>>> b = i2p.socket('Bob', i2p.SOCK_DGRAM,
|
||||
in_depth=2, out_depth=5)
|
||||
|
||||
The created object behaves identically to a socket from
|
||||
module socket, with the following exceptions:
|
||||
|
||||
* I2P Destinations are used as address arguments [1].
|
||||
* bind is a no-op: sockets are always bound.
|
||||
* send* methods send all data and are non-blocking.
|
||||
|
||||
A given session name can only be open in a single Python
|
||||
program at a time. If you need to overcome this
|
||||
limitation, consider patching I2P.
|
||||
|
||||
[1]. Alternatively, a host name can be used as an address.
|
||||
It will be resolved using hosts.txt.
|
||||
|
||||
</pre>
|
||||
|
||||
|
||||
<pre> For details on how to use socket objects, see
|
||||
<a href="http://www.python.org/doc/current/lib/socket-objects.html" class='printable' title="http://www.python.org/doc/current/lib/socket-objects.html">http://www.python.org/doc/current/lib/socket-objects.html</a>
|
||||
</pre>
|
||||
<p>
|
||||
|
||||
<pre> See the examples directory for code examples.
|
||||
</pre>
|
||||
</ul >
|
||||
The class <code >socket</code > defines the following properties:
|
||||
|
||||
<ul >
|
||||
|
||||
<pre> dest - Local I2P Destination of socket
|
||||
session - Session name
|
||||
type - Socket type: SOCK_STREAM, SOCK_DGRAM, or SOCK_RAW.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
The class <code >socket</code > defines the following methods:
|
||||
|
||||
<p>
|
||||
<strong>accept</strong>(self)
|
||||
<ul ><pre>
|
||||
Accept an incoming connection. The socket must be type SOCK_STREAM, and
|
||||
listen() must be called prior to this command. The return value is (conn,
|
||||
remotedest), where conn is a new socket object made for the connection, and
|
||||
remotedest is the remote Destination from which the connection was made.
|
||||
|
||||
Example:
|
||||
|
||||
>>> from i2p import socket
|
||||
>>> s = socket.socket('Alice', socket.SOCK_STREAM)
|
||||
>>> s.listen(10)
|
||||
|
||||
This prepares the server. Now accept an incoming connection:
|
||||
|
||||
>>> c, remotedest = s.accept()
|
||||
>>> c.send('hello world!')
|
||||
|
||||
If accept() is called on a socket that is in non-blocking mode or has a
|
||||
timeout, i2p.socket.BlockError or i2p.socket.Timeout may be raised. This
|
||||
indicates that no incoming connection is currently available.
|
||||
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>bind</strong>(self, address)
|
||||
<ul ><pre>
|
||||
Does nothing. Provided for compatibility with the Python socket command
|
||||
bind(), which binds a server to a port.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>close</strong>(self)
|
||||
<ul ><pre>
|
||||
Closes the socket. It is an error to call any method other than recv() or
|
||||
recvfrom() on a closed socket. For streams, the receive methods return data
|
||||
that was received prior to the closing of the socket. For datagram and raw
|
||||
sockets, the receive methods cannot be used on a closed socket.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>connect</strong>(self, address)
|
||||
|
||||
<ul ><pre>
|
||||
Connect to a remote dest, identified in local SAM bridge's hosts file as host
|
||||
'address'.
|
||||
|
||||
For example:
|
||||
|
||||
>>> s.connect('duck.i2p')
|
||||
|
||||
Alternatively, you can use a full base64 Destination:
|
||||
Example:
|
||||
|
||||
>>> s.connect('238797sdfh2k34kjh....AAAA')
|
||||
|
||||
If connect() is called on a socket that is in non-blocking mode or has a
|
||||
timeout, i2p.socket.BlockError or i2p.socket.Timeout may be raised. This
|
||||
indicates that the connection is still being initiated. Use i2p.select.select()
|
||||
to determine when the connection is ready.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>connect_ex</strong>(self, address)
|
||||
<ul ><pre>
|
||||
Like connect(), but return any error that is raised. Returns None if no error
|
||||
is raised.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>getpeername</strong>(self)
|
||||
|
||||
<ul ><pre>
|
||||
Get the remote Destination associated with the socket. This is equivalent to
|
||||
s.remotedest, and is provided for compatibility with the Python socket module.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>getsockname</strong>(self)
|
||||
<ul ><pre>
|
||||
Get the local Destination associated with the socket. This is equivalent to
|
||||
s.dest, and is provided for compatibility with the Python socket module.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>gettimeout</strong>(self)
|
||||
<ul ><pre>
|
||||
Get the timeout value.
|
||||
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>listen</strong>(self, backlog)
|
||||
<ul ><pre>
|
||||
Listen for connections made to the socket. This method must be called before
|
||||
accept(). The backlog argument specifies the maximum number of queued incoming
|
||||
connections.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>makefile</strong>(self, mode='r', bufsize=-1)
|
||||
<ul ><pre>
|
||||
Return a file object for the socket. See socket.makefile() in the Python
|
||||
documentation for more information.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>recv</strong>(self, bufsize, flags=0)
|
||||
|
||||
<ul ><pre>
|
||||
Receive string data from the socket.
|
||||
|
||||
The maximum amount of data to be received is given by bufsize. If bufsize is
|
||||
zero, this function returns an empty string immediately. If bufsize is nonzero,
|
||||
this function blocks until at least one character is available for reading. If
|
||||
the socket has been closed, an empty string is returned as an end of file
|
||||
indicator.
|
||||
|
||||
If recv() is called on a socket that is in non-blocking mode or has a timeout,
|
||||
i2p.socket.BlockError or i2p.socket.Timeout will be raised if data is not
|
||||
available within the given timeframe.
|
||||
|
||||
For a datagram or raw socket, the first bufsize characters of the packet are
|
||||
read, and the remainder of the packet is discarded. To read the entire packet,
|
||||
use bufsize = -1.
|
||||
|
||||
For datagram and raw sockets, the packet may originate from any Destination.
|
||||
Use recvfrom() with datagrams to determine the Destination from which the
|
||||
packet was received.
|
||||
|
||||
The flags argument can be a bitwise OR of MSG_PEEK, MSG_WAITALL, and/or
|
||||
MSG_DONTWAIT. MSG_PEEK indicates that any data read should not be removed from
|
||||
the socket's incoming buffer. MSG_WAITALL indicates to wait for exactly bufsize
|
||||
characters or an error. MSG_DONTWAIT indicates that the recv() command should
|
||||
not block execution.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>recvfrom</strong>(self, bufsize, flags=0)
|
||||
<ul ><pre>
|
||||
Like recv(), but returns a tuple (data, remoteaddr), where data is the string
|
||||
data received, and remoteaddr is the remote Destination.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>send</strong>(self, string, flags=0)
|
||||
<ul ><pre>
|
||||
Sends string data to a remote Destination.
|
||||
|
||||
For a stream, connect() must be called prior to send(). Once close() is called,
|
||||
no further data can be sent, and the stream cannot be re-opened.
|
||||
|
||||
For datagram and raw sockets, connect() only specifies a Destination to which
|
||||
packets are sent to. send() will then send a packet to the given Destination.
|
||||
connect() can be used multiple times.
|
||||
|
||||
The send() command never blocks execution. The flags argument is ignored.
|
||||
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>sendall</strong>(self, string, flags=0)
|
||||
<ul ><pre>
|
||||
Identical to send().
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>sendto</strong>(self, string, flags, address)
|
||||
<ul ><pre>
|
||||
Send a packet to the given Destination.
|
||||
|
||||
Only valid for datagram and raw sockets. The address argument should be either
|
||||
a name from the hosts file, or a base64 Destination.
|
||||
|
||||
The sendto() command never blocks execution. The flags argument is ignored.
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>setblocking</strong>(self, flag)
|
||||
|
||||
<ul ><pre>
|
||||
Set blocking or non-blocking mode for the socket.
|
||||
|
||||
If flag is True, any method called on the socket will hang until the method has
|
||||
completed. If flag is False, all methods will raise i2p.socket.BlockError() if
|
||||
they cannot complete instantly.
|
||||
|
||||
s.setblocking(False) is equivalent to s.settimeout(0); s.setblocking(True) is
|
||||
equivalent to s.settimeout(None).
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>settimeout</strong>(self, value)
|
||||
<ul ><pre>
|
||||
Set a timeout for the socket.
|
||||
|
||||
The value argument should be a timeout value in seconds, or None. None is
|
||||
equivalent to an infinite timeout.
|
||||
|
||||
A socket operation will raise a i2p.socket.Timeout if the operation cannot
|
||||
complete within in the specified time limit.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Functions"> Functions </a></h2>
|
||||
|
||||
<p>
|
||||
Functions defined in module <code >i2p.socket</code >:
|
||||
|
||||
<p>
|
||||
<strong>resolve</strong>(host, samaddr='127.0.0.1:7656')
|
||||
<ul >
|
||||
|
||||
<pre> Resolve I2P host name --> I2P Destination.
|
||||
Returns the same string if host is already a Destination.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Errors"> Errors </a></h2>
|
||||
|
||||
<p>
|
||||
class <strong>Error</strong>(i2p.Error)
|
||||
<ul ><pre>
|
||||
Base class for all SAM errors.
|
||||
</pre>
|
||||
</ul >
|
||||
class <strong>BlockError</strong>(Error)
|
||||
|
||||
<ul ><pre>
|
||||
Socket call would have blocked.
|
||||
</pre>
|
||||
</ul >
|
||||
class <strong>ClosedError</strong>(Error)
|
||||
<ul ><pre>
|
||||
A command was used on a socket that closed gracefully.
|
||||
</pre>
|
||||
</ul >
|
||||
class <strong>NetworkError</strong>(Error)
|
||||
<ul ><pre>
|
||||
|
||||
Network error occurred within I2P.
|
||||
|
||||
The error object is a 2-tuple: (errtag, errdesc).
|
||||
errtag is a SAM error string,
|
||||
errdesc is a human readable error description.
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
|
||||
<h2><a name="Constants"> Constants </a></h2>
|
||||
|
||||
<p>
|
||||
<strong>Socket types</strong>
|
||||
<ul ><pre>
|
||||
SOCK_STREAM = 1
|
||||
SOCK_DGRAM = 2
|
||||
SOCK_RAW = 3
|
||||
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>Packet sizes</strong>
|
||||
<ul ><pre>
|
||||
MAX_DGRAM = 31744 # Maximum size for datagram packet
|
||||
MAX_RAW = 32768 # Maximum size for raw packet
|
||||
</pre>
|
||||
</ul >
|
||||
<strong>Flags for recv()</strong>
|
||||
<ul ><pre>
|
||||
MSG_DONTWAIT = 128 # Don't block
|
||||
MSG_PEEK = 2 # Peek at incoming data
|
||||
MSG_WAITALL = 64 # Wait for all data or error
|
||||
</pre>
|
||||
</ul >
|
||||
|
||||
<p>
|
||||
</div>
|
||||
</body></html>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user