From 4735508a0a54d53981b83409b88df054ae84edf6 Mon Sep 17 00:00:00 2001 From: z3d Date: Thu, 20 Aug 2009 10:21:25 +0000 Subject: [PATCH 01/31] I2PSnark. --- .../org/klomp/snark/web/I2PSnarkServlet.java | 28 +- installer/resources/themes/console/snark.css | 469 ++++++++---------- 2 files changed, 231 insertions(+), 266 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index 0606635b4..02ce8b000 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -84,7 +84,7 @@ public class I2PSnarkServlet extends HttpServlet { out.write("\n"); out.write(HEADER); out.write(""); - out.write("
"); + out.write("
"); out.write("
I2PSnark Forum\n"); Map trackers = _manager.getTrackers(); for (Iterator iter = trackers.entrySet().iterator(); iter.hasNext(); ) { @@ -95,10 +95,10 @@ public class I2PSnarkServlet extends HttpServlet { if (e < 0) continue; baseURL = baseURL.substring(e + 1); - out.write("" + name + ""); + out.write(" " + name + ""); } out.write("
\n"); - out.write("
\n" + + "
");
+        out.write("
"); out.write("\n\t"); - out.write("\n\t"); @@ -596,12 +596,12 @@ public class I2PSnarkServlet extends HttpServlet { out.write("\n\t"); out.write("\n\t"); @@ -625,14 +625,14 @@ public class I2PSnarkServlet extends HttpServlet { if (pct != 100.0) { if (peer.isInterested() && !peer.isChoking()) { out.write(""); - out.write("" + formatSize(peer.getUploadRate()) + "ps"); + out.write("" + formatSize(peer.getUploadRate()) + "ps"); } else { - out.write(""); else out.write("Choking\">"); - out.write(formatSize(peer.getUploadRate()) + "ps"); + out.write(formatSize(peer.getUploadRate()) + "ps"); } } out.write("\n\t"); diff --git a/installer/resources/themes/console/snark.css b/installer/resources/themes/console/snark.css index 13c233add..8b2a82673 100644 --- a/installer/resources/themes/console/snark.css +++ b/installer/resources/themes/console/snark.css @@ -1,335 +1,300 @@ /* Not yet complete. Subject to flux and change. dr|z3d - 07.25.09 */ body { - background-color: #eef; - color:#001; - font-family:"Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif; - font-size: 8pt; + background: #eef; + color: #001; + font: 8pt "Lucida Sans Unicode","Bitstream Vera Sans",Verdana,Tahoma,Helvetica,sans-serif; } .snarkTitle { - font-size: 12pt; - font-weight: bold; - text-align: center; + font-size: 12pt; + font-weight: bold; + text-align: center; } -.snarkRefresh:link, .snarkRefresh:visited { - text-decoration: none !important; - text-transform: uppercase !important; - padding: 0 16px; - letter-spacing: 0.05em; - font-weight: bold; - font-size: 11pt; - color: #005; - text-shadow: 0px 0px 1px rgba(0, 0, 148, 0.9); - } - - .snarkRefresh:hover{ - text-decoration: none !important; - text-transform: uppercase !important; - padding: 0 16px; - letter-spacing: 0.05em; - font-weight: bold; - font-size: 11pt; - color: #f60; - border-bottom: 3px solid #f60; - border-top: 3px solid #f60; - text-shadow: 0px 0px 1px rgba(255, 128, 0, 0.9); - } - -.snarkRefresh:active{ - text-decoration: none !important; - text-transform: uppercase !important; - padding: 0 16px; - letter-spacing: 0.05em; - font-weight: bold; - font-size: 11pt; - color: #f30; - border-bottom: 3px solid #f30; - border-top: 3px solid #f30; - text-shadow: 0px 0px 1px rgba(255, 32, 0, 0.5); - } +.snarkRefresh:link,.snarkRefresh:visited { + text-decoration: none !important; + text-transform: uppercase !important; + padding: 0 16px; + letter-spacing: 0.05em; + font-weight: bold; + font-size: 11pt; + color: #005; + text-shadow: 0px 0px 1px rgba(0,0,148,0.9); +} + +.snarkRefresh:hover { + text-decoration: none !important; + text-transform: uppercase !important; + padding: 0 16px; + letter-spacing: 0.05em; + font-weight: bold; + font-size: 11pt; + color: #f60; + border-bottom: 3px solid #f60; + border-top: 3px solid #f60; + text-shadow: 0px 0px 1px rgba(255,128,0,0.9); +} + +.snarkRefresh:active { + text-decoration: none !important; + text-transform: uppercase !important; + padding: 0 16px; + letter-spacing: 0.05em; + font-weight: bold; + font-size: 11pt; + color: #f30; + border-bottom: 3px solid #f30; + border-top: 3px solid #f30; + text-shadow: 0px 0px 1px rgba(255,32,0,0.5); +} .snarkMessages { - background-color: #f83; - font-family: "Lucida Console", "DejaVu Sans Mono", Courier, mono !important; - font-size: 9pt; - font-weight: bold; - text-align: left; - margin: 0 0px 10px 0px; - padding: 0; - border-spacing: 0px; - -moz-border-radius: 4px 0 0 0; - -khtml-border-radius: 4px; - border-radius: 4px; - border: 2px solid #930; - text-align: left; - overflow: auto; - background: #f40 url('../console/images/orangetile.png'); - color: #531; - height: 64px; - width: auto; + font: bold 9pt "Lucida Console","DejaVu Sans Mono",Courier,mono !important; + text-align: left; + margin: 0 0px 10px 0px; + padding: 0; + border-spacing: 0px; + -moz-border-radius: 4px 0 0 0; + -khtml-border-radius: 4px; + border-radius: 4px; + border: 2px solid #930; + overflow: auto; + color: #531; + height: 64px; + width: auto; + background: #f83 url('../console/images/orangetile.png'); } pre { - font-family: "Lucida Console", "DejaVu Sans Mono", Courier, mono !important; - width: 100%; - font-size: 8pt; - padding: 0; - text-align: left !important; - height: 8px; + width: 100%; + font: 8pt "Lucida Console","DejaVu Sans Mono",Courier,mono !important; + padding: 0; + text-align: left !important; + height: 8px; } table { - margin: 0px 0px 10px 0px; - border: 0px; - padding: 0px; - border-width: 0px; - border-spacing: 0px; - border-collapse: collapse; + margin: 0px 0px 10px 0px; + border: 0px; + padding: 0px; + border-spacing: 0px; + border-collapse: collapse; } th { - padding: 5px; - font-size: 8pt; - border-top: 1px outset #001; - border-bottom: 1px inset #001; - background: #f60 url('/themes/console/images/tabletitleorange.png') repeat-x; -/* text-align: right; */ - whitespace: nowrap; + padding: 4px; + font-size: 8pt; + border-top: 1px outset #001; + border-bottom: 1px inset #001; + background: #f60 url('/themes/console/images/tabletitleorange.png') repeat-x; + whitespace: nowrap; } .SnarkTorrents { - margin: 0; - border: 1px solid #001; - background-color: #f9f; + margin: 0; + border: 1px solid #001; + background: #f9f; } td { - padding: 5px; -/* text-align: right;*/ + padding: 4px; } + .snarkTorrentEven { - background-color: #fb1; - font-size: 7pt; + background: #fb1; + font-size: 7pt; } + .snarkTorrentOdd { - background-color: #fa1; - font-size: 7pt; + background: #fa1; + font-size: 7pt; } + .snarkNewTorrent { - font-size: 9pt; + font-size: 9pt; } + .snarkAddInfo { - font-size: 9pt; - line-height: 130% !important; + font-size: 9pt; + line-height: 130% !important; } + .snarkConfigTitle { - font-size: 11pt; - font-weight: bold; - text-decoration: underline; + font-size: 11pt; + font-weight: bold; + text-decoration: underline; + text-transform: uppercase; + text-shadow: 0px 0px 2px rgba(172,172,192,0.9); } .snarkConfig { - font-size: 10pt; + font-size: 10pt; + width: 100%; } .page { - background-color: #fff; - color:#310; - min-width: 800px !important; -/* max-width: 800px !important; */ - margin: 5px 0px; - padding: 10px 10px 0px 10px; - -moz-border-radius: 4px; - -khtml-border-radius: 4px; - border-radius: 4px; - border: 1px solid #001; - font-size: 9pt !important; - line-height: 160% !important; - -moz-box-shadow: inset 0px 0px 1px 0px #002; - text-align: center; - background: #ddf url('../console/light/images/lightbluetile.png'); - opacity: 1.0; + background: #fff; + color: #310; + min-width: 800px !important; + margin: 5px 0 0 0; + padding: 10px 10px 0px 10px; + -moz-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + border: 1px solid #001; + font-size: 9pt !important; + line-height: 160% !important; + -moz-box-shadow: inset 0px 0px 1px 0px #002; + text-align: center; + opacity: 1.0; } form { - line-height: 250% + line-height: 250%; } -p { - line-height: 150% -} - -a:link { - padding 5px; +p { + line-height: 150%; } hr { - color: #003; - background: #003; - height: 1px; - border: 0px solid #003; - width: 100%; - margin: 10px 0 7px 0; - text-align: center; + color: #003; + background: #003; + height: 1px; + border: 0px solid #003; + width: 100%; + margin: 10px 0 7px 0; + text-align: center; } -a:link{ - color: #930; - text-decoration: none; - font-weight: bold; - word-wrap: break-word; +a:link { + color: #930; + text-decoration: none; + font-weight: bold; + word-wrap: break-word; } -a:visited{ - color: #606; - text-decoration: none; - font-weight: bold; +a:visited { + color: #606; + text-decoration: none; + font-weight: bold; } -a:hover{ - color: #900; - font-weight: bold; +a:hover { + color: #900; + font-weight: bold; } input { -/* font-family: "Lucida Console", "DejaVu Sans Mono", Courier, mono !important;*/ - font-size: 9pt; - font-weight: bold; - text-align: left; - padding: 2px; -} + font-size: 9pt; + font-weight: bold; + text-align: left; + padding: 2px; +} select { - font-family:"Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif; - background-color: #ffe; - color: #310; - font-size: 9pt; + background: #ffe; + color: #310; + font: 9pt "Lucida Sans Unicode","Bitstream Vera Sans",Verdana,Tahoma,Helvetica,sans-serif; } img { - border: none; - margin: 5px 5px 0px 5px; - opacity: 1.0; - line-height: 100% + border: none; + margin: 5px 5px 0px 5px; + opacity: 1.0; + line-height: 100%; } -img:hover{ - border: none; - margin: 5px 5px 0px 5px; - opacity: 0.5; - line-height: 100% +img: hover { + border: none; + margin: 5px 5px 0px 5px; + opacity: 0.5; + line-height: 100%; } - -div.section { - margin: 0 0 10px 0; - padding: 10px; - background: #ffe; - border: 1px solid #001; - text-align: center; - color: #001; - -moz-border-radius: 4px; - -khtml-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: inset 0px 0px 1px 0px #002; - word-wrap: break-word; - text-align: center; - background: #ffe url('../console/light/images/tabletile.png'); - opacity: 1.0; -} - -div.mainsection { - margin: 0 0 10px 0; - padding: 10px; - background: #ffe; - border: 1px solid #001; - text-align: center; - color: #001; - -moz-border-radius: 4px; - -khtml-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: inset 0px 0px 1px 0px #002; - word-wrap: break-word; - text-align: center; - background: #ffe url('../console/light/images/tabletile.png'); - opacity: 1.0; +div.section,div.mainsection { + margin: 0 0 10px 0; + padding: 10px; + border: 1px solid #001; + color: #001; + -moz-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: inset 0px 0px 1px 0px #002; + word-wrap: break-word; + text-align: center; + background: #ffe url('../console/light/images/tabletile.png'); + opacity: 1.0; } div.newtorrentsection { - margin: 0 0 10px 0; - padding: 10px; - background: #ffe; - border: 1px solid #001; - text-align: center; - color: #001; - -moz-border-radius: 4px; - -khtml-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: inset 0px 0px 1px 0px #002; - word-wrap: break-word; - text-align: center; - background: #ffe url('../console/images/yellowtile.png'); - opacity: 1.0; + margin: 0 0 10px 0; + padding: 0 10px 10px 10px; + border: 1px solid #001; + text-align: center; + color: #001; + -moz-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: inset 0px 0px 1px 0px #002; + word-wrap: break-word; + background: #bb4 url('../console/images/yellowtile.png'); + opacity: 1.0; } div.addtorrentsection { - margin: 0 0 10px 0; - padding: 10px; - background: #ffe; - border: 1px solid #001; - text-align: center; - color: #001; - -moz-border-radius: 4px; - -khtml-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: inset 0px 0px 1px 0px #002; - word-wrap: break-word; - text-align: center; - background: #ffe url('../console/images/greentile.png'); - opacity: 1.0; + margin: 0 0 10px 0; + padding: 0 10px 10px 10px; + border: 1px solid #001; + text-align: center; + color: #001; + -moz-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: inset 0px 0px 1px 0px #002; + word-wrap: break-word; + background: #7f7 url('../console/images/greentile.png'); + opacity: 1.0; } div.configsection { - margin: 0; - padding: 10px; - background: #ffe; - border: 1px solid #001; - color: #ffb; - -moz-border-radius: 4px; - -khtml-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: inset 0px 0px 0px 1px #900; - word-wrap: break-word; - text-align: center; - background: #ffe url('../console/light/images/darkbluetile.png'); - font-weight: bold; /* red tile needs bold text! */ + margin: 0; + padding: 0 10px 10px 10px; + border: 1px solid #001; + color: #ffb; + -moz-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: inset 0px 0px 0px 1px #900; + word-wrap: break-word; + text-align: center; + background: #700 url('../console/light/images/darkbluetile.png'); + font-weight: bold;/* red tile needs bold text! */ } -div.configsection a{ - color: #f90; +div.configsection a { + color: #f90; } -div.configsection a:hover{ - color: #f60; - text-decoration: underline; +div.configsection a: hover { + color: #f60; + text-decoration: underline; } .snarknavbar { - margin: 0 0 10px 0 !important; - padding: 10px; - border: 1px solid #001; - -moz-border-radius: 4px; - -khtml-border-radius: 4px; - border-radius: 4px; - background: #eef; - -moz-box-shadow: inset 0px 0px 1px 0px #002; - background: #ddf url('../console/light/images/tabletile.png'); - text-transform: uppercase !important; - letter-spacing: 0.05em; - font-weight: bold; - font-size: 11pt; - color: #001; - text-shadow: 0px 0px 1px rgba(0, 0, 148, 0.9); -} + margin: 0 0 10px 0 !important; + padding: 10px; + border: 1px solid #001; + -moz-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: inset 0px 0px 1px 0px #002; + background: #ddf url('../console/light/images/tabletile.png'); + text-transform: uppercase !important; + letter-spacing: 0.05em; + font-weight: bold; + font-size: 11pt; + color: #001; + text-shadow: 0px 0px 1px rgba(0,0,148,0.9); + } From cf82b51a1f88dd73f9ae7259d0c2213fa51adc60 Mon Sep 17 00:00:00 2001 From: z3d Date: Thu, 20 Aug 2009 12:10:18 +0000 Subject: [PATCH 02/31] Rename I2PTunnel Webmanager to I2P Tunnels Manager on edit client/server tunnels pages. --- apps/i2ptunnel/jsp/editClient.jsp | 2 +- apps/i2ptunnel/jsp/editServer.jsp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp index 4957e3a57..c0ad7cdb5 100644 --- a/apps/i2ptunnel/jsp/editClient.jsp +++ b/apps/i2ptunnel/jsp/editClient.jsp @@ -13,7 +13,7 @@ %> - I2PTunnel Webmanager - Edit + I2P Tunnel Manager - Edit diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp index 5d2cc3cd4..958c7e8b1 100644 --- a/apps/i2ptunnel/jsp/editServer.jsp +++ b/apps/i2ptunnel/jsp/editServer.jsp @@ -13,7 +13,7 @@ %> - I2PTunnel Webmanager - Edit + I2P Tunnel Manager - Edit From 4d4538a3464db0d6ed89005d06585c5eeb1ad5aa Mon Sep 17 00:00:00 2001 From: z3d Date: Thu, 20 Aug 2009 15:24:14 +0000 Subject: [PATCH 03/31] Space efficiency drive for light console theme. --- .../themes/console/light/console.css | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/installer/resources/themes/console/light/console.css b/installer/resources/themes/console/light/console.css index 6138ab4c8..67c22bcc4 100644 --- a/installer/resources/themes/console/light/console.css +++ b/installer/resources/themes/console/light/console.css @@ -1,7 +1,7 @@ /* Not yet complete. Subject to flux and change. dr|z3d - 07.25.09 */ body { - margin: 25px 10px 0 5px; + margin: 15px 0px 0 5px; padding: 0em; text-align: center; background: #eef; @@ -78,7 +78,7 @@ a:active{ div.routersummaryouter { float: left; width: 215px; - margin: 0 0 10px 20px; + margin: 0 0 10px 0px; padding: 0; border: 0; clear: left;/* fixes a bug in Opera */ @@ -213,7 +213,7 @@ div.warning { } div.main { - margin: 0px 0px 20px 220px; + margin: 0px 0px 20px 195px; padding: 0 15px 15px 25px; background: #eef; text-align: left; @@ -237,7 +237,7 @@ div.main textarea { } div.news { - margin: 0px 15px 20px 245px; + margin: 0px 15px 10px 220px; padding: 20px 30px 20px 30px; border: 1px solid #003; color: #410; @@ -302,7 +302,7 @@ div.news h4 { div.confignav { padding: 15px 10px !important; - margin: 0 0 25px 0; + margin: 0 0px 15px 0; background: #ddf url('images/lightbluetile.png'); -moz-border-radius: 4px; -khtml-border-radius: 4px; @@ -316,8 +316,8 @@ div.confignav { } div.configure { - padding: 0 15px 15px 15px !important; - margin: 10px 0px 25px 0; + padding: 0 15px 0px 15px !important; + margin: 0px 0px 15px 0; background: #ddf url('images/lightbluetile.png'); -moz-border-radius: 4px; -khtml-border-radius: 4px; @@ -327,6 +327,17 @@ div.configure { min-width: 400px; } +div.configure h3, div.graphspanel h3 { + border: 1px solid #002; + border-left: 5px solid #002; + padding: 3px 5px 3px 5px; + margin: 15px 0 15px 0; + border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + -khtml-border-radius: 0 4px 4px 0; + background: #eef; +} + div.graphspanel { padding: 12px; margin: 10px 0px 25px 0; @@ -365,7 +376,7 @@ div.graphspanel form { div.messages { padding: 10px; - margin: 10px 0 20px 0; + margin: 10px 0 15px 0; background: #ddf; -moz-border-radius: 4px; -khtml-border-radius: 4px; @@ -397,7 +408,7 @@ table { border-collapse: collapse; width: 100%; border: 1px solid #000022; - margin: 5px 0px 5px 0px; + margin: 0px -15px 5px 0px; cell-padding: 1px; font-size: 7pt; background: #b4c8ff url('images/tabletitlelight.png') repeat-x; @@ -462,7 +473,7 @@ div.main li { text-align: left; list-style: square; margin: 2px 5px 0px 20px; - padding: 1px 20px 1px 10px; + padding: 1px 10px 1px 10px; line-height: 150%; word-wrap: break-word; } @@ -528,7 +539,7 @@ h1 { text-align: left; color: #002; padding: 10px 15px; - margin: 0 15px 25px 245px; + margin: 0 15px 15px 220px; font: normal bold 16pt/120% "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif; letter-spacing: 0.15em; text-transform: uppercase; @@ -554,7 +565,7 @@ h2 { border-radius: 4px; -moz-border-radius: 4px; -khtml-border-radius: 4px; - margin: 25px 0 20px 0 !important; + margin: 15px 0px 15px 0 !important; -moz-box-shadow: inset 0px 0px 1px 0px #002; word-wrap: break-word; } @@ -571,7 +582,7 @@ h3 { border: 1px solid #002; border-left: 5px solid #002; padding: 3px 5px 3px 5px; - margin: 20px 0 15px 0; + margin: 10px 0 15px 0; border-radius: 0 4px 4px 0; -moz-border-radius: 0 4px 4px 0; -khtml-border-radius: 0 4px 4px 0; @@ -764,8 +775,8 @@ form {} } .joblog { - margin: 25px 0 25px 0; - padding: 20px 40px 20px 40px !important; + margin: 15px 0; + padding: 10px 20px !important; border: 1px solid #003; background-color: #004; background: #ddf url('images/lightbluetile.png'); @@ -786,6 +797,10 @@ form {} word-wrap: break-word !important; } +.joblog table { + margin-top: 10px; +} + .smallhead { font-size: 7pt } From 2f9a4f0fa5f3680e5b83afa50c40ead0b0e8be51 Mon Sep 17 00:00:00 2001 From: z3d Date: Fri, 21 Aug 2009 00:27:32 +0000 Subject: [PATCH 04/31] Slight margin adjustments for the light theme. --- apps/routerconsole/jsp/help.jsp | 2 +- installer/resources/themes/console/light/console.css | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/routerconsole/jsp/help.jsp b/apps/routerconsole/jsp/help.jsp index 48752b19e..0ae11a95a 100644 --- a/apps/routerconsole/jsp/help.jsp +++ b/apps/routerconsole/jsp/help.jsp @@ -176,4 +176,4 @@ client applications can be found on our d

A more complete list of changes can be found in the history.txt file in your i2p directory. -


+


diff --git a/installer/resources/themes/console/light/console.css b/installer/resources/themes/console/light/console.css index 67c22bcc4..e120d8ea3 100644 --- a/installer/resources/themes/console/light/console.css +++ b/installer/resources/themes/console/light/console.css @@ -23,6 +23,7 @@ pre { text-align: left; font: 8pt "Lucida Console", "DejaVu Sans Mono", Courier, mono; color: #333; + margin: 10px; } div.logo { From faeb58f7e2aa483c37e59dc62b20f6277486f155 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 21 Aug 2009 15:40:26 +0000 Subject: [PATCH 05/31] * ClientManager: - Prevent client destination theft by rejecting duplicates - Java 5 cleanups --- .../net/i2p/router/ClientManagerFacade.java | 2 +- .../net/i2p/router/client/ClientManager.java | 55 +++++++++++-------- .../client/ClientManagerFacadeImpl.java | 2 +- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/router/java/src/net/i2p/router/ClientManagerFacade.java b/router/java/src/net/i2p/router/ClientManagerFacade.java index 0ce20df6a..b6c377b70 100644 --- a/router/java/src/net/i2p/router/ClientManagerFacade.java +++ b/router/java/src/net/i2p/router/ClientManagerFacade.java @@ -85,7 +85,7 @@ public abstract class ClientManagerFacade implements Service { * * @return set of Destination objects */ - public Set listClients() { return Collections.EMPTY_SET; } + public Set listClients() { return Collections.EMPTY_SET; } /** * Return the client's current config, or null if not connected diff --git a/router/java/src/net/i2p/router/client/ClientManager.java b/router/java/src/net/i2p/router/client/ClientManager.java index 5b4a4fb53..8221a0bca 100644 --- a/router/java/src/net/i2p/router/client/ClientManager.java +++ b/router/java/src/net/i2p/router/client/ClientManager.java @@ -42,8 +42,8 @@ import net.i2p.util.Log; public class ClientManager { private Log _log; private ClientListenerRunner _listener; - private final HashMap _runners; // Destination --> ClientConnectionRunner - private final Set _pendingRunners; // ClientConnectionRunner for clients w/out a Dest yet + private final HashMap _runners; // Destination --> ClientConnectionRunner + private final Set _pendingRunners; // ClientConnectionRunner for clients w/out a Dest yet private RouterContext _ctx; /** ms to wait before rechecking for inbound messages to deliver to clients */ @@ -90,21 +90,21 @@ public class ClientManager { public void shutdown() { _log.info("Shutting down the ClientManager"); _listener.stopListening(); - Set runners = new HashSet(); + Set runners = new HashSet(); synchronized (_runners) { - for (Iterator iter = _runners.values().iterator(); iter.hasNext();) { - ClientConnectionRunner runner = (ClientConnectionRunner)iter.next(); + for (Iterator iter = _runners.values().iterator(); iter.hasNext();) { + ClientConnectionRunner runner = iter.next(); runners.add(runner); } } synchronized (_pendingRunners) { - for (Iterator iter = _pendingRunners.iterator(); iter.hasNext();) { - ClientConnectionRunner runner = (ClientConnectionRunner)iter.next(); + for (Iterator iter = _pendingRunners.iterator(); iter.hasNext();) { + ClientConnectionRunner runner = iter.next(); runners.add(runner); } } - for (Iterator iter = runners.iterator(); iter.hasNext(); ) { - ClientConnectionRunner runner = (ClientConnectionRunner)iter.next(); + for (Iterator iter = runners.iterator(); iter.hasNext(); ) { + ClientConnectionRunner runner = iter.next(); runner.stopRunning(); } } @@ -131,15 +131,26 @@ public class ClientManager { } } + /** + * Add to the clients list. Check for a dup destination. + */ public void destinationEstablished(ClientConnectionRunner runner) { + Destination dest = runner.getConfig().getDestination(); if (_log.shouldLog(Log.DEBUG)) - _log.debug("DestinationEstablished called for destination " + runner.getConfig().getDestination().calculateHash().toBase64()); + _log.debug("DestinationEstablished called for destination " + dest.calculateHash().toBase64()); synchronized (_pendingRunners) { _pendingRunners.remove(runner); } + boolean fail = false; synchronized (_runners) { - _runners.put(runner.getConfig().getDestination(), runner); + fail = _runners.containsKey(dest); + if (!fail) + _runners.put(dest, runner); + } + if (fail) { + _log.log(Log.CRIT, "Client attempted to register duplicate destination " + dest.calculateHash().toBase64()); + runner.disconnectClient("Duplicate destination"); } } @@ -278,8 +289,8 @@ public class ClientManager { return true; } - public Set listClients() { - Set rv = new HashSet(); + public Set listClients() { + Set rv = new HashSet(); synchronized (_runners) { rv.addAll(_runners.keySet()); } @@ -293,7 +304,7 @@ public class ClientManager { long inLock = 0; synchronized (_runners) { inLock = _ctx.clock().now(); - rv = (ClientConnectionRunner)_runners.get(dest); + rv = _runners.get(dest); } long afterLock = _ctx.clock().now(); if (afterLock - beforeLock > 50) { @@ -331,8 +342,8 @@ public class ClientManager { if (destHash == null) return null; synchronized (_runners) { - for (Iterator iter = _runners.values().iterator(); iter.hasNext(); ) { - ClientConnectionRunner cur = (ClientConnectionRunner)iter.next(); + for (Iterator iter = _runners.values().iterator(); iter.hasNext(); ) { + ClientConnectionRunner cur = iter.next(); if (cur.getDestHash().equals(destHash)) return cur; } @@ -354,8 +365,8 @@ public class ClientManager { } } - Set getRunnerDestinations() { - Set dests = new HashSet(); + Set getRunnerDestinations() { + Set dests = new HashSet(); long beforeLock = _ctx.clock().now(); long inLock = 0; synchronized (_runners) { @@ -390,13 +401,13 @@ public class ClientManager { StringBuilder buf = new StringBuilder(8*1024); buf.append("Local destinations
"); - Map runners = null; + Map runners = null; synchronized (_runners) { runners = (Map)_runners.clone(); } - for (Iterator iter = runners.keySet().iterator(); iter.hasNext(); ) { - Destination dest = (Destination)iter.next(); - ClientConnectionRunner runner = (ClientConnectionRunner)runners.get(dest); + for (Iterator iter = runners.keySet().iterator(); iter.hasNext(); ) { + Destination dest = iter.next(); + ClientConnectionRunner runner = runners.get(dest); buf.append("* ").append(dest.calculateHash().toBase64().substring(0,6)).append("
\n"); LeaseSet ls = runner.getLeaseSet(); if (ls == null) { diff --git a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java index 9e706beda..360cdb611 100644 --- a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java +++ b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java @@ -215,7 +215,7 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade { * @return set of Destination objects */ @Override - public Set listClients() { + public Set listClients() { if (_manager != null) return _manager.listClients(); else From 2e4bd1e440a1c7efd37b9027994d1bae134b2545 Mon Sep 17 00:00:00 2001 From: z3d Date: Sat, 22 Aug 2009 12:15:19 +0000 Subject: [PATCH 06/31] Polish the console 404 error messages. --- apps/routerconsole/jsp/error.jsp | 7 ++-- .../themes/console/classic/console.css | 22 +++++++++++ .../resources/themes/console/dark/console.css | 28 ++++++++++++++ .../themes/console/light/console.css | 37 +++++++++++++++++-- 4 files changed, 87 insertions(+), 7 deletions(-) diff --git a/apps/routerconsole/jsp/error.jsp b/apps/routerconsole/jsp/error.jsp index 92da24f4d..33adb413d 100644 --- a/apps/routerconsole/jsp/error.jsp +++ b/apps/routerconsole/jsp/error.jsp @@ -12,7 +12,7 @@ } // If it can't find the iframe or viewtheme.jsp I wonder if the whole thing blows up... %> -I2P Router Console +I2P Router Console - Page Not Found <%@include file="css.jsp" %> @@ -23,6 +23,7 @@ if (System.getProperty("router.consoleNonce") == null) { %> <%@include file="summary.jsp" %>

<%=ERROR_CODE%> <%=ERROR_MESSAGE%>

-
-The Router Console page <%=ERROR_URI%> was not found. +
+Sorry! You appear to be requesting a non-existent Router Console page.
+Error 404: <%=ERROR_URI%> not found.
diff --git a/installer/resources/themes/console/classic/console.css b/installer/resources/themes/console/classic/console.css index 8b04beace..cf7d728d0 100644 --- a/installer/resources/themes/console/classic/console.css +++ b/installer/resources/themes/console/classic/console.css @@ -103,6 +103,28 @@ div.warning hr { margin: 5px 0; } +/* console error messages */ + +div.sorry { + padding: 20px; + background: #ddf; + margin: -2px 1px 0 195px; + border: 5px solid #bbf; + text-align: justify; + -moz-box-shadow: inset 0px 0px 0px 1px #d00; + word-wrap: break-word; + font-weight: bold; + color: #001; +} + +div.sorry hr { + color: #001; + background: #001; + height: 1px; + border: 1px solid #001; + margin: 10px 0; +} + div.toolbar { margin: 0em 0em 2em 0em; font-weight: bold; diff --git a/installer/resources/themes/console/dark/console.css b/installer/resources/themes/console/dark/console.css index d36db5c62..10eb945fc 100644 --- a/installer/resources/themes/console/dark/console.css +++ b/installer/resources/themes/console/dark/console.css @@ -223,6 +223,34 @@ div.warning { word-wrap: break-word; } +/* console error messages */ + +div.sorry { + margin: 5px 15px 10px 220px; + padding: 20px 20px 20px 75px; + background: #005; + border: 1px solid #99f; + -moz-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + text-align: justify; + background-image:url("images/errortriangle.png"); + background-position:15px center; + background-repeat:no-repeat; + -moz-box-shadow: inset 0px 0px 0px 1px #d00; + word-wrap: break-word; + font-weight: bold; + color: #eef; +} + +div.sorry hr { + color: #eef; + background: #eef; + height: 1px; + border: 1px solid #eef; + margin: 10px 0; +} + div.main { margin: 0px 0px 20px 195px; padding: 0 15px 15px 25px; diff --git a/installer/resources/themes/console/light/console.css b/installer/resources/themes/console/light/console.css index e120d8ea3..71c33841f 100644 --- a/installer/resources/themes/console/light/console.css +++ b/installer/resources/themes/console/light/console.css @@ -1,7 +1,7 @@ /* Not yet complete. Subject to flux and change. dr|z3d - 07.25.09 */ body { - margin: 15px 0px 0 5px; + margin: 15px 0 0 10px; padding: 0em; text-align: center; background: #eef; @@ -184,7 +184,6 @@ div.routersummary td { border: 0 !important; } - div.routersummary tr:nth-child(even) { background-color: #f60; background-image: none !important; @@ -195,6 +194,8 @@ div.routersummarytr:nth-child(odd) { background-image: none !important; } +/* proxy error messages */ + div.warning { margin: 5px 20px 10px 240px; padding: 0px 25px 20px 75px; @@ -213,6 +214,34 @@ div.warning { word-wrap: break-word; } +/* console error messages */ + +div.sorry { + margin: 5px 15px 10px 220px; + padding: 20px 20px 20px 75px; + background: #ffb; + border: 1px solid #002; + -moz-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + text-align: justify; + background-image: url("images/errortriangle.png"); + background-position: 15px center; + background-repeat: no-repeat; + -moz-box-shadow: inset 0px 0px 0px 1px #d00; + word-wrap: break-word; + font-weight: bold; + color: #331; +} + +div.sorry hr { + color: #552; + background: #552; + height: 1px; + border: 1px solid #552; + margin: 10px 0; +} + div.main { margin: 0px 0px 20px 195px; padding: 0 15px 15px 25px; @@ -409,7 +438,7 @@ table { border-collapse: collapse; width: 100%; border: 1px solid #000022; - margin: 0px -15px 5px 0px; + margin: 10px -15px 5px 0px; cell-padding: 1px; font-size: 7pt; background: #b4c8ff url('images/tabletitlelight.png') repeat-x; @@ -566,7 +595,7 @@ h2 { border-radius: 4px; -moz-border-radius: 4px; -khtml-border-radius: 4px; - margin: 15px 0px 15px 0 !important; + margin: 15px 0px 10px 0 !important; -moz-box-shadow: inset 0px 0px 1px 0px #002; word-wrap: break-word; } From 0cc72a49c8af93790031a13eeb0f54ae88e1a549 Mon Sep 17 00:00:00 2001 From: z3d Date: Sat, 22 Aug 2009 17:42:15 +0000 Subject: [PATCH 07/31] "page or resource" indication on 404 console error page. --- apps/routerconsole/jsp/error.jsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/routerconsole/jsp/error.jsp b/apps/routerconsole/jsp/error.jsp index 33adb413d..b900d29a1 100644 --- a/apps/routerconsole/jsp/error.jsp +++ b/apps/routerconsole/jsp/error.jsp @@ -24,6 +24,6 @@ if (System.getProperty("router.consoleNonce") == null) { <%@include file="summary.jsp" %>

<%=ERROR_CODE%> <%=ERROR_MESSAGE%>

-Sorry! You appear to be requesting a non-existent Router Console page.
+Sorry! You appear to be requesting a non-existent Router Console page or resource.
Error 404: <%=ERROR_URI%> not found.
From 3f3d43df41c1845e92394582cd4e024798a0bd97 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 22 Aug 2009 22:55:37 +0000 Subject: [PATCH 08/31] * Streaming, I2PSession: Prep for SessionKeyManager work in the router - Comment out, deprecate, and javadoc for unused keys and tags, they are vestiges of end-to-end crypto --- .../src/net/i2p/client/streaming/package.html | 8 +-- .../net/i2p/client/streaming/Connection.java | 25 +++++---- .../client/streaming/ConnectionManager.java | 25 ++++++--- .../streaming/ConnectionPacketHandler.java | 12 ++-- .../net/i2p/client/streaming/PacketLocal.java | 22 +++++++- .../net/i2p/client/streaming/PacketQueue.java | 32 ++++++----- .../net/i2p/client/I2CPMessageProducer.java | 8 +++ .../src/net/i2p/client/I2PSessionImpl.java | 6 ++ .../src/net/i2p/client/I2PSessionImpl2.java | 55 +++++++++++++------ .../net/i2p/client/I2PSessionMuxedImpl.java | 10 ++++ .../src/net/i2p/crypto/HMAC256Generator.java | 2 +- .../src/net/i2p/crypto/HMACGenerator.java | 2 +- 12 files changed, 145 insertions(+), 62 deletions(-) diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/package.html b/apps/ministreaming/java/src/net/i2p/client/streaming/package.html index 735135074..841860453 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/package.html +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/package.html @@ -16,9 +16,9 @@ net.i2p.client.streaming.I2PServerSocket#accept} method, which will provide 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.

-

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 +

There is a simple pair of demo applications available as well - +net.i2p.client.streaming.StreamSinkServer listens to a destination and dumps +the data from all sockets it accepts to individual files, while +net.i2p.client.streaming.StreamSinkClient connects to a particular destination and sends a specific amount of random data then disconnects.

diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java index c7495131f..a47d361a6 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java @@ -354,6 +354,7 @@ public class Connection { */ } +/********* private class PingNotifier implements ConnectionManager.PingNotifier { private long _startedPingOn; public PingNotifier() { @@ -367,6 +368,7 @@ public class Connection { _options.updateRTT((int)time*2); } } +*********/ List ackPackets(long ackThrough, long nacks[]) { if (ackThrough < _highestAckedThrough) { @@ -548,20 +550,21 @@ public class Connection { killOutstandingPackets(); } + /** ignore tag issues */ private void killOutstandingPackets() { - boolean tagsCancelled = false; + //boolean tagsCancelled = false; synchronized (_outboundPackets) { for (Iterator iter = _outboundPackets.values().iterator(); iter.hasNext(); ) { PacketLocal pl = (PacketLocal)iter.next(); - if ( (pl.getTagsSent() != null) && (pl.getTagsSent().size() > 0) ) - tagsCancelled = true; + //if ( (pl.getTagsSent() != null) && (pl.getTagsSent().size() > 0) ) + // tagsCancelled = true; pl.cancelled(); } _outboundPackets.clear(); _outboundPackets.notifyAll(); } - if (tagsCancelled) - _context.sessionKeyManager().failTags(_remotePeer.getPublicKey()); + //if (tagsCancelled) + // _context.sessionKeyManager().failTags(_remotePeer.getPublicKey()); } private class DisconnectEvent implements SimpleTimer.TimedEvent { @@ -1140,12 +1143,12 @@ public class Connection { // in case things really suck, the other side may have lost thier // session tags (e.g. they restarted), so jump back to ElGamal. - int failTagsAt = _options.getMaxResends() - 2; - if ( (newWindowSize == 1) && (numSends == failTagsAt) ) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Optimistically failing tags at resend " + numSends); - _context.sessionKeyManager().failTags(_remotePeer.getPublicKey()); - } + //int failTagsAt = _options.getMaxResends() - 2; + //if ( (newWindowSize == 1) && (numSends == failTagsAt) ) { + // if (_log.shouldLog(Log.WARN)) + // _log.warn("Optimistically failing tags at resend " + numSends); + // _context.sessionKeyManager().failTags(_remotePeer.getPublicKey()); + //} if (numSends - 1 > _options.getMaxResends()) { if (_log.shouldLog(Log.DEBUG)) diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java index 7efc6cc40..af44c41f2 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java @@ -349,24 +349,35 @@ public class ConnectionManager { return new HashSet(_connectionByInboundId.values()); } } + + /** blocking */ public boolean ping(Destination peer, long timeoutMs) { - return ping(peer, timeoutMs, true); + return ping(peer, timeoutMs, true, null); } public boolean ping(Destination peer, long timeoutMs, boolean blocking) { - return ping(peer, timeoutMs, blocking, null, null, null); + return ping(peer, timeoutMs, blocking, null); } + /** + * @deprecated I2PSession ignores tags, use non-tag variant + * @param keyToUse ignored + * @param tagsToSend ignored + */ public boolean ping(Destination peer, long timeoutMs, boolean blocking, SessionKey keyToUse, Set tagsToSend, PingNotifier notifier) { + return ping(peer, timeoutMs, blocking, notifier); + } + + public boolean ping(Destination peer, long timeoutMs, boolean blocking, PingNotifier notifier) { Long id = new Long(_context.random().nextLong(Packet.MAX_STREAM_ID-1)+1); PacketLocal packet = new PacketLocal(_context, peer); packet.setSendStreamId(id.longValue()); packet.setFlag(Packet.FLAG_ECHO); packet.setFlag(Packet.FLAG_SIGNATURE_INCLUDED); packet.setOptionalFrom(_session.getMyDestination()); - if ( (keyToUse != null) && (tagsToSend != null) ) { - packet.setKeyUsed(keyToUse); - packet.setTagsSent(tagsToSend); - } + //if ( (keyToUse != null) && (tagsToSend != null) ) { + // packet.setKeyUsed(keyToUse); + // packet.setTagsSent(tagsToSend); + //} PingRequest req = new PingRequest(peer, packet, notifier); @@ -435,7 +446,7 @@ public class ConnectionManager { } public void pong() { _log.debug("Ping successful"); - _context.sessionKeyManager().tagsDelivered(_peer.getPublicKey(), _packet.getKeyUsed(), _packet.getTagsSent()); + //_context.sessionKeyManager().tagsDelivered(_peer.getPublicKey(), _packet.getKeyUsed(), _packet.getTagsSent()); synchronized (ConnectionManager.PingRequest.this) { _ponged = true; ConnectionManager.PingRequest.this.notifyAll(); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java index f7b245cb8..91a06e088 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java @@ -263,12 +263,12 @@ public class ConnectionPacketHandler { numResends++; // ACK the tags we delivered so we can use them - if ( (p.getKeyUsed() != null) && (p.getTagsSent() != null) - && (p.getTagsSent().size() > 0) ) { - _context.sessionKeyManager().tagsDelivered(p.getTo().getPublicKey(), - p.getKeyUsed(), - p.getTagsSent()); - } + //if ( (p.getKeyUsed() != null) && (p.getTagsSent() != null) + // && (p.getTagsSent().size() > 0) ) { + // _context.sessionKeyManager().tagsDelivered(p.getTo().getPublicKey(), + // p.getKeyUsed(), + // p.getTagsSent()); + //} if (_log.shouldLog(Log.DEBUG)) _log.debug("Packet acked after " + p.getAckTime() + "ms: " + p); } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java index 9ed29b50f..b1438a033 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java @@ -47,11 +47,31 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat public Destination getTo() { return _to; } public void setTo(Destination to) { _to = to; } + /** + * @deprecated should always return null + */ public SessionKey getKeyUsed() { return _keyUsed; } - public void setKeyUsed(SessionKey key) { _keyUsed = key; } + + /** + * @deprecated I2PSession throws out the tags + */ + public void setKeyUsed(SessionKey key) { + if (key != null) + _log.error("Who is sending tags thru the streaming lib?"); + _keyUsed = key; + } + /** + * @deprecated should always return null or an empty set + */ public Set getTagsSent() { return _tagsSent; } + + /** + * @deprecated I2PSession throws out the tags + */ public void setTagsSent(Set tags) { + if (tags != null && tags.size() > 0) + _log.error("Who is sending tags thru the streaming lib? " + tags.size()); if ( (_tagsSent != null) && (_tagsSent.size() > 0) && (tags.size() > 0) ) { //int old = _tagsSent.size(); //_tagsSent.addAll(tags); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java index db4adb27c..8a4692ada 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java @@ -36,16 +36,18 @@ public class PacketQueue { /** * Add a new packet to be sent out ASAP + * + * keys and tags disabled since dropped in I2PSession */ public void enqueue(PacketLocal packet) { packet.prepare(); - SessionKey keyUsed = packet.getKeyUsed(); - if (keyUsed == null) - keyUsed = new SessionKey(); - Set tagsSent = packet.getTagsSent(); - if (tagsSent == null) - tagsSent = new HashSet(0); + //SessionKey keyUsed = packet.getKeyUsed(); + //if (keyUsed == null) + // keyUsed = new SessionKey(); + //Set tagsSent = packet.getTagsSent(); + //if (tagsSent == null) + // tagsSent = new HashSet(0); // cache this from before sendMessage String conStr = null; @@ -92,13 +94,19 @@ public class PacketQueue { // I2PSessionImpl2 //sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, expires); // I2PSessionMuxedImpl - sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, expires, + //sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, expires, + // I2PSession.PROTO_STREAMING, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED); + // I2PSessionMuxedImpl no tags + sent = _session.sendMessage(packet.getTo(), buf, 0, size, null, null, expires, I2PSession.PROTO_STREAMING, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED); else // I2PSessionImpl2 //sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, 0); // I2PSessionMuxedImpl - sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, + //sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, + // I2PSession.PROTO_STREAMING, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED); + // I2PSessionMuxedImpl no tags + sent = _session.sendMessage(packet.getTo(), buf, 0, size, null, null, I2PSession.PROTO_STREAMING, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED); end = _context.clock().now(); @@ -129,13 +137,11 @@ public class PacketQueue { if (c != null) // handle race on b0rk c.disconnect(false); } else { - packet.setKeyUsed(keyUsed); - packet.setTagsSent(tagsSent); + //packet.setKeyUsed(keyUsed); + //packet.setTagsSent(tagsSent); packet.incrementSends(); if (_log.shouldLog(Log.DEBUG)) { - String msg = "SEND " + packet + (tagsSent.size() > 0 - ? " with " + tagsSent.size() + " tags" - : "") + String msg = "SEND " + packet + " send # " + packet.getNumSends() + " sendTime: " + (end-begin) + " con: " + conStr; diff --git a/core/java/src/net/i2p/client/I2CPMessageProducer.java b/core/java/src/net/i2p/client/I2CPMessageProducer.java index b897d22d0..220c0a851 100644 --- a/core/java/src/net/i2p/client/I2CPMessageProducer.java +++ b/core/java/src/net/i2p/client/I2CPMessageProducer.java @@ -93,6 +93,10 @@ class I2CPMessageProducer { /** * Package up and send the payload to the router for delivery * + * @param tag unused - no end-to-end crypto + * @param tags unused - no end-to-end crypto + * @param key unused - no end-to-end crypto + * @param newKey unused - no end-to-end crypto */ public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag, SessionKey key, Set tags, SessionKey newKey, long expires) throws I2PSessionException { @@ -135,6 +139,10 @@ class I2CPMessageProducer { /** * Create a new signed payload and send it off to the destination * + * @param tag unused - no end-to-end crypto + * @param tags unused - no end-to-end crypto + * @param key unused - no end-to-end crypto + * @param newKey unused - no end-to-end crypto */ private Payload createPayload(Destination dest, byte[] payload, SessionTag tag, SessionKey key, Set tags, SessionKey newKey) throws I2PSessionException { diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java index 8fc308715..7bee11a2c 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl.java @@ -355,17 +355,23 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa */ public abstract boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException; + /** + * @param keyUsed unused - no end-to-end crypto + * @param tagsSent unused - no end-to-end crypto + */ public abstract boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException; public abstract void receiveStatus(int msgId, long nonce, int status); +/****** no end-to-end crypto protected static final Set createNewTags(int num) { Set tags = new HashSet(); for (int i = 0; i < num; i++) tags.add(new SessionTag(true)); return tags; } +*******/ /** * Recieve a payload message and let the app know its available diff --git a/core/java/src/net/i2p/client/I2PSessionImpl2.java b/core/java/src/net/i2p/client/I2PSessionImpl2.java index 508057c2c..5b57b3137 100644 --- a/core/java/src/net/i2p/client/I2PSessionImpl2.java +++ b/core/java/src/net/i2p/client/I2PSessionImpl2.java @@ -133,14 +133,28 @@ class I2PSessionImpl2 extends I2PSessionImpl { return sendMessage(dest, payload, offset, size, null, null, 0); } + /** + * @param keyUsed unused - no end-to-end crypto + * @param tagsSent unused - no end-to-end crypto + */ @Override public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException { return sendMessage(dest, payload, 0, payload.length, keyUsed, tagsSent, 0); } + + /** + * @param keyUsed unused - no end-to-end crypto + * @param tagsSent unused - no end-to-end crypto + */ public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent) throws I2PSessionException { return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0); } + + /** + * @param keyUsed unused - no end-to-end crypto + * @param tagsSent unused - no end-to-end crypto + */ public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expires) throws I2PSessionException { if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message"); @@ -196,13 +210,17 @@ class I2PSessionImpl2 extends I2PSessionImpl { private static final int NUM_TAGS = 50; + /** + * @param keyUsed unused - no end-to-end crypto + * @param tagsSent unused - no end-to-end crypto + */ protected boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent, long expires) throws I2PSessionException { - SessionKey key = null; - SessionKey newKey = null; - SessionTag tag = null; - Set sentTags = null; - int oldTags = 0; + //SessionKey key = null; + //SessionKey newKey = null; + //SessionTag tag = null; + //Set sentTags = null; + //int oldTags = 0; long begin = _context.clock().now(); /*********** if (I2CPMessageProducer.END_TO_END_CRYPTO) { @@ -256,27 +274,27 @@ class I2PSessionImpl2 extends I2PSessionImpl { long nonce = _context.random().nextInt(Integer.MAX_VALUE); if (_log.shouldLog(Log.DEBUG)) _log.debug("before sync state"); MessageState state = new MessageState(_context, nonce, getPrefix()); - state.setKey(key); - state.setTags(sentTags); - state.setNewKey(newKey); + //state.setKey(key); + //state.setTags(sentTags); + //state.setNewKey(newKey); state.setTo(dest); - if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key); + //if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key); - if (keyUsed != null) { + //if (keyUsed != null) { //if (I2CPMessageProducer.END_TO_END_CRYPTO) { // if (newKey != null) // keyUsed.setData(newKey.getData()); // else // keyUsed.setData(key.getData()); //} else { - keyUsed.setData(SessionKey.INVALID_KEY.getData()); + // keyUsed.setData(SessionKey.INVALID_KEY.getData()); //} - } - if (tagsSent != null) { - if (sentTags != null) { - tagsSent.addAll(sentTags); - } - } + //} + //if (tagsSent != null) { + // if (sentTags != null) { + // tagsSent.addAll(sentTags); + // } + //} if (_log.shouldLog(Log.DEBUG)) _log.debug("before sync state"); long beforeSendingSync = _context.clock().now(); @@ -291,7 +309,8 @@ class I2PSessionImpl2 extends I2PSessionImpl { + state.getNonce() + " for best effort " + " sync took " + (inSendingSync-beforeSendingSync) + " add took " + (afterSendingSync-inSendingSync)); - _producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey, expires); + //_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey, expires); + _producer.sendMessage(this, dest, nonce, payload, null, null, null, null, expires); // since this is 'best effort', all we're waiting for is a status update // saying that the router received it - in theory, that should come back diff --git a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java index 58b5cae9f..c0533b1ff 100644 --- a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java +++ b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java @@ -128,6 +128,10 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession { return sendMessage(dest, payload, 0, payload.length, null, null, 0, proto, fromport, toport); } + /** + * @param keyUsed unused - no end-to-end crypto + * @param tagsSent unused - no end-to-end crypto + */ @Override public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expires) @@ -135,6 +139,10 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession { return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, PROTO_UNSPECIFIED, PORT_UNSPECIFIED, PORT_UNSPECIFIED); } + /** + * @param keyUsed unused - no end-to-end crypto + * @param tagsSent unused - no end-to-end crypto + */ @Override public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, int proto, int fromport, int toport) throws I2PSessionException { @@ -142,6 +150,8 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession { } /** + * @param keyUsed unused - no end-to-end crypto + * @param tagsSent unused - no end-to-end crypto * @param proto 1-254 or 0 for unset; recommended: * I2PSession.PROTO_UNSPECIFIED * I2PSession.PROTO_STREAMING diff --git a/core/java/src/net/i2p/crypto/HMAC256Generator.java b/core/java/src/net/i2p/crypto/HMAC256Generator.java index 0335d1e7e..e84489d97 100644 --- a/core/java/src/net/i2p/crypto/HMAC256Generator.java +++ b/core/java/src/net/i2p/crypto/HMAC256Generator.java @@ -12,7 +12,7 @@ import org.bouncycastle.crypto.macs.I2PHMac; /** * Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs - * in {@link org.bouncycastle.crypto.macs.HMac} and + * in {@link org.bouncycastle.crypto.macs.I2PHMac} and * {@link org.bouncycastle.crypto.digests.MD5Digest}. * */ diff --git a/core/java/src/net/i2p/crypto/HMACGenerator.java b/core/java/src/net/i2p/crypto/HMACGenerator.java index e37ec9202..9bf06aa70 100644 --- a/core/java/src/net/i2p/crypto/HMACGenerator.java +++ b/core/java/src/net/i2p/crypto/HMACGenerator.java @@ -15,7 +15,7 @@ import org.bouncycastle.crypto.macs.I2PHMac; /** * Calculate the HMAC-MD5 of a key+message. All the good stuff occurs - * in {@link org.bouncycastle.crypto.macs.HMac} and + * in {@link org.bouncycastle.crypto.macs.I2PHMac} and * {@link org.bouncycastle.crypto.digests.MD5Digest}. * */ From 7e547743c71bca99c73c1974559b43f5f79f1c99 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 23 Aug 2009 12:29:34 +0000 Subject: [PATCH 09/31] * Message: Move 2 unused classes out of the router lib (~15KB) (more SKM prep) --- .../{src => test}/net/i2p/router/message/BuildTestMessageJob.java | 0 .../java/{src => test}/net/i2p/router/message/SendGarlicJob.java | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename router/java/{src => test}/net/i2p/router/message/BuildTestMessageJob.java (100%) rename router/java/{src => test}/net/i2p/router/message/SendGarlicJob.java (100%) diff --git a/router/java/src/net/i2p/router/message/BuildTestMessageJob.java b/router/java/test/net/i2p/router/message/BuildTestMessageJob.java similarity index 100% rename from router/java/src/net/i2p/router/message/BuildTestMessageJob.java rename to router/java/test/net/i2p/router/message/BuildTestMessageJob.java diff --git a/router/java/src/net/i2p/router/message/SendGarlicJob.java b/router/java/test/net/i2p/router/message/SendGarlicJob.java similarity index 100% rename from router/java/src/net/i2p/router/message/SendGarlicJob.java rename to router/java/test/net/i2p/router/message/SendGarlicJob.java From 5a4c2de425111f2d62e498f394bcf32b049c701f Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 23 Aug 2009 16:12:09 +0000 Subject: [PATCH 10/31] * Message, I2PSession, SessionKeyManager, Console: Prep for SessionKeyManager work in the router - Fix up SKM renderStatusHTML(); add debug.jsp to see it; Redefine getClientSessionKeyManager(); More cleanups --- apps/routerconsole/jsp/debug.jsp | 30 +++++++ .../client/MessagePayloadMessageHandler.java | 25 +++--- .../src/net/i2p/crypto/SessionKeyManager.java | 4 + .../crypto/TransientSessionKeyManager.java | 90 ++++++++++++------- .../net/i2p/router/ClientManagerFacade.java | 2 +- .../i2p/router/DummyClientManagerFacade.java | 2 +- .../net/i2p/router/client/ClientManager.java | 5 +- .../client/ClientManagerFacadeImpl.java | 2 +- 8 files changed, 115 insertions(+), 45 deletions(-) create mode 100644 apps/routerconsole/jsp/debug.jsp diff --git a/apps/routerconsole/jsp/debug.jsp b/apps/routerconsole/jsp/debug.jsp new file mode 100644 index 000000000..694f9c1df --- /dev/null +++ b/apps/routerconsole/jsp/debug.jsp @@ -0,0 +1,30 @@ +<%@page contentType="text/html"%> +<%@page pageEncoding="UTF-8"%> + +DEBUG +<%@include file="css.jsp" %> + +<%@include file="summary.jsp" %> +
+<% + /* + * Quick and easy place to put debugging stuff + */ + net.i2p.router.RouterContext ctx = (net.i2p.router.RouterContext) net.i2p.I2PAppContext.getGlobalContext(); + + /* + * Print out the status for all the SessionKeyManagers + */ + out.print("

Router SKM

"); + ctx.sessionKeyManager().renderStatusHTML(out); + java.util.Set clients = ctx.clientManager().listClients(); + for (net.i2p.data.Destination dest : clients) { + net.i2p.data.Hash h = dest.calculateHash(); + net.i2p.crypto.SessionKeyManager skm = ctx.clientManager().getClientSessionKeyManager(h); + if (skm != null) { + out.print("

" + h.toBase64().substring(0,6) + " SKM

"); + skm.renderStatusHTML(out); + } + } +%> +
diff --git a/core/java/src/net/i2p/client/MessagePayloadMessageHandler.java b/core/java/src/net/i2p/client/MessagePayloadMessageHandler.java index 4b17a67d3..7b294e3b6 100644 --- a/core/java/src/net/i2p/client/MessagePayloadMessageHandler.java +++ b/core/java/src/net/i2p/client/MessagePayloadMessageHandler.java @@ -22,6 +22,8 @@ import net.i2p.util.Log; * of a message by accepting it, decrypting the payload, adding it to the set of * recieved messages, and telling the router that it has been recieved correctly. * + * We don't really decrypt (no more end-to-end crypto) + * * @author jrandom */ class MessagePayloadMessageHandler extends HandlerImpl { @@ -51,21 +53,24 @@ class MessagePayloadMessageHandler extends HandlerImpl { /** * Decrypt the payload + * + * We don't really decrypt (no more end-to-end crypto) + * If we do, we need to use the correct key manager in the decrypt() call below */ private Payload decryptPayload(MessagePayloadMessage msg, I2PSessionImpl session) throws DataFormatException { Payload payload = msg.getPayload(); - if (!I2CPMessageProducer.END_TO_END_CRYPTO) { + //if (!I2CPMessageProducer.END_TO_END_CRYPTO) { payload.setUnencryptedData(payload.getEncryptedData()); return payload; - } + //} - byte[] data = _context.elGamalAESEngine().decrypt(payload.getEncryptedData(), session.getDecryptionKey()); - if (data == null) { - if (_log.shouldLog(Log.WARN)) - _log.warn("Error decrypting the payload"); - throw new DataFormatException("Unable to decrypt the payload"); - } - payload.setUnencryptedData(data); - return payload; + //byte[] data = _context.elGamalAESEngine().decrypt(payload.getEncryptedData(), session.getDecryptionKey()); + //if (data == null) { + // if (_log.shouldLog(Log.WARN)) + // _log.warn("Error decrypting the payload"); + // throw new DataFormatException("Unable to decrypt the payload"); + //} + //payload.setUnencryptedData(data); + //return payload; } } diff --git a/core/java/src/net/i2p/crypto/SessionKeyManager.java b/core/java/src/net/i2p/crypto/SessionKeyManager.java index b1547864c..2cd86ba7d 100644 --- a/core/java/src/net/i2p/crypto/SessionKeyManager.java +++ b/core/java/src/net/i2p/crypto/SessionKeyManager.java @@ -9,6 +9,8 @@ package net.i2p.crypto; * */ +import java.io.IOException; +import java.io.Writer; import java.util.Set; import net.i2p.I2PAppContext; @@ -130,4 +132,6 @@ public class SessionKeyManager { */ public void shutdown() { // nop } + + public void renderStatusHTML(Writer out) throws IOException {} } diff --git a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java index 02be5acd5..2e034e2b1 100644 --- a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java +++ b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java @@ -9,7 +9,10 @@ package net.i2p.crypto; * */ +import java.io.IOException; +import java.io.Writer; import java.util.ArrayList; +import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -17,6 +20,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; @@ -503,57 +507,83 @@ public class TransientSessionKeyManager extends SessionKeyManager { return removed; } - public String renderStatusHTML() { + @Override + public void renderStatusHTML(Writer out) throws IOException { StringBuilder buf = new StringBuilder(1024); - buf.append("

Inbound sessions

"); - buf.append("
");
         List msgs = _manager.getMessages();
         for (int i = msgs.size()-1; i >= 0; i--) {
             String msg = (String)msgs.get(i);
@@ -493,7 +493,7 @@ public class I2PSnarkServlet extends HttpServlet {
         
         if (remaining == 0)
             out.write("");
+                      + "\" title=\"View file\">");
         out.write(filename);
         if (remaining == 0)
             out.write("");
@@ -568,7 +568,7 @@ public class I2PSnarkServlet extends HttpServlet {
                 out.write("
"); out.write(""); + out.write(""); String ch = peer.toString().substring(0, 4); String client; if ("AwMD".equals(ch)) @@ -587,7 +587,7 @@ public class I2PSnarkServlet extends HttpServlet { client = "Robert"; else client = "Unknown (" + ch + ')'; - out.write("" + client + "  " + peer.toString().substring(5, 9) + ""); + out.write("" + client + "  " + peer.toString().substring(5, 9) + ""); if (showDebug) out.write(" inactive " + (peer.getInactiveTime() / 1000) + "s"); out.write(""); float pct = (float) (100.0 * (float) peer.completed() / snark.meta.getPieces()); if (pct == 100.0) - out.write("Seed"); + out.write("Seed"); else { String ps = String.valueOf(pct); if (ps.length() > 5) ps = ps.substring(0, 5); - out.write("" + ps + "%"); + out.write("" + ps + "%"); } out.write(""); @@ -610,14 +610,14 @@ public class I2PSnarkServlet extends HttpServlet { if (remaining > 0) { if (peer.isInteresting() && !peer.isChoked()) { out.write(""); - out.write("" + formatSize(peer.getDownloadRate()) + "ps"); + out.write("" + formatSize(peer.getDownloadRate()) + "ps"); } else { - out.write(""); else out.write("Choked\">"); - out.write(formatSize(peer.getDownloadRate()) + "ps"); + out.write(formatSize(peer.getDownloadRate()) + "ps"); } } out.write("
"); + buf.append("

Inbound sessions

" + + "
"); Set inbound = getInboundTagSets(); Map> inboundSets = new HashMap(inbound.size()); + // FIXME what does this loop do? nothing? for (Iterator iter = inbound.iterator(); iter.hasNext();) { TagSet ts = iter.next(); if (!inboundSets.containsKey(ts.getAssociatedKey())) inboundSets.put(ts.getAssociatedKey(), new HashSet()); Set sets = inboundSets.get(ts.getAssociatedKey()); sets.add(ts); } + int total = 0; + long now = _context.clock().now(); for (Iterator iter = inboundSets.keySet().iterator(); iter.hasNext();) { SessionKey skey = iter.next(); - Set sets = inboundSets.get(skey); - buf.append(""); - buf.append(""); - buf.append("" + + "" + + ""); + buf.append("\n"); + out.write(buf.toString()); + buf.setLength(0); } - buf.append("
Session key: ").append(skey.toBase64()).append("# Sets: ").append(sets.size()).append("
    "); + Set sets = new TreeSet(new TagSetComparator()); + sets.addAll(inboundSets.get(skey)); + buf.append("
Session key: ").append(skey.toBase64()).append("# Sets: ").append(sets.size()).append("
    "); for (Iterator siter = sets.iterator(); siter.hasNext();) { TagSet ts = siter.next(); - buf.append("
  • Received on: ").append(new Date(ts.getDate())).append(" with ") - .append(ts.getTags().size()).append(" tags remaining
  • "); + int size = ts.getTags().size(); + total += size; + buf.append("
  • Received: ").append(DataHelper.formatDuration(now - ts.getDate())).append(" ago with "); + buf.append(size).append(" tags remaining
  • "); } - buf.append("
"); - - buf.append("

Outbound sessions

"); - - buf.append(""); + buf.append("\n" + + "
Total tags: ").append(total).append(" ("); + buf.append(DataHelper.formatSize(32*total)).append("B)
" + + "

Outbound sessions

" + + ""); + total = 0; Set outbound = getOutboundSessions(); for (Iterator iter = outbound.iterator(); iter.hasNext();) { OutboundSession sess = iter.next(); - buf.append(""); - buf.append(""); - buf.append("" + + "" + + ""); + buf.append("\n"); + out.write(buf.toString()); + buf.setLength(0); } - buf.append("
Target key: ").append(sess.getTarget().toString()).append("
"); - buf.append("Established: ").append(new Date(sess.getEstablishedDate())).append("
"); - buf.append("Last Used: ").append(new Date(sess.getLastUsedDate())).append("
"); - buf.append("# Sets: ").append(sess.getTagSets().size()).append("
Session key: ").append(sess.getCurrentKey().toBase64()).append("
    "); - for (Iterator siter = sess.getTagSets().iterator(); siter.hasNext();) { + Set sets = new TreeSet(new TagSetComparator()); + sets.addAll(sess.getTagSets()); + buf.append("
Target key: ").append(sess.getTarget().toString()).append("
" + + "Established: ").append(DataHelper.formatDuration(now - sess.getEstablishedDate())).append(" ago
" + + "Last Used: ").append(DataHelper.formatDuration(now - sess.getLastUsedDate())).append(" ago
" + + "# Sets: ").append(sess.getTagSets().size()).append("
Session key: ").append(sess.getCurrentKey().toBase64()).append("
    "); + for (Iterator siter = sets.iterator(); siter.hasNext();) { TagSet ts = siter.next(); - buf.append("
  • Sent on: ").append(new Date(ts.getDate())).append(" with ").append( - ts.getTags() - .size()) - .append(" tags remaining
  • "); + int size = ts.getTags().size(); + total += size; + buf.append("
  • Sent: ").append(DataHelper.formatDuration(now - ts.getDate())).append(" ago with "); + buf.append(size).append(" tags remaining
  • "); } - buf.append("
"); + buf.append("
Total tags: ").append(total).append(" ("); + buf.append(DataHelper.formatSize(32*total)).append("B)
"); - return buf.toString(); + out.write(buf.toString()); + } + + /** + * Just for the HTML method above so we can see what's going on easier + * Earliest first + */ + private class TagSetComparator implements Comparator { + public int compare(Object l, Object r) { + return (int) (((TagSet)l).getDate() - ((TagSet)r).getDate()); + } } class OutboundSession { @@ -760,7 +790,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { @Override public int hashCode() { long rv = 0; - if (_key != null) rv = rv * 7 + _key.hashCode(); + if (_key != null) rv = _key.hashCode(); rv = rv * 7 + _date; // no need to hashCode the tags, key + date should be enough return (int) rv; diff --git a/router/java/src/net/i2p/router/ClientManagerFacade.java b/router/java/src/net/i2p/router/ClientManagerFacade.java index b6c377b70..131823287 100644 --- a/router/java/src/net/i2p/router/ClientManagerFacade.java +++ b/router/java/src/net/i2p/router/ClientManagerFacade.java @@ -92,6 +92,6 @@ public abstract class ClientManagerFacade implements Service { * */ public abstract SessionConfig getClientSessionConfig(Destination dest); - public abstract SessionKeyManager getClientSessionKeyManager(Destination dest); + public abstract SessionKeyManager getClientSessionKeyManager(Hash dest); public void renderStatusHTML(Writer out) throws IOException { } } diff --git a/router/java/src/net/i2p/router/DummyClientManagerFacade.java b/router/java/src/net/i2p/router/DummyClientManagerFacade.java index 5e362e3dd..9c0c6838e 100644 --- a/router/java/src/net/i2p/router/DummyClientManagerFacade.java +++ b/router/java/src/net/i2p/router/DummyClientManagerFacade.java @@ -41,7 +41,7 @@ public class DummyClientManagerFacade extends ClientManagerFacade { public void messageDeliveryStatusUpdate(Destination fromDest, MessageId id, boolean delivered) {} public SessionConfig getClientSessionConfig(Destination _dest) { return null; } - public SessionKeyManager getClientSessionKeyManager(Destination _dest) { return null; } + public SessionKeyManager getClientSessionKeyManager(Hash _dest) { return null; } public void requestLeaseSet(Hash dest, LeaseSet set) {} diff --git a/router/java/src/net/i2p/router/client/ClientManager.java b/router/java/src/net/i2p/router/client/ClientManager.java index 8221a0bca..cbc5d778b 100644 --- a/router/java/src/net/i2p/router/client/ClientManager.java +++ b/router/java/src/net/i2p/router/client/ClientManager.java @@ -328,9 +328,10 @@ public class ClientManager { /** * Return the client's SessionKeyManager - * + * Use this instead of the RouterContext.sessionKeyManager() + * to prevent correlation attacks across destinations */ - public SessionKeyManager getClientSessionKeyManager(Destination dest) { + public SessionKeyManager getClientSessionKeyManager(Hash dest) { ClientConnectionRunner runner = getRunner(dest); if (runner != null) return runner.getSessionKeyManager(); diff --git a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java index 360cdb611..e90d12f53 100644 --- a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java +++ b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java @@ -194,7 +194,7 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade { * Return the client's current manager or null if not connected * */ - public SessionKeyManager getClientSessionKeyManager(Destination dest) { + public SessionKeyManager getClientSessionKeyManager(Hash dest) { if (_manager != null) return _manager.getClientSessionKeyManager(dest); else { From 80f9f857e5196261f4435073e38b01327aa2e2da Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 23 Aug 2009 17:42:04 +0000 Subject: [PATCH 11/31] more HTML fixup --- .../i2p/crypto/TransientSessionKeyManager.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java index 2e034e2b1..e079fce5d 100644 --- a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java +++ b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java @@ -514,7 +514,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { ""); Set inbound = getInboundTagSets(); Map> inboundSets = new HashMap(inbound.size()); - // FIXME what does this loop do? nothing? + // Build a map of the inbound tag sets, grouped by SessionKey for (Iterator iter = inbound.iterator(); iter.hasNext();) { TagSet ts = iter.next(); if (!inboundSets.containsKey(ts.getAssociatedKey())) inboundSets.put(ts.getAssociatedKey(), new HashSet()); @@ -541,8 +541,8 @@ public class TransientSessionKeyManager extends SessionKeyManager { out.write(buf.toString()); buf.setLength(0); } - buf.append("\n" + + buf.append("\n" + "
Total tags: ").append(total).append(" ("); - buf.append(DataHelper.formatSize(32*total)).append("B)
Total tags: ").append(total).append(" ("); + buf.append(DataHelper.formatSize(32*total)).append("B)
" + "

Outbound sessions

" + ""); @@ -552,12 +552,12 @@ public class TransientSessionKeyManager extends SessionKeyManager { OutboundSession sess = iter.next(); Set sets = new TreeSet(new TagSetComparator()); sets.addAll(sess.getTagSets()); - buf.append("" + - "" + - "" + + "" + + "\n" + + buf.append("\n" + "
Target key: ").append(sess.getTarget().toString()).append("
" + + buf.append("
Target key: ").append(sess.getTarget().toBase64().substring(0, 64)).append("
" + "Established: ").append(DataHelper.formatDuration(now - sess.getEstablishedDate())).append(" ago
" + "Last Used: ").append(DataHelper.formatDuration(now - sess.getLastUsedDate())).append(" ago
" + - "# Sets: ").append(sess.getTagSets().size()).append("
Session key: ").append(sess.getCurrentKey().toBase64()).append("
    "); + "Session key: ").append(sess.getCurrentKey().toBase64()).append("
# Sets: ").append(sess.getTagSets().size()).append("
    "); for (Iterator siter = sets.iterator(); siter.hasNext();) { TagSet ts = siter.next(); int size = ts.getTags().size(); @@ -569,8 +569,8 @@ public class TransientSessionKeyManager extends SessionKeyManager { out.write(buf.toString()); buf.setLength(0); } - buf.append("
Total tags: ").append(total).append(" ("); - buf.append(DataHelper.formatSize(32*total)).append("B)
Total tags: ").append(total).append(" ("); + buf.append(DataHelper.formatSize(32*total)).append("B)
"); out.write(buf.toString()); From 456ed0aab48abf0c20bf0e63399dedb512c2e477 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 24 Aug 2009 00:28:49 +0000 Subject: [PATCH 12/31] history for 2 props, -17 --- history.txt | 30 +++++++++++++++++++ .../src/net/i2p/router/RouterVersion.java | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index dd75c7023..181c85cfd 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,33 @@ +2009-08-24 zzz + * ClientManager: + - Prevent client destination theft by rejecting duplicates + - Java 5 cleanups + * Console: + - Put favicon on every page + - Make every page UTF-8, ☃ safe for snowmen + - Remove options boxes on configtunnels.jsp + - Fix UTF-8 form submission (i2ptunnel too) + - Throw 403 instead of 404 from flags.jsp and viewstat.jsp + so we don't render error.jsp + * I2CP: Fix the SessionConfig serializer in DataHelper, + so that UTF-8 tunnel names are not corrupted by + I2CP and can be displayed on the console + * Message: Move 2 unused classes out of the router lib (~15KB) + (more SKM prep) + * Message, I2PSession, SessionKeyManager, Console: + Prep for SessionKeyManager work in the router - + Fix up SKM renderStatusHTML(); add debug.jsp to see it; + Redefine getClientSessionKeyManager(); + More cleanups + * Ministreaming: Kill deprecation warnings + * profiles.jsp: Bulletproofing, less memory usage + * Streaming, I2PSession: + Prep for SessionKeyManager work in the router - + Comment out, deprecate, and javadoc for unused keys and tags, + they are vestiges of end-to-end crypto + * Updates: Verify zip at startup before extracting + * Wrapper: Take a couple fields out of the log so it's narrower + 2009-08-20 zzz * Config files: - Add some path and encoding help diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index ac1036698..32a2083be 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 16; + public final static long BUILD = 17; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; From ed0575e9379257acf34246e9dc3b15a0c51b4f5d Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 24 Aug 2009 20:51:09 +0000 Subject: [PATCH 13/31] update launchpad and freshmeat too --- checklist.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checklist.txt b/checklist.txt index e65134b7c..3feaf2a4f 100644 --- a/checklist.txt +++ b/checklist.txt @@ -73,4 +73,4 @@ Website files to change: release-x.y.z.html (new) Sync with mtn.i2p2.i2p -Announce on #i2p, forum.i2p +Announce on #i2p, forum.i2p, freshmeat.net, launchpad.net From 8053fb5eae8cb7b1ce6266da9db152357068654c Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 27 Aug 2009 19:59:11 +0000 Subject: [PATCH 14/31] prop history, -18 --- history.txt | 34 +++++++++++++++++++ .../src/net/i2p/router/RouterVersion.java | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index 181c85cfd..04a3f6369 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,37 @@ +2009-08-28 zzz + * Client: Fail if no date handshake after 30s or no leaseset + after 5m, rather than hanging forever. + * Console: + - Prevent OOMs in NewsFetcher or StatsSummarizer from + killing the router + - Fix favicon (-17) + * Data: Speed up many hashcodes + * DataHelper: Fix byte array hashcode for small arrays + * DecayingBloomFilter: + - Replace with new DecayingHashSet for 3 of 4 uses, + and also in the 4th if the router is low-bandwidth. + Saves 8 MB heap. + * EepGet, I2PSnark: + - New I2PSocketEepGet fetches through existing tunnels + rather than through the proxy + - Use new eepget for i2psnark + - Add a fake user agent for non-proxied fetches + - Cleanups + * NetDb: + - oops, store leaseset locally even when shutting down + (fix -16) + - Java 5 cleanups + * PRNG: + - Rename config option to prng.buffers (was router.prng.buffers) + - Change the default from 16 to 2 for I2PAppContext (saves 3.5MB) + * Tunnel: + - Adjust the random drop probability for the message size + - Concurrentify HashSetIVValidator + * TunnelPool: + - Don't test tunnels when shutting down + - Less rates + - Java 5 cleanups + 2009-08-24 zzz * ClientManager: - Prevent client destination theft by rejecting duplicates diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 32a2083be..4c8aefc89 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 17; + public final static long BUILD = 18; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; From ae89d2f2ab115cd8ae0930820cb7320f92b28bc5 Mon Sep 17 00:00:00 2001 From: z3d Date: Fri, 28 Aug 2009 03:12:23 +0000 Subject: [PATCH 15/31] Rejig netdb.jsp into tables; sidepanel logo refresh. --- apps/routerconsole/jsp/debug.jsp | 5 +-- .../themes/console/classic/console.css | 2 +- .../themes/console/classic/images/i2plogo.png | Bin 0 -> 6507 bytes .../resources/themes/console/dark/i2plogo.png | Bin 0 -> 11479 bytes .../themes/console/light/images/i2plogo.png | Bin 6507 -> 11994 bytes .../KademliaNetworkDatabaseFacade.java | 34 +++++++++--------- 6 files changed, 21 insertions(+), 20 deletions(-) create mode 100644 installer/resources/themes/console/classic/images/i2plogo.png create mode 100644 installer/resources/themes/console/dark/i2plogo.png diff --git a/apps/routerconsole/jsp/debug.jsp b/apps/routerconsole/jsp/debug.jsp index 694f9c1df..b05f46a21 100644 --- a/apps/routerconsole/jsp/debug.jsp +++ b/apps/routerconsole/jsp/debug.jsp @@ -1,10 +1,11 @@ <%@page contentType="text/html"%> <%@page pageEncoding="UTF-8"%> -DEBUG +I2P Router Console - Debug <%@include file="css.jsp" %> <%@include file="summary.jsp" %> +

Router SKM

<% /* @@ -15,7 +16,7 @@ /* * Print out the status for all the SessionKeyManagers */ - out.print("

Router SKM

"); + ctx.sessionKeyManager().renderStatusHTML(out); java.util.Set clients = ctx.clientManager().listClients(); for (net.i2p.data.Destination dest : clients) { diff --git a/installer/resources/themes/console/classic/console.css b/installer/resources/themes/console/classic/console.css index cf7d728d0..b9bcaed8a 100644 --- a/installer/resources/themes/console/classic/console.css +++ b/installer/resources/themes/console/classic/console.css @@ -145,7 +145,7 @@ div.routersummary { width: 185px; color: inherit; margin: 0; - padding: 7px 1px; + padding: 10px 1px 7px 1px; text-align: center !important; border: 5px solid #bbf; font-size: 9pt; diff --git a/installer/resources/themes/console/classic/images/i2plogo.png b/installer/resources/themes/console/classic/images/i2plogo.png new file mode 100644 index 0000000000000000000000000000000000000000..52d1fd6c7d7e7f5ccd9c1cac26f5502a4549d007 GIT binary patch literal 6507 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 RS2X|t002ovPDHLkV1kNC;u!z{ literal 0 HcmV?d00001 diff --git a/installer/resources/themes/console/dark/i2plogo.png b/installer/resources/themes/console/dark/i2plogo.png new file mode 100644 index 0000000000000000000000000000000000000000..0448b9065f4d491581a7ff6bfb23ae5ed545d6ea GIT binary patch literal 11479 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 literal 0 HcmV?d00001 diff --git a/installer/resources/themes/console/light/images/i2plogo.png b/installer/resources/themes/console/light/images/i2plogo.png index 52d1fd6c7d7e7f5ccd9c1cac26f5502a4549d007..ac6a6c619c99e2b8574275ccedc0b92b0b00b206 100644 GIT binary patch literal 11994 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 RS2X|t002ovPDHLkV1kNC;u!z{ diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index ff53f7d15..5d4db71a5 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -998,7 +998,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { StringBuilder buf = new StringBuilder(size); out.write("

Network Database Contents (View LeaseSets)

\n"); if (!_initialized) { - buf.append("Not initialized\n"); + buf.append("Not initialized\n"); out.write(buf.toString()); out.flush(); return; @@ -1047,8 +1047,8 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { buf.append("VersionCount\n"); for (String routerVersion : versionList) { int num = versions.count(routerVersion); - buf.append("").append(DataHelper.stripHTML(routerVersion)); - buf.append("").append(num).append("\n"); + buf.append("").append(DataHelper.stripHTML(routerVersion)); + buf.append("").append(num).append("\n"); } buf.append("\n"); } @@ -1066,7 +1066,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { buf.append("\"").append(country.toUpperCase()).append("\""); "); buf.append(_context.commSystem().getCountryName(country)); - buf.append("").append(num).append("\n"); + buf.append("").append(num).append("\n"); } buf.append("\n"); } @@ -1081,21 +1081,22 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { */ private void renderRouterInfo(StringBuilder buf, RouterInfo info, boolean isUs, boolean full) { String hash = info.getIdentity().getHash().toBase64(); - buf.append(""); + buf.append("\n"); out.write(buf.toString()); @@ -580,18 +657,27 @@ public class TransientSessionKeyManager extends SessionKeyManager { * Just for the HTML method above so we can see what's going on easier * Earliest first */ - private class TagSetComparator implements Comparator { + private static class TagSetComparator implements Comparator { public int compare(Object l, Object r) { return (int) (((TagSet)l).getDate() - ((TagSet)r).getDate()); } } - class OutboundSession { + private class OutboundSession { private PublicKey _target; private SessionKey _currentKey; private long _established; private long _lastUsed; + /** before the first ack, all tagsets go here. These are never expired, we rely + on the callers to call failTags() or ackTags() to remove them from this list. */ + private /* FIXME final FIXME */ List _unackedTagSets; + /** + * As tagsets are acked, they go here. + * After the first ack, new tagsets go here (i.e. presumed acked) + */ private /* FIXME final FIXME */ List _tagSets; + /** set to true after first tagset is acked */ + private boolean _acked; public OutboundSession(PublicKey target) { this(target, null, _context.clock().now(), _context.clock().now(), new ArrayList()); @@ -602,14 +688,43 @@ public class TransientSessionKeyManager extends SessionKeyManager { _currentKey = curKey; _established = established; _lastUsed = lastUsed; - _tagSets = tagSets; + _unackedTagSets = tagSets; + _tagSets = new ArrayList(); } - /** list of TagSet objects */ + /** + * @return list of TagSet objects + * This is used only by renderStatusHTML(). + * It includes both acked and unacked TagSets. + */ List getTagSets() { - synchronized (_tagSets) { - return new ArrayList(_tagSets); + List rv; + synchronized (_unackedTagSets) { + rv = new ArrayList(_unackedTagSets); } + synchronized (_tagSets) { + rv.addAll(_tagSets); + } + return rv; + } + + /** + * got an ack for these tags + * For tagsets delivered after the session was acked, this is a nop + * because the tagset was originally placed directly on the acked list. + */ + void ackTags(TagSet set) { + if (_unackedTagSets.remove(set)) { + _tagSets.add(set); + _acked = true; + } + set.setAcked(); + } + + /** didn't get an ack for these tags */ + void failTags(TagSet set) { + _unackedTagSets.remove(set); + _tagSets.remove(set); } public PublicKey getTarget() { @@ -656,7 +771,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { int removed = 0; synchronized (_tagSets) { for (int i = 0; i < _tagSets.size(); i++) { - TagSet set = (TagSet) _tagSets.get(i); + TagSet set = _tagSets.get(i); if (set.getDate() + SESSION_TAG_DURATION_MS <= now) { _tagSets.remove(i); i--; @@ -672,7 +787,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { _lastUsed = now; synchronized (_tagSets) { while (_tagSets.size() > 0) { - TagSet set = (TagSet) _tagSets.get(0); + TagSet set = _tagSets.get(0); if (set.getDate() + SESSION_TAG_DURATION_MS > now) { SessionTag tag = set.consumeNext(); if (tag != null) return tag; @@ -686,12 +801,13 @@ public class TransientSessionKeyManager extends SessionKeyManager { return null; } + /** @return the total number of tags in acked TagSets */ public int availableTags() { int tags = 0; long now = _context.clock().now(); synchronized (_tagSets) { for (int i = 0; i < _tagSets.size(); i++) { - TagSet set = (TagSet) _tagSets.get(i); + TagSet set = _tagSets.get(i); if (set.getDate() + SESSION_TAG_DURATION_MS > now) tags += set.getTags().size(); } @@ -719,19 +835,31 @@ public class TransientSessionKeyManager extends SessionKeyManager { return -1; } + /** + * If the session has never been acked, put the TagSet on the unacked list. + * Otherwise, consider it good right away. + */ public void addTags(TagSet set) { _lastUsed = _context.clock().now(); - synchronized (_tagSets) { - _tagSets.add(set); + if (_acked) { + synchronized (_tagSets) { + _tagSets.add(set); + } + } else { + synchronized (_unackedTagSets) { + _unackedTagSets.add(set); + } } } } - static class TagSet { + private static class TagSet implements TagSetHandle { private Set _sessionTags; private SessionKey _key; private long _date; - private Exception _createdBy; + //private Exception _createdBy; + /** only used in renderStatusHTML() for debugging */ + private boolean _acked; public TagSet(Set tags, SessionKey key, long date) { if (key == null) throw new IllegalArgumentException("Missing key"); @@ -739,12 +867,12 @@ public class TransientSessionKeyManager extends SessionKeyManager { _sessionTags = tags; _key = key; _date = date; - if (true) { - long now = I2PAppContext.getGlobalContext().clock().now(); - _createdBy = new Exception("Created by: key=" + _key.toBase64() + " on " - + new Date(now) + "/" + now - + " via " + Thread.currentThread().getName()); - } + //if (true) { + // long now = I2PAppContext.getGlobalContext().clock().now(); + // _createdBy = new Exception("Created by: key=" + _key.toBase64() + " on " + // + new Date(now) + "/" + now + // + " via " + Thread.currentThread().getName()); + //} } /** when the tag set was created */ @@ -770,22 +898,26 @@ public class TransientSessionKeyManager extends SessionKeyManager { } public void consume(SessionTag tag) { - if (contains(tag)) { - _sessionTags.remove(tag); - } + _sessionTags.remove(tag); } + /** let's do this without counting the elements first */ public SessionTag consumeNext() { - if (_sessionTags.size() <= 0) { + SessionTag first; + try { + first = _sessionTags.iterator().next(); + } catch (NoSuchElementException nsee) { return null; } - - SessionTag first = (SessionTag) _sessionTags.iterator().next(); _sessionTags.remove(first); return first; } - public Exception getCreatedBy() { return _createdBy; } + //public Exception getCreatedBy() { return _createdBy; } + + public void setAcked() { _acked = true; } + /** only used in renderStatusHTML() for debugging */ + public boolean getAcked() { return _acked; } @Override public int hashCode() { @@ -800,9 +932,19 @@ public class TransientSessionKeyManager extends SessionKeyManager { public boolean equals(Object o) { if ((o == null) || !(o instanceof TagSet)) return false; TagSet ts = (TagSet) o; - return DataHelper.eq(ts.getAssociatedKey(), getAssociatedKey()) + return DataHelper.eq(ts.getAssociatedKey(), _key) //&& DataHelper.eq(ts.getTags(), getTags()) - && ts.getDate() == getDate(); + && ts.getDate() == _date; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(256); + buf.append("TagSet established: ").append(new Date(_date)); + buf.append(" Session key: ").append(_key.toBase64()); + buf.append(" Size: ").append(_sessionTags.size()); + buf.append(" Acked? ").append(_acked); + return buf.toString(); } } } diff --git a/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java b/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java index a049b7b8c..961c0f769 100644 --- a/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java +++ b/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java @@ -17,7 +17,7 @@ import java.util.Set; import net.i2p.crypto.SessionKeyManager; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; -import net.i2p.data.Destination; +import net.i2p.data.Hash; import net.i2p.data.PublicKey; import net.i2p.data.SessionKey; import net.i2p.data.SessionTag; @@ -59,14 +59,16 @@ public class GarlicMessageBuilder { * * So a value somewhat higher than the low threshold * seems appropriate. + * + * Use care when adjusting these values. See ConnectionOptions in streaming, + * and TransientSessionKeyManager in crypto, for more information. */ private static final int DEFAULT_TAGS = 40; - private static final int LOW_THRESHOLD = 20; + private static final int LOW_THRESHOLD = 30; - public static int estimateAvailableTags(RouterContext ctx, PublicKey key, Destination local) { - // per-dest Unimplemented - //SessionKeyManager skm = ctx.clientManager().getClientSessionKeyManager(local); - SessionKeyManager skm = ctx.sessionKeyManager(); + /** @param local non-null; do not use this method for the router's SessionKeyManager */ + public static int estimateAvailableTags(RouterContext ctx, PublicKey key, Hash local) { + SessionKeyManager skm = ctx.clientManager().getClientSessionKeyManager(local); if (skm == null) return 0; SessionKey curKey = skm.getCurrentKey(key); @@ -75,19 +77,54 @@ public class GarlicMessageBuilder { return skm.getAvailableTags(key, curKey); } - public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config) { - return buildMessage(ctx, config, new SessionKey(), new HashSet()); + /** + * Unused and probably a bad idea. + * + * Used below only on a recursive call if the garlic message contains a garlic message. + * We don't need the SessionKey or SesssionTags returned + * This uses the router's SKM, which is probably not what you want. + * This isn't fully implemented, because the key and tags aren't saved - maybe + * it should force elGamal? + * + * @param ctx scope + * @param config how/what to wrap + */ + private static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config) { + Log log = ctx.logManager().getLog(GarlicMessageBuilder.class); + log.error("buildMessage 2 args, using router SKM", new Exception("who did it")); + return buildMessage(ctx, config, new SessionKey(), new HashSet(), ctx.sessionKeyManager()); } - public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags) { - return buildMessage(ctx, config, wrappedKey, wrappedTags, DEFAULT_TAGS); + /** + * called by OCMJH + * + * @param ctx scope + * @param config how/what to wrap + * @param wrappedKey output parameter that will be filled with the sessionKey used + * @param wrappedTags output parameter that will be filled with the sessionTags used + */ + public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, + SessionKeyManager skm) { + return buildMessage(ctx, config, wrappedKey, wrappedTags, DEFAULT_TAGS, false, skm); } - public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, int numTagsToDeliver) { + /** unused */ + /*** + public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, + int numTagsToDeliver) { return buildMessage(ctx, config, wrappedKey, wrappedTags, numTagsToDeliver, false); } + ***/ - public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, int numTagsToDeliver, boolean forceElGamal) { + /** + * @param ctx scope + * @param config how/what to wrap + * @param wrappedKey output parameter that will be filled with the sessionKey used + * @param wrappedTags output parameter that will be filled with the sessionTags used + * @param numTagsToDeliver only if the estimated available tags are below the threshold + */ + private static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, + int numTagsToDeliver, boolean forceElGamal, SessionKeyManager skm) { Log log = ctx.logManager().getLog(GarlicMessageBuilder.class); PublicKey key = config.getRecipientPublicKey(); if (key == null) { @@ -104,14 +141,14 @@ public class GarlicMessageBuilder { if (log.shouldLog(Log.INFO)) log.info("Encrypted with public key " + key + " to expire on " + new Date(config.getExpiration())); - SessionKey curKey = ctx.sessionKeyManager().getCurrentKey(key); + SessionKey curKey = skm.getCurrentKey(key); SessionTag curTag = null; if (curKey == null) - curKey = ctx.sessionKeyManager().createSession(key); + curKey = skm.createSession(key); if (!forceElGamal) { - curTag = ctx.sessionKeyManager().consumeNextAvailableTag(key, curKey); + curTag = skm.consumeNextAvailableTag(key, curKey); - int availTags = ctx.sessionKeyManager().getAvailableTags(key, curKey); + int availTags = skm.getAvailableTags(key, curKey); if (log.shouldLog(Log.DEBUG)) log.debug("Available tags for encryption to " + key + ": " + availTags); @@ -120,7 +157,7 @@ public class GarlicMessageBuilder { wrappedTags.add(new SessionTag(true)); if (log.shouldLog(Log.INFO)) log.info("Too few are available (" + availTags + "), so we're including more"); - } else if (ctx.sessionKeyManager().getAvailableTimeLeft(key, curKey) < 60*1000) { + } else if (skm.getAvailableTimeLeft(key, curKey) < 60*1000) { // if we have enough tags, but they expire in under 30 seconds, we want more for (int i = 0; i < numTagsToDeliver; i++) wrappedTags.add(new SessionTag(true)); @@ -138,16 +175,19 @@ public class GarlicMessageBuilder { } /** + * used by TestJob and directly above + * * @param ctx scope * @param config how/what to wrap - * @param wrappedKey output parameter that will be filled with the sessionKey used + * @param wrappedKey unused - why?? * @param wrappedTags output parameter that will be filled with the sessionTags used * @param target public key of the location being garlic routed to (may be null if we * know the encryptKey and encryptTag) * @param encryptKey sessionKey used to encrypt the current message * @param encryptTag sessionTag used to encrypt the current message */ - public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, PublicKey target, SessionKey encryptKey, SessionTag encryptTag) { + public static GarlicMessage buildMessage(RouterContext ctx, GarlicConfig config, SessionKey wrappedKey, Set wrappedTags, + PublicKey target, SessionKey encryptKey, SessionTag encryptTag) { Log log = ctx.logManager().getLog(GarlicMessageBuilder.class); if (config == null) throw new IllegalArgumentException("Null config specified"); @@ -209,6 +249,7 @@ public class GarlicMessageBuilder { cloves[i] = buildClove(ctx, (PayloadGarlicConfig)c); } else { log.debug("Subclove IS NOT a payload garlic clove"); + // See notes below cloves[i] = buildClove(ctx, c); } if (cloves[i] == null) @@ -242,6 +283,22 @@ public class GarlicMessageBuilder { return buildCommonClove(ctx, clove, config); } + /** + * UNUSED + * + * The Garlic Message we are building contains another garlic message, + * as specified by a GarlicConfig (NOT a PayloadGarlicConfig). + * + * So this calls back to the top, to buildMessage(ctx, config), + * which uses the router's SKM, i.e. the wrong one. + * Unfortunately we've lost the reference to the SessionKeyManager way down here, + * so we can't call buildMessage(ctx, config, key, tags, skm). + * + * If we do ever end up constructing a garlic message that contains a garlic message, + * we'll have to fix this by passing the skm through the last buildMessage, + * through buildCloveSet, to here. + * + */ private static byte[] buildClove(RouterContext ctx, GarlicConfig config) throws DataFormatException, IOException { GarlicClove clove = new GarlicClove(ctx); GarlicMessage msg = buildMessage(ctx, config); diff --git a/router/java/src/net/i2p/router/message/GarlicMessageParser.java b/router/java/src/net/i2p/router/message/GarlicMessageParser.java index 84ed50b2c..8d53fc212 100644 --- a/router/java/src/net/i2p/router/message/GarlicMessageParser.java +++ b/router/java/src/net/i2p/router/message/GarlicMessageParser.java @@ -10,6 +10,7 @@ package net.i2p.router.message; import java.util.Date; +import net.i2p.crypto.SessionKeyManager; import net.i2p.data.Certificate; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; @@ -32,13 +33,14 @@ public class GarlicMessageParser { _log = _context.logManager().getLog(GarlicMessageParser.class); } - public CloveSet getGarlicCloves(GarlicMessage message, PrivateKey encryptionKey) { + /** @param skm use tags from this session key manager */ + public CloveSet getGarlicCloves(GarlicMessage message, PrivateKey encryptionKey, SessionKeyManager skm) { byte encData[] = message.getData(); byte decrData[] = null; try { if (_log.shouldLog(Log.DEBUG)) _log.debug("Decrypting with private key " + encryptionKey); - decrData = _context.elGamalAESEngine().decrypt(encData, encryptionKey); + decrData = _context.elGamalAESEngine().decrypt(encData, encryptionKey, skm); } catch (DataFormatException dfe) { if (_log.shouldLog(Log.WARN)) _log.warn("Error decrypting", dfe); diff --git a/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java b/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java index fcc5bbddf..a12d55452 100644 --- a/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java +++ b/router/java/src/net/i2p/router/message/GarlicMessageReceiver.java @@ -8,6 +8,7 @@ package net.i2p.router.message; * */ +import net.i2p.crypto.SessionKeyManager; import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.data.PrivateKey; @@ -47,13 +48,16 @@ public class GarlicMessageReceiver { _clientDestination = clientDestination; _parser = new GarlicMessageParser(context); _receiver = receiver; + //_log.error("New GMR dest = " + clientDestination); } public void receive(GarlicMessage message) { PrivateKey decryptionKey = null; + SessionKeyManager skm = null; if (_clientDestination != null) { LeaseSetKeys keys = _context.keyManager().getKeys(_clientDestination); - if (keys != null) { + skm = _context.clientManager().getClientSessionKeyManager(_clientDestination); + if (keys != null && skm != null) { decryptionKey = keys.getDecryptionKey(); } else { if (_log.shouldLog(Log.WARN)) @@ -62,9 +66,10 @@ public class GarlicMessageReceiver { } } else { decryptionKey = _context.keyManager().getPrivateKey(); + skm = _context.sessionKeyManager(); } - CloveSet set = _parser.getGarlicCloves(message, decryptionKey); + CloveSet set = _parser.getGarlicCloves(message, decryptionKey, skm); if (set != null) { for (int i = 0; i < set.getCloveCount(); i++) { GarlicClove clove = set.getClove(i); diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java b/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java index efbdd90f0..75b5c8dc9 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java @@ -17,6 +17,7 @@ import net.i2p.data.LeaseSet; import net.i2p.data.Payload; import net.i2p.data.PublicKey; import net.i2p.data.SessionKey; +import net.i2p.data.SessionTag; import net.i2p.data.TunnelId; import net.i2p.data.i2np.DataMessage; import net.i2p.data.i2np.DatabaseStoreMessage; @@ -46,13 +47,15 @@ class OutboundClientMessageJobHelper { * * For now, its just a tunneled DeliveryStatusMessage * + * Unused? + * * @param bundledReplyLeaseSet if specified, the given LeaseSet will be packaged with the message (allowing * much faster replies, since their netDb search will return almost instantly) * @return garlic, or null if no tunnels were found (or other errors) */ static GarlicMessage createGarlicMessage(RouterContext ctx, long replyToken, long expiration, PublicKey recipientPK, Payload data, Hash from, Destination dest, TunnelInfo replyTunnel, - SessionKey wrappedKey, Set wrappedTags, + SessionKey wrappedKey, Set wrappedTags, boolean requireAck, LeaseSet bundledReplyLeaseSet) { PayloadGarlicConfig dataClove = buildDataClove(ctx, data, dest, expiration); return createGarlicMessage(ctx, replyToken, expiration, recipientPK, dataClove, from, dest, replyTunnel, wrappedKey, @@ -62,15 +65,18 @@ class OutboundClientMessageJobHelper { * Allow the app to specify the data clove directly, which enables OutboundClientMessage to resend the * same payload (including expiration and unique id) in different garlics (down different tunnels) * + * This is called from OCMOSJ + * * @return garlic, or null if no tunnels were found (or other errors) */ static GarlicMessage createGarlicMessage(RouterContext ctx, long replyToken, long expiration, PublicKey recipientPK, PayloadGarlicConfig dataClove, Hash from, Destination dest, TunnelInfo replyTunnel, SessionKey wrappedKey, - Set wrappedTags, boolean requireAck, LeaseSet bundledReplyLeaseSet) { + Set wrappedTags, boolean requireAck, LeaseSet bundledReplyLeaseSet) { GarlicConfig config = createGarlicConfig(ctx, replyToken, expiration, recipientPK, dataClove, from, dest, replyTunnel, requireAck, bundledReplyLeaseSet); if (config == null) return null; - GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, config, wrappedKey, wrappedTags); + GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, config, wrappedKey, wrappedTags, + ctx.clientManager().getClientSessionKeyManager(from)); return msg; } diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java index 5263a614e..9e1ad88a4 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java @@ -10,6 +10,8 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import net.i2p.crypto.SessionKeyManager; +import net.i2p.crypto.TagSetHandle; import net.i2p.data.Base64; import net.i2p.data.Certificate; import net.i2p.data.Destination; @@ -20,6 +22,7 @@ import net.i2p.data.Payload; import net.i2p.data.PublicKey; import net.i2p.data.RouterInfo; import net.i2p.data.SessionKey; +import net.i2p.data.SessionTag; import net.i2p.data.i2cp.MessageId; import net.i2p.data.i2np.DataMessage; import net.i2p.data.i2np.DeliveryInstructions; @@ -471,7 +474,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl { return; } - int existingTags = GarlicMessageBuilder.estimateAvailableTags(getContext(), _leaseSet.getEncryptionKey(), _from); + int existingTags = GarlicMessageBuilder.estimateAvailableTags(getContext(), _leaseSet.getEncryptionKey(), + _from.calculateHash()); _outTunnel = selectOutboundTunnel(_to); // boolean wantACK = _wantACK || existingTags <= 30 || getContext().random().nextInt(100) < 5; // what's the point of 5% random? possible improvements or replacements: @@ -489,7 +493,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { PublicKey key = _leaseSet.getEncryptionKey(); SessionKey sessKey = new SessionKey(); - Set tags = new HashSet(); + Set tags = new HashSet(); // If we want an ack, bundle a leaseSet... (so he can get back to us) LeaseSet replyLeaseSet = getReplyLeaseSet(wantACK); // ... and vice versa (so we know he got it) @@ -531,8 +535,16 @@ public class OutboundClientMessageOneShotJob extends JobImpl { SendTimeoutJob onFail = null; ReplySelector selector = null; if (wantACK) { - onReply = new SendSuccessJob(getContext(), sessKey, tags); - onFail = new SendTimeoutJob(getContext()); + TagSetHandle tsh = null; + if ( (sessKey != null) && (tags != null) && (tags.size() > 0) ) { + if (_leaseSet != null) { + SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_from.calculateHash()); + if (skm != null) + tsh = skm.tagsDelivered(_leaseSet.getEncryptionKey(), sessKey, tags); + } + } + onReply = new SendSuccessJob(getContext(), sessKey, tsh); + onFail = new SendTimeoutJob(getContext(), sessKey, tsh); selector = new ReplySelector(token); } @@ -550,9 +562,9 @@ public class OutboundClientMessageOneShotJob extends JobImpl { + _lease.getGateway().toBase64()); DispatchJob dispatchJob = new DispatchJob(getContext(), msg, selector, onReply, onFail, (int)(_overallExpiration-getContext().clock().now())); - if (false) // dispatch may take 100+ms, so toss it in its own job - getContext().jobQueue().addJob(dispatchJob); - else + //if (false) // dispatch may take 100+ms, so toss it in its own job + // getContext().jobQueue().addJob(dispatchJob); + //else dispatchJob.runJob(); } else { if (_log.shouldLog(Log.WARN)) @@ -848,6 +860,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { /** build the payload clove that will be used for all of the messages, placing the clove in the status structure */ private boolean buildClove() { +// FIXME set SKM PayloadGarlicConfig clove = new PayloadGarlicConfig(); DeliveryInstructions instructions = new DeliveryInstructions(); @@ -932,14 +945,14 @@ public class OutboundClientMessageOneShotJob extends JobImpl { */ private class SendSuccessJob extends JobImpl implements ReplyJob { private SessionKey _key; - private Set _tags; + private TagSetHandle _tags; /** * Create a new success job that will be fired when the message encrypted with * the given session key and bearing the specified tags are confirmed delivered. * */ - public SendSuccessJob(RouterContext enclosingContext, SessionKey key, Set tags) { + public SendSuccessJob(RouterContext enclosingContext, SessionKey key, TagSetHandle tags) { super(enclosingContext); _key = key; _tags = tags; @@ -955,10 +968,10 @@ public class OutboundClientMessageOneShotJob extends JobImpl { + ": SUCCESS! msg " + _clientMessageId + " sent after " + sendTime + "ms"); - if ( (_key != null) && (_tags != null) && (_tags.size() > 0) ) { - if (_leaseSet != null) - getContext().sessionKeyManager().tagsDelivered(_leaseSet.getEncryptionKey(), - _key, _tags); + if (_key != null && _tags != null && _leaseSet != null) { + SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_from.calculateHash()); + if (skm != null) + skm.tagsAcked(_leaseSet.getEncryptionKey(), _key, _tags); } long dataMsgId = _cloveId; @@ -994,8 +1007,13 @@ public class OutboundClientMessageOneShotJob extends JobImpl { * */ private class SendTimeoutJob extends JobImpl { - public SendTimeoutJob(RouterContext enclosingContext) { + private SessionKey _key; + private TagSetHandle _tags; + + public SendTimeoutJob(RouterContext enclosingContext, SessionKey key, TagSetHandle tags) { super(enclosingContext); + _key = key; + _tags = tags; } public String getName() { return "Send client message timed out"; } @@ -1005,6 +1023,11 @@ public class OutboundClientMessageOneShotJob extends JobImpl { + ": Soft timeout through the lease " + _lease); _lease.setNumFailure(_lease.getNumFailure()+1); + if (_key != null && _tags != null && _leaseSet != null) { + SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_from.calculateHash()); + if (skm != null) + skm.failTags(_leaseSet.getEncryptionKey(), _key, _tags); + } dieFatal(); } } From 157190757b4fa2f725e0abd482ceb8938c6f483a Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 1 Sep 2009 13:47:27 +0000 Subject: [PATCH 24/31] * SessionKeyManager: - Fix TagSet hashCode - More synchronization --- .../crypto/TransientSessionKeyManager.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java index 57862c3b8..6f4706e26 100644 --- a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java +++ b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java @@ -699,10 +699,8 @@ public class TransientSessionKeyManager extends SessionKeyManager { */ List getTagSets() { List rv; - synchronized (_unackedTagSets) { - rv = new ArrayList(_unackedTagSets); - } synchronized (_tagSets) { + rv = new ArrayList(_unackedTagSets); rv.addAll(_tagSets); } return rv; @@ -714,17 +712,21 @@ public class TransientSessionKeyManager extends SessionKeyManager { * because the tagset was originally placed directly on the acked list. */ void ackTags(TagSet set) { - if (_unackedTagSets.remove(set)) { - _tagSets.add(set); - _acked = true; + synchronized (_tagSets) { + if (_unackedTagSets.remove(set)) { + _tagSets.add(set); + _acked = true; + } } set.setAcked(); } /** didn't get an ack for these tags */ void failTags(TagSet set) { - _unackedTagSets.remove(set); - _tagSets.remove(set); + synchronized (_tagSets) { + _unackedTagSets.remove(set); + _tagSets.remove(set); + } } public PublicKey getTarget() { @@ -919,6 +921,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { /** only used in renderStatusHTML() for debugging */ public boolean getAcked() { return _acked; } +/****** this will return a dup if two in the same ms, so just use java @Override public int hashCode() { long rv = 0; @@ -936,6 +939,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { //&& DataHelper.eq(ts.getTags(), getTags()) && ts.getDate() == _date; } +******/ @Override public String toString() { From 49ff3cfbf32be3b7db061a46df17b7e6caa7b7b9 Mon Sep 17 00:00:00 2001 From: sponge Date: Wed, 2 Sep 2009 00:08:03 +0000 Subject: [PATCH 25/31] * Small logic fix for dr|z3d --- history.txt | 3 +++ .../networkdb/kademlia/KademliaNetworkDatabaseFacade.java | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/history.txt b/history.txt index 04a3f6369..d44b0bac8 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,6 @@ +2009-09-02 sponge + * Small logic fix for dr|z3d + 2009-08-28 zzz * Client: Fail if no date handshake after 30s or no leaseset after 5m, rather than hanging forever. diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index f0ce99cf8..f6a1a31ef 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -1091,7 +1091,11 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade { buf.append("Our info: ").append(hash).append("
"); if (isUs) { - buf.append("Our info: ").append(hash).append("
\n"); + buf.append("Our info: ").append(hash).append("
\n"); } else { - buf.append("Peer info for: ").append(hash).append("
\n"); + buf.append("Peer info for: ").append(hash).append("\n"); + buf.append("[Full entry]
\n"); } long age = _context.clock().now() - info.getPublished(); if (isUs && _context.router().isHidden()) - buf.append("Hidden, Updated: ").append(DataHelper.formatDuration(age)).append(" ago
\n"); + buf.append("Hidden, Updated: ").append(DataHelper.formatDuration(age)).append(" ago
\n"); else if (age > 0) - buf.append("Published: ").append(DataHelper.formatDuration(age)).append(" ago
\n"); + buf.append("Published: ").append(DataHelper.formatDuration(age)).append(" ago
\n"); else - buf.append("Published: in ").append(DataHelper.formatDuration(0-age)).append("???
\n"); - buf.append("Address(es): "); + buf.append("Published: in ").append(DataHelper.formatDuration(0-age)).append("???
\n"); + buf.append("Address(es): "); String country = _context.commSystem().getCountry(info.getIdentity().getHash()); if(country != null) { buf.append("\"").append(country.toUpperCase()).append("\""); Date: Sun, 30 Aug 2009 16:13:46 +0000 Subject: [PATCH 21/31] new disable tunnel tests option --- .../java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java index a83b78aaa..f7c752c8c 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java @@ -309,7 +309,8 @@ public class TunnelPoolManager implements TunnelManagerFacade { void buildComplete(PooledTunnelCreatorConfig cfg) { //buildComplete(); if (cfg.getLength() > 1 && - !_context.router().gracefulShutdownInProgress()) { + (!_context.router().gracefulShutdownInProgress()) && + !Boolean.valueOf(_context.getProperty("router.disableTunnelTesting")).booleanValue()) { TunnelPool pool = cfg.getTunnelPool(); if (pool == null) { // never seen this before, do we reallly need to bother From 15f0cda41f7298c22709a83710ee4f24d9efa22c Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 30 Aug 2009 16:21:38 +0000 Subject: [PATCH 22/31] cleanup of things moved to GarlicMessageReceiver long ago --- .../message/HandleGarlicMessageJob.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java b/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java index 05aecfb89..32959b3a4 100644 --- a/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java +++ b/router/java/src/net/i2p/router/message/HandleGarlicMessageJob.java @@ -31,14 +31,18 @@ import net.i2p.util.Log; public class HandleGarlicMessageJob extends JobImpl implements GarlicMessageReceiver.CloveReceiver { private Log _log; private GarlicMessage _message; - private RouterIdentity _from; - private Hash _fromHash; - private Map _cloves; // map of clove Id --> Expiration of cloves we've already seen + //private RouterIdentity _from; + //private Hash _fromHash; + //private Map _cloves; // map of clove Id --> Expiration of cloves we've already seen //private MessageHandler _handler; - private GarlicMessageParser _parser; + //private GarlicMessageParser _parser; private final static int FORWARD_PRIORITY = 50; + /** + * @param from ignored + * @param fromHash ignored + */ public HandleGarlicMessageJob(RouterContext context, GarlicMessage msg, RouterIdentity from, Hash fromHash) { super(context); _log = context.logManager().getLog(HandleGarlicMessageJob.class); @@ -46,11 +50,11 @@ public class HandleGarlicMessageJob extends JobImpl implements GarlicMessageRece if (_log.shouldLog(Log.DEBUG)) _log.debug("New handle garlicMessageJob called w/ message from [" + from + "]", new Exception("Debug")); _message = msg; - _from = from; - _fromHash = fromHash; - _cloves = new HashMap(); + //_from = from; + //_fromHash = fromHash; + //_cloves = new HashMap(); //_handler = new MessageHandler(context); - _parser = new GarlicMessageParser(context); + //_parser = new GarlicMessageParser(context); } public String getName() { return "Handle Inbound Garlic Message"; } From e0f1047d72acb3bbd64225054133417ccad69f4a Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 30 Aug 2009 16:27:03 +0000 Subject: [PATCH 23/31] * SessionKeyManager, OCMOSJ, Garlic: - Enable per-client SessionKeyManagers for better anonymity - tagsDelivered() now means tags are sent, not acked. - OCMOSJ uses the new TagSetHandle object returned from tagsDelivered() to call tagsAcked() or failTags() as appropriate. - Assume tags delivered on an established session to reduce streaming lib stalls caused by massive tag deliveries; should increase throughput and window sizes on long-lived streams - Unacked tagsets on a new session are stored on a separate list - Don't kill an OB Session just because it's temporarily out of tags - Increase min tag threshold to 30 (was 20) due to new speculative tags delivered scheme, and to increase effective max window - More Java 5 and dead code cleanups, and more comments and javadoc, debug logging cleanups --- .../src/net/i2p/crypto/SessionKeyManager.java | 5 +- .../java/src/net/i2p/crypto/TagSetHandle.java | 8 + .../crypto/TransientSessionKeyManager.java | 260 ++++++++++++++---- .../router/message/GarlicMessageBuilder.java | 95 +++++-- .../router/message/GarlicMessageParser.java | 6 +- .../router/message/GarlicMessageReceiver.java | 9 +- .../OutboundClientMessageJobHelper.java | 12 +- .../OutboundClientMessageOneShotJob.java | 51 +++- 8 files changed, 346 insertions(+), 100 deletions(-) create mode 100644 core/java/src/net/i2p/crypto/TagSetHandle.java diff --git a/core/java/src/net/i2p/crypto/SessionKeyManager.java b/core/java/src/net/i2p/crypto/SessionKeyManager.java index 2cd86ba7d..126ab0c03 100644 --- a/core/java/src/net/i2p/crypto/SessionKeyManager.java +++ b/core/java/src/net/i2p/crypto/SessionKeyManager.java @@ -95,7 +95,8 @@ public class SessionKeyManager { * method after receiving an ack to a message delivering them) * */ - public void tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) { // nop + public TagSetHandle tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) { // nop + return null; } /** @@ -134,4 +135,6 @@ public class SessionKeyManager { } public void renderStatusHTML(Writer out) throws IOException {} + public void failTags(PublicKey target, SessionKey key, TagSetHandle ts) {} + public void tagsAcked(PublicKey target, SessionKey key, TagSetHandle ts) {} } diff --git a/core/java/src/net/i2p/crypto/TagSetHandle.java b/core/java/src/net/i2p/crypto/TagSetHandle.java new file mode 100644 index 000000000..7e06939b5 --- /dev/null +++ b/core/java/src/net/i2p/crypto/TagSetHandle.java @@ -0,0 +1,8 @@ +package net.i2p.crypto; + +/** + * An opaque handle to a TagSet returned by the SessionKeyManager, + * so that OCMOSJ can report that the tags were later acked, or not. + * + */ +public interface TagSetHandle {} diff --git a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java index e079fce5d..57862c3b8 100644 --- a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java +++ b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java @@ -19,6 +19,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeSet; @@ -36,6 +37,41 @@ import net.i2p.util.SimpleTimer; * to disk). However, this being java, we cannot guarantee that the keys aren't swapped * out to disk so this should not be considered secure in that sense. * + * The outbound and inbound sides are completely independent, each with + * their own keys and tags. + * + * For a new session, outbound tags are not considered delivered until an ack is received. + * Otherwise, the loss of the first message would render all subsequent messages + * undecryptable. True? + * + * For an existing session, outbound tags are immediately considered delivered, and are + * later revoked if the ack times out. This prevents massive stream slowdown caused by + * repeated tag delivery after the minimum tag threshold is reached. Included tags + * pushes messages above the ideal 1956 size by ~2KB and causes excessive fragmentation + * and padding. As the tags are not seen by the streaming lib, they aren't accounted + * for in the window size, and one or more of a series of large messages is likely to be dropped, + * either due to high fragmentation or drop priorites at the tunnel OBEP. + * + * For this to work, the minimum tag threshold and tag delivery quanitity defined in + * GarlicMessageBuilder must be chosen with streaming lib windows sizes in mind. + * If a single TagSet is not delivered, there will be no stall as long as the + * current window size is smaller than the minimum tag threshold. + * A second consecutive TagSet delivery failure will cause a complete stall, as + * all subsequent messages will fail to decrypt. + * See ConnectionOptions in streaming for more information. + * + * There are large inefficiencies caused by the repeated delivery of tags in a new session. + * With an initial streaming window size of 6 and 40 tags per delivery, a web server + * would deliver up to 240 tags (7680 bytes, not including bundled leaseset, etc.) + * in the first volley of the response. + * + * Could the two directions be linked somehow, such that the initial request could + * contain a key or tags for the response? + * + * Should the tag threshold and quantity be adaptive? + * + * Todo: Switch to ConcurrentHashMaps and ReadWriteLocks, only get write lock during cleanup + * */ public class TransientSessionKeyManager extends SessionKeyManager { private Log _log; @@ -126,6 +162,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { } /* FIXME Exporting non-public type through public API */ +/****** leftover from when we had the persistent SKM protected void setData(Set inboundTagSets, Set outboundSessions) { if (_log.shouldLog(Log.INFO)) _log.info("Loading " + inboundTagSets.size() + " inbound tag sets, and " @@ -152,6 +189,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { _outboundSessions.putAll(sessions); } } +******/ /** * Retrieve the session key currently associated with encryption to the target, @@ -179,13 +217,10 @@ public class TransientSessionKeyManager extends SessionKeyManager { * Associate a new session key with the specified target. Metrics to determine * when to expire that key begin with this call. * - * Unused except in tests? */ @Override public void createSession(PublicKey target, SessionKey key) { - OutboundSession sess = new OutboundSession(target); - sess.setCurrentKey(key); - addSession(sess); + createAndReturnSession(target, key); } /** @@ -218,7 +253,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { if (sess.getCurrentKey().equals(key)) { SessionTag nxt = sess.consumeNext(); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Tag consumed: " + nxt + " with key: " + key.toBase64()); + _log.debug("OB Tag consumed: " + nxt + " with: " + key); return nxt; } if (_log.shouldLog(Log.DEBUG)) @@ -261,23 +296,31 @@ public class TransientSessionKeyManager extends SessionKeyManager { /** * Take note of the fact that the given sessionTags associated with the key for - * encryption to the target have definitely been received at the target (aka call this - * method after receiving an ack to a message delivering them) + * encryption to the target have been sent. Whether to use the tags immediately + * (i.e. assume they will be received) or to wait until an ack, is implementation dependent. * + * Here, we wait for the ack if the session is new, otherwise we use right away. + * Will this work??? + * If the tags are pipelined sufficiently, it will. + * + * @return the TagSetHandle. Caller MUST subsequently call failTags() or tagsAcked() + * with this handle. */ @Override - public void tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) { + public TagSetHandle tagsDelivered(PublicKey target, SessionKey key, Set sessionTags) { if (_log.shouldLog(Log.DEBUG)) { //_log.debug("Tags delivered to set " + set + " on session " + sess); if (sessionTags.size() > 0) - _log.debug("Tags delivered: " + sessionTags.size() + " for key: " + key.toBase64() + ": " + sessionTags); + _log.debug("Tags delivered: " + sessionTags.size() + " for key: " + key + ": " + sessionTags); } OutboundSession sess = getSession(target); if (sess == null) sess = createAndReturnSession(target, key); - sess.setCurrentKey(key); + else + sess.setCurrentKey(key); TagSet set = new TagSet(sessionTags, key, _context.clock().now()); sess.addTags(set); + return set; } /** @@ -285,12 +328,44 @@ public class TransientSessionKeyManager extends SessionKeyManager { * has failed to respond when they should have. This call essentially lets the system recover * from corrupted tag sets and crashes * + * @deprecated unused and rather drastic */ @Override public void failTags(PublicKey target) { removeSession(target); } + /** + * Mark these tags as invalid, since the peer + * has failed to ack them in time. + */ + @Override + public void failTags(PublicKey target, SessionKey key, TagSetHandle ts) { + OutboundSession sess = getSession(target); + if (sess == null) + return; + if(!key.equals(sess.getCurrentKey())) + return; + sess.failTags((TagSet)ts); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("TagSet failed: " + ts); + } + + /** + * Mark these tags as acked, start to use them (if we haven't already) + */ + @Override + public void tagsAcked(PublicKey target, SessionKey key, TagSetHandle ts) { + OutboundSession sess = getSession(target); + if (sess == null) + return; + if(!key.equals(sess.getCurrentKey())) + return; + sess.ackTags((TagSet)ts); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("TagSet acked: " + ts); + } + /** * Accept the given tags and associate them with the given key for decryption * @@ -304,9 +379,9 @@ public class TransientSessionKeyManager extends SessionKeyManager { for (Iterator iter = sessionTags.iterator(); iter.hasNext();) { SessionTag tag = iter.next(); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Receiving tag " + tag + " for key " + key.toBase64() + " / " + key.toString() + ": tagSet: " + tagSet); + _log.debug("Receiving tag " + tag + " for key " + key + ": tagSet: " + tagSet); synchronized (_inboundTagSets) { - old = (TagSet)_inboundTagSets.put(tag, tagSet); + old = _inboundTagSets.put(tag, tagSet); overage = _inboundTagSets.size() - MAX_INBOUND_SESSION_TAGS; if (old != null) { if (!old.getAssociatedKey().equals(tagSet.getAssociatedKey())) { @@ -334,9 +409,9 @@ public class TransientSessionKeyManager extends SessionKeyManager { } if (_log.shouldLog(Log.WARN)) { - _log.warn("Multiple tags matching! tagSet: " + tagSet + " and old tagSet: " + old + " tag: " + dupTag + "/" + dupTag.toBase64()); - _log.warn("Earlier tag set creation: " + old + ": key=" + old.getAssociatedKey().toBase64(), old.getCreatedBy()); - _log.warn("Current tag set creation: " + tagSet + ": key=" + tagSet.getAssociatedKey().toBase64(), tagSet.getCreatedBy()); + _log.warn("Multiple tags matching! tagSet: " + tagSet + " and old tagSet: " + old + " tag: " + dupTag + "/" + dupTag); + _log.warn("Earlier tag set creation: " + old + ": key=" + old.getAssociatedKey()); + _log.warn("Current tag set creation: " + tagSet + ": key=" + tagSet.getAssociatedKey()); } } @@ -410,26 +485,26 @@ public class TransientSessionKeyManager extends SessionKeyManager { */ @Override public SessionKey consumeTag(SessionTag tag) { - if (false) aggressiveExpire(); + //if (false) aggressiveExpire(); synchronized (_inboundTagSets) { TagSet tagSet = (TagSet) _inboundTagSets.remove(tag); if (tagSet == null) { if (_log.shouldLog(Log.DEBUG)) - _log.debug("Cannot consume tag " + tag + " as it is not known"); + _log.debug("Cannot consume IB " + tag + " as it is not known"); return null; } tagSet.consume(tag); SessionKey key = tagSet.getAssociatedKey(); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Consuming tag " + tag.toString() + " for sessionKey " + key.toBase64() + " / " + key.toString() + " on tagSet: " + tagSet); + _log.debug("Consuming IB " + tag + " for " + key + " on: " + tagSet); return key; } } private OutboundSession getSession(PublicKey target) { synchronized (_outboundSessions) { - return (OutboundSession) _outboundSessions.get(target); + return _outboundSessions.get(target); } } @@ -443,7 +518,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { if (target == null) return; OutboundSession session = null; synchronized (_outboundSessions) { - session = (OutboundSession)_outboundSessions.remove(target); + session = _outboundSessions.remove(target); } if ( (session != null) && (_log.shouldLog(Log.WARN)) ) _log.warn("Removing session tags with " + session.availableTags() + " available for " @@ -461,11 +536,11 @@ public class TransientSessionKeyManager extends SessionKeyManager { int remaining = 0; long now = _context.clock().now(); StringBuilder buf = null; - StringBuilder bufSummary = null; + //StringBuilder bufSummary = null; if (_log.shouldLog(Log.DEBUG)) { buf = new StringBuilder(128); buf.append("Expiring inbound: "); - bufSummary = new StringBuilder(1024); + //bufSummary = new StringBuilder(1024); } synchronized (_inboundTagSets) { for (Iterator iter = _inboundTagSets.keySet().iterator(); iter.hasNext();) { @@ -477,10 +552,10 @@ public class TransientSessionKeyManager extends SessionKeyManager { iter.remove(); removed++; if (buf != null) - buf.append(tag.toString()).append(" @ age ").append(DataHelper.formatDuration(age)); - } else if (false && (bufSummary != null) ) { - bufSummary.append("\nTagSet: " + ts.toString() + ", key: " + ts.getAssociatedKey().toBase64()+"/" + ts.getAssociatedKey().toString() - + ": tag: " + tag.toString()); + buf.append(tag).append(" @ age ").append(DataHelper.formatDuration(age)); + //} else if (false && (bufSummary != null) ) { + // bufSummary.append("\nTagSet: " + ts + ", key: " + ts.getAssociatedKey() + // + ": tag: " + tag); } } remaining = _inboundTagSets.size(); @@ -488,8 +563,8 @@ public class TransientSessionKeyManager extends SessionKeyManager { _context.statManager().addRateData("crypto.sessionTagsRemaining", remaining, 0); if ( (buf != null) && (removed > 0) ) _log.debug(buf.toString()); - if (bufSummary != null) - _log.debug("Cleaning up with remaining: " + bufSummary.toString()); + //if (bufSummary != null) + // _log.debug("Cleaning up with remaining: " + bufSummary.toString()); //_log.warn("Expiring tags: [" + tagsToDrop + "]"); @@ -498,9 +573,11 @@ public class TransientSessionKeyManager extends SessionKeyManager { PublicKey key = iter.next(); OutboundSession sess = _outboundSessions.get(key); removed += sess.expireTags(); - if (sess.availableTags() <= 0) { + // don't kill a new session or one that's temporarily out of tags + if (sess.getLastUsedDate() < now - (SESSION_LIFETIME_MAX_MS / 2) && + sess.availableTags() <= 0) { iter.remove(); - removed++; + removed++; // just to have a non-zero return value? } } } @@ -563,7 +640,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { int size = ts.getTags().size(); total += size; buf.append("
  • Sent: ").append(DataHelper.formatDuration(now - ts.getDate())).append(" ago with "); - buf.append(size).append(" tags remaining
  • "); + buf.append(size).append(" tags remaining; acked? ").append(ts.getAcked()).append(""); } buf.append("
    \n"); } else { buf.append("Peer info for: ").append(hash).append("\n"); - buf.append("[Full entry]
    \n"); + if (full) { + buf.append("[Back]
    \n"); + } else { + buf.append("[Full entry]
    \n"); + } } long age = _context.clock().now() - info.getPublished(); From de07705671da846eaa22f2d7646bb840e075d031 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 2 Sep 2009 15:02:19 +0000 Subject: [PATCH 26/31] deliver more tags as the available number gets low --- .../i2p/crypto/TransientSessionKeyManager.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java index 6f4706e26..931da3ffa 100644 --- a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java +++ b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java @@ -56,7 +56,7 @@ import net.i2p.util.SimpleTimer; * GarlicMessageBuilder must be chosen with streaming lib windows sizes in mind. * If a single TagSet is not delivered, there will be no stall as long as the * current window size is smaller than the minimum tag threshold. - * A second consecutive TagSet delivery failure will cause a complete stall, as + * Additional TagSets will be sent before the acked tags completely run out. See below. * all subsequent messages will fail to decrypt. * See ConnectionOptions in streaming for more information. * @@ -420,7 +420,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { if ( (sessionTags.size() <= 0) && (_log.shouldLog(Log.DEBUG)) ) _log.debug("Received 0 tags for key " + key); - if (false) aggressiveExpire(); + //if (false) aggressiveExpire(); } /** @@ -810,8 +810,14 @@ public class TransientSessionKeyManager extends SessionKeyManager { synchronized (_tagSets) { for (int i = 0; i < _tagSets.size(); i++) { TagSet set = _tagSets.get(i); - if (set.getDate() + SESSION_TAG_DURATION_MS > now) - tags += set.getTags().size(); + if (set.getDate() + SESSION_TAG_DURATION_MS > now) { + int sz = set.getTags().size(); + // so tags are sent when the acked tags are below + // 30, 17, and 4. + if (!set.getAcked()) + sz /= 3; + tags += sz; + } } } return tags; @@ -860,7 +866,7 @@ public class TransientSessionKeyManager extends SessionKeyManager { private SessionKey _key; private long _date; //private Exception _createdBy; - /** only used in renderStatusHTML() for debugging */ + /** did we get an ack for this tagset? */ private boolean _acked; public TagSet(Set tags, SessionKey key, long date) { @@ -918,7 +924,6 @@ public class TransientSessionKeyManager extends SessionKeyManager { //public Exception getCreatedBy() { return _createdBy; } public void setAcked() { _acked = true; } - /** only used in renderStatusHTML() for debugging */ public boolean getAcked() { return _acked; } /****** this will return a dup if two in the same ms, so just use java From 58e960ceb5e46d9b7c78b153ed09248765c4a9a5 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 4 Sep 2009 14:20:20 +0000 Subject: [PATCH 27/31] Fix inbound tunnel tests by using the correct SKM --- .../net/i2p/router/tunnel/pool/TestJob.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/router/java/src/net/i2p/router/tunnel/pool/TestJob.java b/router/java/src/net/i2p/router/tunnel/pool/TestJob.java index 478ea4d6e..ba3eda6d6 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TestJob.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TestJob.java @@ -3,6 +3,7 @@ package net.i2p.router.tunnel.pool; import java.util.HashSet; import java.util.Set; +import net.i2p.crypto.SessionKeyManager; import net.i2p.data.Certificate; import net.i2p.data.SessionKey; import net.i2p.data.SessionTag; @@ -130,12 +131,13 @@ class TestJob extends JobImpl { payload.setExpiration(m.getMessageExpiration()); SessionKey encryptKey = getContext().keyGenerator().generateSessionKey(); - _encryptTag = new SessionTag(true); + SessionTag encryptTag = new SessionTag(true); + _encryptTag = encryptTag; SessionKey sentKey = new SessionKey(); Set sentTags = null; GarlicMessage msg = GarlicMessageBuilder.buildMessage(getContext(), payload, sentKey, sentTags, getContext().keyManager().getPublicKey(), - encryptKey, _encryptTag); + encryptKey, encryptTag); if (msg == null) { // overloaded / unknown peers / etc @@ -143,9 +145,15 @@ class TestJob extends JobImpl { return; } Set encryptTags = new HashSet(1); - encryptTags.add(_encryptTag); - // Register the single tag with the SKM - getContext().sessionKeyManager().tagsReceived(encryptKey, encryptTags); + encryptTags.add(encryptTag); + // Register the single tag with the appropriate SKM + if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) { + SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_pool.getSettings().getDestination()); + if (skm != null) + skm.tagsReceived(encryptKey, encryptTags); + } else { + getContext().sessionKeyManager().tagsReceived(encryptKey, encryptTags); + } if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending garlic test of " + _outTunnel + " / " + _replyTunnel); @@ -312,7 +320,13 @@ class TestJob extends JobImpl { _log.warn("Timeout: found? " + _found, getAddedBy()); if (!_found) { // don't clog up the SKM with old one-tag tagsets - getContext().sessionKeyManager().consumeTag(_encryptTag); + if (_cfg.isInbound() && !_pool.getSettings().isExploratory()) { + SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_pool.getSettings().getDestination()); + if (skm != null) + skm.consumeTag(_encryptTag); + } else { + getContext().sessionKeyManager().consumeTag(_encryptTag); + } testFailed(getContext().clock().now() - _started); } } From 1f1d089fda5b04fdc298409fdd7c8aa0bc529974 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 4 Sep 2009 19:58:15 +0000 Subject: [PATCH 28/31] Move FloodSearchJob to its own file, history for two props, -19 --- history.txt | 25 ++ .../src/net/i2p/router/RouterVersion.java | 2 +- .../networkdb/kademlia/FloodSearchJob.java | 225 ++++++++++++++++++ .../FloodfillNetworkDatabaseFacade.java | 207 ---------------- 4 files changed, 251 insertions(+), 208 deletions(-) create mode 100644 router/java/src/net/i2p/router/networkdb/kademlia/FloodSearchJob.java diff --git a/history.txt b/history.txt index d44b0bac8..048585b51 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,28 @@ +2009-09-04 zzz + * SessionKeyManager, OCMOSJ, Garlic: + - Enable per-client SessionKeyManagers for better anonymity + - tagsDelivered() now means tags are sent, not acked. + - OCMOSJ uses the new TagSetHandle object returned from tagsDelivered() + to call tagsAcked() or failTags() as appropriate. + - Assume tags delivered on an established session to + reduce streaming lib stalls caused by massive tag deliveries; + should increase throughput and window sizes on long-lived streams + - Unacked tagsets on a new session are stored on a separate list + - Don't kill an OB Session just because it's temporarily out of tags + - Increase min tag threshold to 30 (was 20) due to new speculative + tags delivered scheme, and to increase effective max window + - More Java 5 and dead code cleanups, and more comments and javadoc, + debug logging cleanups + - Key toString()s for easier debugging + - HandleGarlicMessageJob: cleanup of unused things + * Tunnel TestJob: + - Consume the tag after a failed test so it doesn't + stay in the SKM + - Disable tests with router.disableTunnelTesting=true + * configkeyring.jsp: Add delete and cancel buttons + * Logging: Fix directory for rotated log + * TunnelDispatcher: Cleanup + 2009-09-02 sponge * Small logic fix for dr|z3d diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 4c8aefc89..40028c0e0 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 18; + public final static long BUILD = 19; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA; diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodSearchJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodSearchJob.java new file mode 100644 index 000000000..810b29c4f --- /dev/null +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodSearchJob.java @@ -0,0 +1,225 @@ +package net.i2p.router.networkdb.kademlia; + +import java.util.ArrayList; +import java.util.List; + +import net.i2p.data.Hash; +import net.i2p.data.i2np.DatabaseLookupMessage; +import net.i2p.data.i2np.DatabaseSearchReplyMessage; +import net.i2p.data.i2np.DatabaseStoreMessage; +import net.i2p.data.i2np.I2NPMessage; +import net.i2p.router.Job; +import net.i2p.router.JobImpl; +import net.i2p.router.MessageSelector; +import net.i2p.router.OutNetMessage; +import net.i2p.router.ReplyJob; +import net.i2p.router.RouterContext; +import net.i2p.router.TunnelInfo; +import net.i2p.util.Log; + +/** + * Try sending a search to some floodfill peers, but if we don't get a successful + * match within half the allowed lookup time, give up and start querying through + * the normal (kademlia) channels. This should cut down on spurious lookups caused + * by simple delays in responses from floodfill peers + * + */ +public class FloodSearchJob extends JobImpl { + private Log _log; + private FloodfillNetworkDatabaseFacade _facade; + private Hash _key; + private final List _onFind; + private final List _onFailed; + private long _expiration; + private int _timeoutMs; + private long _origExpiration; + private boolean _isLease; + private volatile int _lookupsRemaining; + private volatile boolean _dead; + public FloodSearchJob(RouterContext ctx, FloodfillNetworkDatabaseFacade facade, Hash key, Job onFind, Job onFailed, int timeoutMs, boolean isLease) { + super(ctx); + _log = ctx.logManager().getLog(FloodSearchJob.class); + _facade = facade; + _key = key; + _onFind = new ArrayList(); + _onFind.add(onFind); + _onFailed = new ArrayList(); + _onFailed.add(onFailed); + int timeout = -1; + timeout = timeoutMs / FLOOD_SEARCH_TIME_FACTOR; + if (timeout < timeoutMs) + timeout = timeoutMs; + _timeoutMs = timeout; + _expiration = timeout + ctx.clock().now(); + _origExpiration = timeoutMs + ctx.clock().now(); + _isLease = isLease; + _lookupsRemaining = 0; + _dead = false; + } + void addDeferred(Job onFind, Job onFailed, long timeoutMs, boolean isLease) { + if (_dead) { + getContext().jobQueue().addJob(onFailed); + } else { + if (onFind != null) synchronized (_onFind) { _onFind.add(onFind); } + if (onFailed != null) synchronized (_onFailed) { _onFailed.add(onFailed); } + } + } + public long getExpiration() { return _expiration; } + private static final int CONCURRENT_SEARCHES = 2; + private static final int FLOOD_SEARCH_TIME_FACTOR = 2; + private static final int FLOOD_SEARCH_TIME_MIN = 30*1000; + public void runJob() { + // pick some floodfill peers and send out the searches + List floodfillPeers = _facade.getFloodfillPeers(); + FloodLookupSelector replySelector = new FloodLookupSelector(getContext(), this); + ReplyJob onReply = new FloodLookupMatchJob(getContext(), this); + Job onTimeout = new FloodLookupTimeoutJob(getContext(), this); + OutNetMessage out = getContext().messageRegistry().registerPending(replySelector, onReply, onTimeout, _timeoutMs); + + for (int i = 0; _lookupsRemaining < CONCURRENT_SEARCHES && i < floodfillPeers.size(); i++) { + Hash peer = (Hash)floodfillPeers.get(i); + if (peer.equals(getContext().routerHash())) + continue; + + DatabaseLookupMessage dlm = new DatabaseLookupMessage(getContext(), true); + TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundTunnel(); + TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundTunnel(); + if ( (replyTunnel == null) || (outTunnel == null) ) { + _dead = true; + List removed = null; + synchronized (_onFailed) { + removed = new ArrayList(_onFailed); + _onFailed.clear(); + } + while (removed.size() > 0) + getContext().jobQueue().addJob((Job)removed.remove(0)); + getContext().messageRegistry().unregisterPending(out); + return; + } + dlm.setFrom(replyTunnel.getPeer(0)); + dlm.setMessageExpiration(getContext().clock().now()+10*1000); + dlm.setReplyTunnel(replyTunnel.getReceiveTunnelId(0)); + dlm.setSearchKey(_key); + + if (_log.shouldLog(Log.INFO)) + _log.info(getJobId() + ": Floodfill search for " + _key.toBase64() + " to " + peer.toBase64()); + getContext().tunnelDispatcher().dispatchOutbound(dlm, outTunnel.getSendTunnelId(0), peer); + _lookupsRemaining++; + } + + if (_lookupsRemaining <= 0) { + if (_log.shouldLog(Log.INFO)) + _log.info(getJobId() + ": Floodfill search for " + _key.toBase64() + " had no peers to send to"); + // no floodfill peers, go to the normal ones + getContext().messageRegistry().unregisterPending(out); + _facade.searchFull(_key, _onFind, _onFailed, _timeoutMs*FLOOD_SEARCH_TIME_FACTOR, _isLease); + } + } + public String getName() { return "NetDb search (phase 1)"; } + + Hash getKey() { return _key; } + void decrementRemaining() { _lookupsRemaining--; } + int getLookupsRemaining() { return _lookupsRemaining; } + + void failed() { + if (_dead) return; + _dead = true; + int timeRemaining = (int)(_origExpiration - getContext().clock().now()); + if (_log.shouldLog(Log.INFO)) + _log.info(getJobId() + ": Floodfill search for " + _key.toBase64() + " failed with " + timeRemaining); + if (timeRemaining > 0) { + _facade.searchFull(_key, _onFind, _onFailed, timeRemaining, _isLease); + } else { + List removed = null; + synchronized (_onFailed) { + removed = new ArrayList(_onFailed); + _onFailed.clear(); + } + while (removed.size() > 0) + getContext().jobQueue().addJob((Job)removed.remove(0)); + } + } + void success() { + if (_dead) return; + if (_log.shouldLog(Log.INFO)) + _log.info(getJobId() + ": Floodfill search for " + _key.toBase64() + " successful"); + _dead = true; + _facade.complete(_key); + List removed = null; + synchronized (_onFind) { + removed = new ArrayList(_onFind); + _onFind.clear(); + } + while (removed.size() > 0) + getContext().jobQueue().addJob((Job)removed.remove(0)); + } + + private static class FloodLookupTimeoutJob extends JobImpl { + private FloodSearchJob _search; + public FloodLookupTimeoutJob(RouterContext ctx, FloodSearchJob job) { + super(ctx); + _search = job; + } + public void runJob() { + _search.decrementRemaining(); + if (_search.getLookupsRemaining() <= 0) + _search.failed(); + } + public String getName() { return "NetDb search (phase 1) timeout"; } + } + + private static class FloodLookupMatchJob extends JobImpl implements ReplyJob { + private Log _log; + private FloodSearchJob _search; + public FloodLookupMatchJob(RouterContext ctx, FloodSearchJob job) { + super(ctx); + _log = ctx.logManager().getLog(FloodLookupMatchJob.class); + _search = job; + } + public void runJob() { + if ( (getContext().netDb().lookupLeaseSetLocally(_search.getKey()) != null) || + (getContext().netDb().lookupRouterInfoLocally(_search.getKey()) != null) ) { + _search.success(); + } else { + int remaining = _search.getLookupsRemaining(); + if (_log.shouldLog(Log.INFO)) + _log.info(getJobId() + "/" + _search.getJobId() + ": got a reply looking for " + + _search.getKey().toBase64() + ", with " + remaining + " outstanding searches"); + // netDb reply pointing us at other people + if (remaining <= 0) + _search.failed(); + } + } + public String getName() { return "NetDb search (phase 1) match"; } + public void setMessage(I2NPMessage message) {} + } + + private static class FloodLookupSelector implements MessageSelector { + private RouterContext _context; + private FloodSearchJob _search; + public FloodLookupSelector(RouterContext ctx, FloodSearchJob search) { + _context = ctx; + _search = search; + } + public boolean continueMatching() { return _search.getLookupsRemaining() > 0; } + public long getExpiration() { return _search.getExpiration(); } + public boolean isMatch(I2NPMessage message) { + if (message == null) return false; + if (message instanceof DatabaseStoreMessage) { + DatabaseStoreMessage dsm = (DatabaseStoreMessage)message; + // is it worth making sure the reply came in on the right tunnel? + if (_search.getKey().equals(dsm.getKey())) { + _search.decrementRemaining(); + return true; + } + } else if (message instanceof DatabaseSearchReplyMessage) { + DatabaseSearchReplyMessage dsrm = (DatabaseSearchReplyMessage)message; + if (_search.getKey().equals(dsrm.getSearchKey())) { + _search.decrementRemaining(); + return true; + } + } + return false; + } + } +} diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java index 3772b18d1..2b8659c69 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/FloodfillNetworkDatabaseFacade.java @@ -351,210 +351,3 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad } } } - -/** - * Try sending a search to some floodfill peers, but if we don't get a successful - * match within half the allowed lookup time, give up and start querying through - * the normal (kademlia) channels. This should cut down on spurious lookups caused - * by simple delays in responses from floodfill peers - * - */ -class FloodSearchJob extends JobImpl { - private Log _log; - private FloodfillNetworkDatabaseFacade _facade; - private Hash _key; - private final List _onFind; - private final List _onFailed; - private long _expiration; - private int _timeoutMs; - private long _origExpiration; - private boolean _isLease; - private volatile int _lookupsRemaining; - private volatile boolean _dead; - public FloodSearchJob(RouterContext ctx, FloodfillNetworkDatabaseFacade facade, Hash key, Job onFind, Job onFailed, int timeoutMs, boolean isLease) { - super(ctx); - _log = ctx.logManager().getLog(FloodSearchJob.class); - _facade = facade; - _key = key; - _onFind = new ArrayList(); - _onFind.add(onFind); - _onFailed = new ArrayList(); - _onFailed.add(onFailed); - int timeout = -1; - timeout = timeoutMs / FLOOD_SEARCH_TIME_FACTOR; - if (timeout < timeoutMs) - timeout = timeoutMs; - _timeoutMs = timeout; - _expiration = timeout + ctx.clock().now(); - _origExpiration = timeoutMs + ctx.clock().now(); - _isLease = isLease; - _lookupsRemaining = 0; - _dead = false; - } - void addDeferred(Job onFind, Job onFailed, long timeoutMs, boolean isLease) { - if (_dead) { - getContext().jobQueue().addJob(onFailed); - } else { - if (onFind != null) synchronized (_onFind) { _onFind.add(onFind); } - if (onFailed != null) synchronized (_onFailed) { _onFailed.add(onFailed); } - } - } - public long getExpiration() { return _expiration; } - private static final int CONCURRENT_SEARCHES = 2; - private static final int FLOOD_SEARCH_TIME_FACTOR = 2; - private static final int FLOOD_SEARCH_TIME_MIN = 30*1000; - public void runJob() { - // pick some floodfill peers and send out the searches - List floodfillPeers = _facade.getFloodfillPeers(); - FloodLookupSelector replySelector = new FloodLookupSelector(getContext(), this); - ReplyJob onReply = new FloodLookupMatchJob(getContext(), this); - Job onTimeout = new FloodLookupTimeoutJob(getContext(), this); - OutNetMessage out = getContext().messageRegistry().registerPending(replySelector, onReply, onTimeout, _timeoutMs); - - for (int i = 0; _lookupsRemaining < CONCURRENT_SEARCHES && i < floodfillPeers.size(); i++) { - Hash peer = (Hash)floodfillPeers.get(i); - if (peer.equals(getContext().routerHash())) - continue; - - DatabaseLookupMessage dlm = new DatabaseLookupMessage(getContext(), true); - TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundTunnel(); - TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundTunnel(); - if ( (replyTunnel == null) || (outTunnel == null) ) { - _dead = true; - List removed = null; - synchronized (_onFailed) { - removed = new ArrayList(_onFailed); - _onFailed.clear(); - } - while (removed.size() > 0) - getContext().jobQueue().addJob((Job)removed.remove(0)); - getContext().messageRegistry().unregisterPending(out); - return; - } - dlm.setFrom(replyTunnel.getPeer(0)); - dlm.setMessageExpiration(getContext().clock().now()+10*1000); - dlm.setReplyTunnel(replyTunnel.getReceiveTunnelId(0)); - dlm.setSearchKey(_key); - - if (_log.shouldLog(Log.INFO)) - _log.info(getJobId() + ": Floodfill search for " + _key.toBase64() + " to " + peer.toBase64()); - getContext().tunnelDispatcher().dispatchOutbound(dlm, outTunnel.getSendTunnelId(0), peer); - _lookupsRemaining++; - } - - if (_lookupsRemaining <= 0) { - if (_log.shouldLog(Log.INFO)) - _log.info(getJobId() + ": Floodfill search for " + _key.toBase64() + " had no peers to send to"); - // no floodfill peers, go to the normal ones - getContext().messageRegistry().unregisterPending(out); - _facade.searchFull(_key, _onFind, _onFailed, _timeoutMs*FLOOD_SEARCH_TIME_FACTOR, _isLease); - } - } - public String getName() { return "NetDb search (phase 1)"; } - - Hash getKey() { return _key; } - void decrementRemaining() { _lookupsRemaining--; } - int getLookupsRemaining() { return _lookupsRemaining; } - - void failed() { - if (_dead) return; - _dead = true; - int timeRemaining = (int)(_origExpiration - getContext().clock().now()); - if (_log.shouldLog(Log.INFO)) - _log.info(getJobId() + ": Floodfill search for " + _key.toBase64() + " failed with " + timeRemaining); - if (timeRemaining > 0) { - _facade.searchFull(_key, _onFind, _onFailed, timeRemaining, _isLease); - } else { - List removed = null; - synchronized (_onFailed) { - removed = new ArrayList(_onFailed); - _onFailed.clear(); - } - while (removed.size() > 0) - getContext().jobQueue().addJob((Job)removed.remove(0)); - } - } - void success() { - if (_dead) return; - if (_log.shouldLog(Log.INFO)) - _log.info(getJobId() + ": Floodfill search for " + _key.toBase64() + " successful"); - _dead = true; - _facade.complete(_key); - List removed = null; - synchronized (_onFind) { - removed = new ArrayList(_onFind); - _onFind.clear(); - } - while (removed.size() > 0) - getContext().jobQueue().addJob((Job)removed.remove(0)); - } -} - -class FloodLookupTimeoutJob extends JobImpl { - private FloodSearchJob _search; - public FloodLookupTimeoutJob(RouterContext ctx, FloodSearchJob job) { - super(ctx); - _search = job; - } - public void runJob() { - _search.decrementRemaining(); - if (_search.getLookupsRemaining() <= 0) - _search.failed(); - } - public String getName() { return "NetDb search (phase 1) timeout"; } -} - -class FloodLookupMatchJob extends JobImpl implements ReplyJob { - private Log _log; - private FloodSearchJob _search; - public FloodLookupMatchJob(RouterContext ctx, FloodSearchJob job) { - super(ctx); - _log = ctx.logManager().getLog(FloodLookupMatchJob.class); - _search = job; - } - public void runJob() { - if ( (getContext().netDb().lookupLeaseSetLocally(_search.getKey()) != null) || - (getContext().netDb().lookupRouterInfoLocally(_search.getKey()) != null) ) { - _search.success(); - } else { - int remaining = _search.getLookupsRemaining(); - if (_log.shouldLog(Log.INFO)) - _log.info(getJobId() + "/" + _search.getJobId() + ": got a reply looking for " - + _search.getKey().toBase64() + ", with " + remaining + " outstanding searches"); - // netDb reply pointing us at other people - if (remaining <= 0) - _search.failed(); - } - } - public String getName() { return "NetDb search (phase 1) match"; } - public void setMessage(I2NPMessage message) {} -} - -class FloodLookupSelector implements MessageSelector { - private RouterContext _context; - private FloodSearchJob _search; - public FloodLookupSelector(RouterContext ctx, FloodSearchJob search) { - _context = ctx; - _search = search; - } - public boolean continueMatching() { return _search.getLookupsRemaining() > 0; } - public long getExpiration() { return _search.getExpiration(); } - public boolean isMatch(I2NPMessage message) { - if (message == null) return false; - if (message instanceof DatabaseStoreMessage) { - DatabaseStoreMessage dsm = (DatabaseStoreMessage)message; - // is it worth making sure the reply came in on the right tunnel? - if (_search.getKey().equals(dsm.getKey())) { - _search.decrementRemaining(); - return true; - } - } else if (message instanceof DatabaseSearchReplyMessage) { - DatabaseSearchReplyMessage dsrm = (DatabaseSearchReplyMessage)message; - if (_search.getKey().equals(dsrm.getSearchKey())) { - _search.decrementRemaining(); - return true; - } - } - return false; - } -} From 2341793546284c56c22cf5d5ed41aa8b1d1d270c Mon Sep 17 00:00:00 2001 From: BlubMail Date: Sun, 6 Sep 2009 00:43:45 +0000 Subject: [PATCH 29/31] writeString(String str): synchronize using socket write lock to prevent split SAM messages --- apps/sam/java/src/net/i2p/sam/SAMHandler.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/sam/java/src/net/i2p/sam/SAMHandler.java b/apps/sam/java/src/net/i2p/sam/SAMHandler.java index d53a5a662..80d099107 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMHandler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMHandler.java @@ -102,8 +102,8 @@ public abstract class SAMHandler implements Runnable { } static public void writeBytes(ByteBuffer data, SocketChannel out) throws IOException { - while (data.hasRemaining()) out.write(data); - out.socket().getOutputStream().flush(); + while (data.hasRemaining()) out.write(data); + out.socket().getOutputStream().flush(); } /** @@ -124,9 +124,13 @@ public abstract class SAMHandler implements Runnable { * @return True if the string was successfully written, false otherwise */ protected final boolean writeString(String str) { + boolean success; if (_log.shouldLog(Log.DEBUG)) _log.debug("Sending the client: [" + str + "]"); - return writeString(str, socket); + synchronized (socketWLock) { + success = writeString(str, socket); + } + return success; } public static boolean writeString(String str, SocketChannel out) From 1850e893e94ed3c252244d936516b8d0b26e0890 Mon Sep 17 00:00:00 2001 From: mkvore-commit Date: Mon, 7 Sep 2009 17:33:29 +0000 Subject: [PATCH 30/31] removes a bug in SAM v1 and v2 (introduced when merging with v3) --- apps/sam/java/src/net/i2p/sam/SAMHandler.java | 8 +++++--- core/c/jbigi/build.sh | 14 +++++++------- core/c/jcpuid/build.sh | 4 ++-- history.txt | 3 +++ 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/apps/sam/java/src/net/i2p/sam/SAMHandler.java b/apps/sam/java/src/net/i2p/sam/SAMHandler.java index d53a5a662..1bad40f64 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMHandler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMHandler.java @@ -124,9 +124,11 @@ public abstract class SAMHandler implements Runnable { * @return True if the string was successfully written, false otherwise */ protected final boolean writeString(String str) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending the client: [" + str + "]"); - return writeString(str, socket); + synchronized (socketWLock) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Sending the client: [" + str + "]"); + return writeString(str, socket); + } } public static boolean writeString(String str, SocketChannel out) diff --git a/core/c/jbigi/build.sh b/core/c/jbigi/build.sh index 7ef900070..af6dbaa90 100755 --- a/core/c/jbigi/build.sh +++ b/core/c/jbigi/build.sh @@ -15,21 +15,21 @@ mkdir -p lib/ mkdir -p bin/local -VER=4.2.4 +VER=4.3.1 if [ "$1" != "dynamic" -a ! -d gmp-$VER ] then - TAR=gmp-$VER.tar.bz2 + TAR=gmp-$VER.tar.lzma if [ ! -f $TAR ] then - echo "GMP tarball $TAR not found. You must download it from http://gmplib.org/" - exit 1 + echo "Downloading ftp://ftp.gmplib.org/pub/gmp-4.3.1/gmp-4.3.1.tar.lzma" + wget ftp://ftp.gmplib.org/pub/gmp-4.3.1/gmp-4.3.1.tar.lzma fi echo "Building the jbigi library with GMP Version $VER" echo "Extracting GMP..." - tar -xjf gmp-$VER.tar.bz2 + tar -xf gmp-$VER.tar.lzma --lzma fi cd bin/local @@ -42,7 +42,7 @@ then # --with-pic is required for static linking ../../gmp-$VER/configure --with-pic;; *) - ../../gmp-$VER/configure;; + ../../gmp-$VER/configure --with-pic;; esac make sh ../../build_jbigi.sh static @@ -54,7 +54,7 @@ cp *jbigi???* ../../lib/ echo 'Library copied to lib/' cd ../.. -I2P=~/i2p +I2P=~/i2p/i2p if [ ! -f $I2P/lib/i2p.jar ] then echo "I2P installation not found in $I2P - correct \$I2P definition in script to run speed test" diff --git a/core/c/jcpuid/build.sh b/core/c/jcpuid/build.sh index f5c8ea1e1..244eb07b2 100755 --- a/core/c/jcpuid/build.sh +++ b/core/c/jcpuid/build.sh @@ -37,13 +37,13 @@ FreeBSD*) Linux*) COMPILEFLAGS="-fPIC -Wall" INCLUDES="-I. -Iinclude -I$JAVA_HOME/include -I$JAVA_HOME/include/linux" - LINKFLAGS="-shared -static -static-libgcc -Wl,-soname,libjcpuid-x86-linux.so" + LINKFLAGS="-shared -Wl,-soname,libjcpuid-x86-linux.so" LIBFILE="lib/freenet/support/CPUInformation/libjcpuid-x86-linux.so";; esac echo "Compiling C code..." rm -f $LIBFILE -$CC $LINKFLAGS $INCLUDES src/*.c -o $LIBFILE +$CC $COMPILEFLAGS $LINKFLAGS $INCLUDES src/*.c -o $LIBFILE strip $LIBFILE echo Built $LIBFILE diff --git a/history.txt b/history.txt index 048585b51..7731041e5 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,6 @@ +2009-09-07 mkvore + * removes a SAM v1&2 bug + 2009-09-04 zzz * SessionKeyManager, OCMOSJ, Garlic: - Enable per-client SessionKeyManagers for better anonymity From 7e8037979f6a5db16724e2e89e2d3e8536f55fe5 Mon Sep 17 00:00:00 2001 From: sponge Date: Mon, 21 Sep 2009 08:54:41 +0000 Subject: [PATCH 31/31] * fixups to SlackBuilds. requiredbuilder does the wrong thing, and thinks that java is perl! This isn't really a big deal, the file format is simple enough and the requirements are known. --- Slackware/i2p-base/i2p-base.SlackBuild | 8 ++++++-- Slackware/i2p/i2p.SlackBuild | 10 ++++++++-- Slackware/i2p/slack-required | 6 ++++-- history.txt | 5 +++++ router/java/src/net/i2p/router/RouterVersion.java | 2 +- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Slackware/i2p-base/i2p-base.SlackBuild b/Slackware/i2p-base/i2p-base.SlackBuild index d91d87263..7101fb950 100644 --- a/Slackware/i2p-base/i2p-base.SlackBuild +++ b/Slackware/i2p-base/i2p-base.SlackBuild @@ -24,7 +24,7 @@ mkdir -p $PKG # es: usr/local NAME=i2p-base VERSION=0.0.1 -BUILD=1sim +BUILD=1sponge ARCH=noarch INSTALL_DIR=opt cd $PKG @@ -38,5 +38,9 @@ sed "s|directory|/$INSTALL_DIR/i2p/|g" $CWD/doinst.sh > $PKG/install/doinst.sh cat $CWD/slack-desc > $PKG/install/slack-desc cd $PKG -requiredbuilder -v -y -s $CWD $PKG +# +# Not really that important to exec this. +#requiredbuilder -v -y -s $CWD $PKG +# +cat $CWD/slack-required > $PKG/install/slack-required makepkg -l y -c n $CWD/${NAME}-$VERSION-$ARCH-$BUILD.tgz diff --git a/Slackware/i2p/i2p.SlackBuild b/Slackware/i2p/i2p.SlackBuild index f7ced54bf..a8ecf3be2 100755 --- a/Slackware/i2p/i2p.SlackBuild +++ b/Slackware/i2p/i2p.SlackBuild @@ -15,7 +15,7 @@ # It's suggested to subscribe to various dns host, like i2host.i2p # For any additional information, visit i2host.i2p and forum.i2p -BUILD=1sim +BUILD=1sponge # put here installation dir, without first and last / # eg: usr/local @@ -113,5 +113,11 @@ sed "s|directory|/$INSTALL_DIR/i2p/|g" $CWD/doinst.sh > $PKG/install/doinst.sh cat $CWD/slack-desc > $PKG/install/slack-desc cd $PKG -requiredbuilder -v -y -s $CWD $PKG +# +# requiredbuilder fucks up REALLY bad, and thinks java is perl?! +# It also did not catch the shell requirements! BOOOOOOOOOOO! HISSSSSSSS! +# +#requiredbuilder -v -y -s $CWD $PKG +# +cat $CWD/slack-required > $PKG/install/slack-required makepkg -l y -c n $CWD/${NAME}-$VERSION-$ARCH-$BUILD.tgz diff --git a/Slackware/i2p/slack-required b/Slackware/i2p/slack-required index 3dcf36221..d83d818e6 100644 --- a/Slackware/i2p/slack-required +++ b/Slackware/i2p/slack-required @@ -1,2 +1,4 @@ -glibc >= 2.7-i486-17 | glibc-solibs >= 2.7-i486-17 -perl >= 5.10.0-i486-1 +jre >= 5 +i2p-base >= 0.0.1 +bash >= 3.1.017 + diff --git a/history.txt b/history.txt index 7731041e5..724b25709 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,8 @@ +2009-09-21 sponge + * fixups to SlackBuilds. requiredbuilder does the wrong thing, and + thinks that java is perl! This isn't really a big deal, + the file format is simple enough and the requirements are known. + 2009-09-07 mkvore * removes a SAM v1&2 bug diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 40028c0e0..67b9de3fe 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 19; + public final static long BUILD = 20; /** for example "-test" */ public final static String EXTRA = ""; public final static String FULL_VERSION = VERSION + "-" + BUILD + EXTRA;