Selected and modified portions of patch from drz3d:
- Overhaul front-end to use full html output and a default light theme - Enable GeoIP lookups for nodes to permit display of flags - Add country code lookups for nodes and display on tooltip - Mitigate logging output issues caused by html output - Set useMicroDescriptors to FALSE so additional node information can be presented in the UI, if enough memory available - Allow 2nd parameter in extra-info-digest when pulling full descriptors - Display platform, observed bandwidth and uptime on circuit node tooltips - Add hints and notes to config section, and include missing options - Change maxCircuitDirtiness to Tor default of 10 minutes The modifications to the Orchid source code are released under the same license as the parent application. -- dr|z3d - z3d@mail.i2p Thanks to George Hodan for the orchid image http://www.publicdomainpictures.net/view-image.php?image=35307 See LICENSE-FatCowIcons.txt & LICENSE-Fugue-Icons.txt in i2p/licenses/ for icon licences Conversion from new MaxMind format to v.1 GeoIP.dat format courtesy of: https://github.com/sherpya/geolite2legacy Usage: wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz ./geolite2legacy.py -i GeoLite2-Country-CSV.zip -f geoname2fips.csv -o GeoIP.dat For GeoIP licensing information, See the MaxMind license in the I2P application directory: i2p/licenses/LICENSE-GeoIP.txt
12
CHANGES.txt
@ -1,3 +1,15 @@
|
||||
* 2019-08-xx 1.2.2-0.6
|
||||
- Overhaul front-end to use full html output and a default light theme
|
||||
- Enable GeoIP lookups for nodes to permit display of flags
|
||||
- Add country code lookups for nodes and display on tooltip
|
||||
- Mitigate logging output issues caused by html output
|
||||
- Set useMicroDescriptors to FALSE so additional node information can
|
||||
be presented in the UI, if enough memory available
|
||||
- Allow 2nd parameter in extra-info-digest when pulling full descriptors
|
||||
- Display platform, observed bandwidth and uptime on circuit node tooltips
|
||||
- Add hints and notes to config section, and include missing options
|
||||
- Change maxCircuitDirtiness to Tor default of 10 minutes
|
||||
|
||||
* 2019-06-24 1.2.2-0.5
|
||||
- Remove unused XMLRPC Transport and related libs
|
||||
- Reduce max descriptor age
|
||||
|
14
build.xml
@ -13,9 +13,17 @@
|
||||
<buildnumber file="scripts/build.number" />
|
||||
<property name="release.number" value="1.2.2-0.5" />
|
||||
|
||||
<!-- add the GeoIP.dat file & README.txt -->
|
||||
<copy file="geoip/GeoIP.dat" todir="plugin/geoip/" overwrite="true" />
|
||||
<copy file="geoip/README.txt" todir="plugin/geoip/" overwrite="true" />
|
||||
|
||||
<!-- add info about maxmind license -->
|
||||
<copy file="geoip/license.txt" tofile="plugin/licenses/LICENSE-GeoIP.txt" overwrite="true" />
|
||||
|
||||
<!-- make the update xpi2p -->
|
||||
<copy file="LICENSE.txt" todir="plugin/" overwrite="true" />
|
||||
<copy file="README.txt" todir="plugin/" overwrite="true" />
|
||||
<copy file="CHANGES.txt" todir="plugin/" overwrite="true" />
|
||||
<copy file="scripts/plugin.config" todir="plugin/" overwrite="true" />
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
|
||||
<arg value="update-only=true" />
|
||||
@ -61,6 +69,11 @@
|
||||
|
||||
<target name="clean" >
|
||||
<ant dir="src" target="clean" />
|
||||
<defaultexcludes remove="**/*~"/>
|
||||
<delete>
|
||||
<fileset dir="." includes="*/*.~ **/*.*~ *.*~" />
|
||||
</delete>
|
||||
|
||||
<delete file="plugin/i2ptunnel.config" />
|
||||
<delete file="plugin/orchid.config" />
|
||||
<delete file="plugin/plugin.config" />
|
||||
@ -71,6 +84,7 @@
|
||||
<delete file="plugin/console/webapps/orchid.war.pack" />
|
||||
<delete file="plugin/LICENSE.txt" />
|
||||
<delete file="plugin/README.txt" />
|
||||
<delete file="plugin/CHANGES.txt" />
|
||||
<delete file="orchid.xpi2p" />
|
||||
<delete file="orchid-update.xpi2p" />
|
||||
<delete file="orchid.su3" />
|
||||
|
BIN
geoip/GeoIP.dat
Normal file
8
geoip/README.txt
Normal file
@ -0,0 +1,8 @@
|
||||
Conversion from new MaxMind format to v.1 GeoIP.dat format courtesy of:
|
||||
https://github.com/sherpya/geolite2legacy
|
||||
|
||||
Usage:
|
||||
|
||||
wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz
|
||||
./geolite2legacy.py -i GeoLite2-Country-CSV.zip -f geoname2fips.csv -o GeoIP.dat
|
||||
|
2
geoip/license.txt
Normal file
@ -0,0 +1,2 @@
|
||||
For licensing information, See the MaxMind license in the I2P application directory:
|
||||
i2p/licenses/LICENSE-GeoIP.txt
|
11
resources/ajaxRefresh.js
Normal file
@ -0,0 +1,11 @@
|
||||
setInterval(function() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "/orchid/?" + new Date().getTime(), true);
|
||||
xhr.responseType = "text";
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState==4 && xhr.status==200) {
|
||||
document.getElementById("orchid").innerHTML = xhr.responseText;
|
||||
}
|
||||
}
|
||||
xhr.send();
|
||||
}, 15000);
|
13
resources/collapse.css
Normal file
@ -0,0 +1,13 @@
|
||||
#configuration {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#expand {
|
||||
display: inline-block !important;
|
||||
z-index: 100 !important;
|
||||
}
|
||||
|
||||
#collapse {
|
||||
display: none !important;
|
||||
z-index: -1 !important;
|
||||
}
|
13
resources/expand.css
Normal file
@ -0,0 +1,13 @@
|
||||
#configuration {
|
||||
display: table !important;
|
||||
}
|
||||
|
||||
#expand {
|
||||
display: none !important;
|
||||
z-index: -1 !important;
|
||||
}
|
||||
|
||||
#collapse {
|
||||
display: inline-block !important;
|
||||
z-index: 100 !important;
|
||||
}
|
BIN
resources/images/circuits.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
resources/images/collapse.png
Normal file
After Width: | Height: | Size: 275 B |
BIN
resources/images/configure.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/images/cross.png
Normal file
After Width: | Height: | Size: 559 B |
BIN
resources/images/directory.png
Normal file
After Width: | Height: | Size: 826 B |
BIN
resources/images/exit.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
resources/images/expand.png
Normal file
After Width: | Height: | Size: 281 B |
BIN
resources/images/favicon.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
resources/images/globe.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
resources/images/hiddenservice.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/images/infohelp.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
resources/images/internal.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
resources/images/node.png
Normal file
After Width: | Height: | Size: 576 B |
BIN
resources/images/orchid.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
resources/images/refresh.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/images/rx.png
Normal file
After Width: | Height: | Size: 662 B |
BIN
resources/images/starting.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/images/tag.png
Normal file
After Width: | Height: | Size: 813 B |
BIN
resources/images/tag_stream.png
Normal file
After Width: | Height: | Size: 801 B |
BIN
resources/images/target.png
Normal file
After Width: | Height: | Size: 892 B |
BIN
resources/images/tick.png
Normal file
After Width: | Height: | Size: 741 B |
BIN
resources/images/tile2.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/images/tx.png
Normal file
After Width: | Height: | Size: 765 B |
966
resources/orchid.css
Normal file
@ -0,0 +1,966 @@
|
||||
/* I2P Orchid Plugin Theme by dr|z3d 2019 */
|
||||
/* Thanks to George Hodan for the orchid image */
|
||||
/* http://www.publicdomainpictures.net/view-image.php?image=35307 */
|
||||
/* See LICENSE-FatCowIcons.txt & LICENSE-Fugue-Icons.txt in i2p/licenses/ for icon licences */
|
||||
|
||||
body {
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
min-width: 860px;
|
||||
color: #41465f;
|
||||
background: url(images/tile2.png) fixed #a4a4cb;
|
||||
}
|
||||
|
||||
body, table {
|
||||
font-family: "Droid Sans", "Noto Sans", Ubuntu, "Segoe UI", Verdana, "Helvetica Neue", sans-serif;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
html {
|
||||
scrollbar-color: rgba(16,16,48,.3) rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
html:hover {
|
||||
scrollbar-color: rgba(16,16,48,.4) rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: #77f;
|
||||
color: #fff;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
hr, .hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a:link, .node a:visited, .nickname {
|
||||
color: #3b6bbf;
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #2c4e8f;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover, a:focus {
|
||||
color: #f60;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #f30;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin: 15px 5px;
|
||||
padding: 8px;
|
||||
max-width: 1920px;
|
||||
border: 1px solid #447;
|
||||
box-shadow: inset 0 0 0 1px #bbf;
|
||||
background: #fff;
|
||||
background: linear-gradient(to right, #fff, #efefff, #fff);
|
||||
/* background: repeating-linear-gradient(45deg, rgba(255,255,255,.5) 1px, rgba(221, 221, 255, .1) 2px, rgba(255,255,255,.1) 2px), repeating-linear-gradient(135deg, rgba(255,255,255,1), rgba(221, 221, 255, .3) 1px, #fff 2px) #fafaff !important;
|
||||
background-blend-mode: multiply, normal !important;*/
|
||||
filter: drop-shadow(0 1px 1px #97a2ce);
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 0 2px;
|
||||
font-family: "Droid Sans Mono", "Noto Mono", "DejaVu Sans Mono", "Lucida Console", monospace;
|
||||
color: #373;
|
||||
}
|
||||
|
||||
.notice code {
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
background: #99a;
|
||||
-moz-user-select: all;
|
||||
-webkit-user-select: all;
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
#refresh {
|
||||
margin: -1px -2px 0 0;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#refresh a, #refresh a:visited {
|
||||
margin: -2px 0 !important;
|
||||
padding: 5px 5px 5px 20px;
|
||||
display: inline-block;
|
||||
line-height: 100%;
|
||||
letter-spacing: normal;
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
text-transform: none;
|
||||
font-size: 9.5pt;
|
||||
color: #41465f !important;
|
||||
border: 1px solid #97a2ce;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 0 0 1px #fff;
|
||||
background: #fff url(images/refresh.png) 4px center no-repeat;
|
||||
background: url(images/refresh.png) 4px center no-repeat, linear-gradient(to bottom, #fff, #eef);
|
||||
background-size: 14px 14px, 100% 100% !important;
|
||||
}
|
||||
|
||||
#refresh a:hover, #refresh a:focus {
|
||||
border: 1px solid #f60;
|
||||
background: #eee url(images/refresh.png) 4px center no-repeat;
|
||||
background: url(images/refresh.png) 4px center no-repeat, linear-gradient(to bottom, #ddd, #fff);
|
||||
filter: drop-shadow(0 0 1px #89f);
|
||||
}
|
||||
|
||||
#refresh a:active {
|
||||
box-shadow: inset 3px 3px 3px #999;
|
||||
box-shadow: inset 0 0 0 1px #fff, inset 3px 3px 3px #999;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #7778bf;
|
||||
background: #f8f8ff;
|
||||
}
|
||||
|
||||
tr {
|
||||
border: 1px solid #7778bf;
|
||||
}
|
||||
|
||||
th#circuitsatus, #configtitle {
|
||||
font-size: 13pt;
|
||||
}
|
||||
|
||||
tr.subtitle th {
|
||||
padding: 6px 8px;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
font-size: 10.5pt;
|
||||
background: #f6f6ff;
|
||||
background: linear-gradient(to bottom, #fdfdff, #f0f0ff);
|
||||
}
|
||||
|
||||
#status th, #status td {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#status th, #status td {
|
||||
width: 25% !important;
|
||||
}
|
||||
|
||||
#status td {
|
||||
padding: 9px 8px;
|
||||
border: 1px solid #7778bf;
|
||||
box-shadow: inset 0 0 0 1px #fff;
|
||||
background: #f2f2ff;
|
||||
}
|
||||
|
||||
#status td:first-child {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
#status td:first-child::first-letter {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
#status td::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#starting, #running, #registered, #notregistered {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
#starting::before, #running::before, #registered::before, #notregistered::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
background: url(images/starting.png) center center no-repeat;
|
||||
background-size: 16px 16px;
|
||||
animation: spin linear 3s forwards infinite;
|
||||
}
|
||||
|
||||
#running::before, #registered::before {
|
||||
background: url(images/tick.png) center center no-repeat;
|
||||
background-size: 16px 16px;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
#notregistered::before {
|
||||
background: url(images/cross.png) center center no-repeat;
|
||||
background-size: 16px 16px;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0)
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg)
|
||||
}
|
||||
}
|
||||
|
||||
table table {
|
||||
box-shadow: 0 0 1px 0 #bbc;
|
||||
}
|
||||
|
||||
table table tr:not(.stream):nth-child(odd), #configuration tr:not(.stream):nth-child(odd) {
|
||||
background: #f2f2ff;
|
||||
background: repeating-linear-gradient(45deg, rgba(255,255,255,.5) 2px, rgba(220, 220, 255, .3) 3px, #fafaff 5px), #fafaff;
|
||||
}
|
||||
|
||||
table table tr:not(.stream):nth-child(even), #configuration tr:not(.stream):nth-child(even) {
|
||||
background: #ededff;
|
||||
background: repeating-linear-gradient(135deg, rgba(252,252,255,.5) 2px, rgba(240, 240, 255, .3) 3px, #fafaff 5px) #f0f0ff;
|
||||
}
|
||||
|
||||
table table:not(#status) tr:nth-child(n+3):hover, #configuration tr:nth-child(n+3):hover td, #status td:hover {
|
||||
color: #292d3d;
|
||||
background-color: #ffe;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
table table td::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
min-height: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#ports td::before {
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
table table td, #configuration table td {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
td.notice, tr:hover td.notice {
|
||||
padding: 15px 8px 15px 38px !important;
|
||||
color: #222;
|
||||
box-shadow: inset 0 0 0 1px #fff, inset 0 0 1px 1px #bbf;
|
||||
background: #f8f8ff url(images/infohelp.png) 8px center no-repeat !important;
|
||||
background-size: 24px 24px !important;
|
||||
}
|
||||
|
||||
th, #configtitle {
|
||||
padding: 6px 8px;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
text-align: left;
|
||||
font-size: 11pt;
|
||||
background: #fff;
|
||||
background: linear-gradient(to bottom, #fcfcff 50%, #f2f2ff 50%, #efefff);
|
||||
}
|
||||
|
||||
th[colspan="2"] {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#circuitstatus, #configtitle {
|
||||
font-size: 12.5pt;
|
||||
}
|
||||
|
||||
th#title {
|
||||
padding: 10px 8px;
|
||||
font-size: 16pt;
|
||||
letter-spacing: 0.08em !important;
|
||||
line-height: 110%;
|
||||
text-shadow: 0 1px 1px #fff,0 -1px 1px #e2e2ff, 0 2px 1px #ddf;
|
||||
background: url(images/orchid.png) right top no-repeat, linear-gradient(to bottom, #fcfcff 50%, #f2f2ff 50%, #efefff);
|
||||
background-size: auto 120%, 100% 100%;
|
||||
}
|
||||
|
||||
#circuitstatus {
|
||||
background: url(images/circuits.png) 8px center no-repeat, linear-gradient(to bottom, #fcfcff 50%, #f2f2ff 50%, #efefff);
|
||||
padding: 8px 8px 8px 37px;
|
||||
background-size: 24px 24px, 100% 100%;
|
||||
}
|
||||
|
||||
#circuitstatus, #configtitle, #circuitmonitor tr:first-child, #circuitstatus tr:first-child, #conncache tr:first-child, #ports tr:first-child {
|
||||
text-shadow: 0 1px 2px #fff, 0 1px 1px #ccf;
|
||||
}
|
||||
|
||||
#circuitmon tr:nth-child(2) th:nth-child(n+3), #circuitmon td:nth-child(n+3),
|
||||
#conncache th:nth-child(n+2), #conncache td:nth-child(n+2) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#circuitmon td:nth-child(2), #circuitmon td:nth-child(3) {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
#circuitmon td:nth-child(3) .nowrap {
|
||||
margin-left: 28px;
|
||||
min-width: 80px;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#circuitmon .circuit td:nth-child(3) i {
|
||||
padding: 1px 8px;
|
||||
display: inline-block;
|
||||
min-width: 120px;
|
||||
text-align: center;
|
||||
border: 1px solid #ddf;
|
||||
box-shadow: inset 0 0 0 1px #fff;
|
||||
background: repeating-linear-gradient(135deg, rgba(238, 238, 255,.7) 1px, rgba(238, 238, 255, .7) 5px, rgba(221, 221, 255, .7) 6px, rgba(221, 221, 255, .7) 11px);
|
||||
background-size: 800% 800%;
|
||||
animation: progress 120s linear infinite both;
|
||||
}
|
||||
|
||||
.stream .target {
|
||||
margin: 0 1px;
|
||||
}
|
||||
|
||||
#circuitmon td:last-child, .stream td:nth-child(2) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#conncache tr:first-child th, #circuitstatus tr:first-child th, #ports tr:first-child th, #circuitmon tr:first-child th {
|
||||
font-size: 11.5pt;
|
||||
}
|
||||
|
||||
#conncache .nowrap, #ports .nowrap {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#conncache .nowrap {
|
||||
min-width: 30px;
|
||||
}
|
||||
|
||||
#conncache td:first-child {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#conncache td:nth-child(2) .nowrap {
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
#ports .nowrap, #conncache td:nth-child(n+4) .nowrap {
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
#ports .subtitle th:last-child, #ports td:last-child {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#ports td:last-child .nowrap {
|
||||
min-width: 120px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.circuitcount {
|
||||
margin-left: 6px;
|
||||
padding: 3px 4px 2px;
|
||||
min-width: 24px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 1px #fff;
|
||||
border-radius: 2px;
|
||||
background: #dfdfff;
|
||||
}
|
||||
|
||||
.circuitcontainer {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.nodecontainer {
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.node {
|
||||
margin: 1px 0;
|
||||
padding: 2px 8px 2px 0;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
border: 1px solid #bbf;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 0 0 1px #fff, 0 0 0 1px #eef;
|
||||
background: linear-gradient(to bottom, #f8f8ff, #e2e2ff);
|
||||
-moz-user-select: all;
|
||||
-webkit-user-select: all;
|
||||
user-select: all;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.node .hidden {
|
||||
font-size: 0;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.node:hover {
|
||||
border-right: 10px solid #bbf;
|
||||
}
|
||||
|
||||
.node:active {
|
||||
border-color: #f30;
|
||||
box-shadow: none;
|
||||
cursor: copy;
|
||||
}
|
||||
|
||||
.nickname a::selection {
|
||||
background: rgba(0,0,0,0) !important;
|
||||
color: #3b6bbf !important;
|
||||
}
|
||||
|
||||
.node:active .nickname a::selection {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.node:active .nickname {
|
||||
background-color: #f30;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.node:active .flag {
|
||||
border-color: #f30;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
a:hover .nickname::after, .flag::after {
|
||||
color: #41465f;
|
||||
}
|
||||
|
||||
a:visited:hover .nickname, a:visited:focus .nickname {
|
||||
color: #f60 !important;
|
||||
}
|
||||
|
||||
a:visited:active .nickname {
|
||||
color: #f30 !important;
|
||||
}
|
||||
|
||||
.node img {
|
||||
vertical-align: middle;
|
||||
margin: -1px 4px 0 0;
|
||||
}
|
||||
|
||||
.node b {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.flag {
|
||||
margin-right: 4px;
|
||||
margin: -3px 4px -3px 0;
|
||||
padding: 2px 1px 3px 5px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
border-right: 1px solid #bbf;
|
||||
box-shadow: inset 0 0 0 1px #fff;
|
||||
cursor: help;
|
||||
-mozilla-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.flag img {
|
||||
padding-bottom: 1px
|
||||
}
|
||||
|
||||
.nickname::after, .flag::after {
|
||||
content: attr(data-ipv4);
|
||||
padding: 2px 3px 2px 15px;
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
top: -20px;
|
||||
left: -10px;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 1px #fff;
|
||||
border: 1px solid #373;
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 0 0 1px #fff, 0 0 1px 1px rgba(192,192,192,.3);
|
||||
background: url(images/node.png) left -1px center no-repeat, #ada;
|
||||
background: url(images/node.png) left center no-repeat, linear-gradient(to bottom, #efe, #ded);
|
||||
background-size: 16px 16px, 100% 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.nickname::after {
|
||||
padding-left: 17px;
|
||||
color: #41465f;
|
||||
}
|
||||
|
||||
.nickname.unknown::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#circuitmon .nickname::after {
|
||||
left: -70px;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.flag::after {
|
||||
content: attr(data-country);
|
||||
padding-left: 18px;
|
||||
z-index: 101;
|
||||
background: url(images/globe.png) left 4px center no-repeat, #ada;
|
||||
background: url(images/globe.png) left 4px center no-repeat, linear-gradient(to bottom, #efe, #ded);
|
||||
background-size: 12px 12px, 100% 100% !important;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
margin: -2px -8px -2px -4px;
|
||||
padding: 2px 6px 3px 5px;
|
||||
display: inline-block;
|
||||
box-shadow: inset 0 0 0 1px #fff;
|
||||
width: 100px;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.node:hover .nickname {
|
||||
width: auto;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
#conncache .nickname {
|
||||
margin-right: -8px;
|
||||
}
|
||||
|
||||
.nickname:hover::after, .flag:hover::after {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#circuitmon .nodecontainer::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: calc(50%);
|
||||
right: -4px !important;
|
||||
height: 1px;
|
||||
width: 4px;
|
||||
vertical-align: middle;
|
||||
background: #bbf;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#circuitmon .nodecontainer:last-of-type::after {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.stream {
|
||||
background-color: #f8fff8;
|
||||
}
|
||||
|
||||
.stream td:first-child, .stream:hover td:first-child, .circuit td:first-child, .circuit:hover td:first-child {
|
||||
padding-left: 26px !important;
|
||||
background-image: url(images/tag_stream.png) !important;
|
||||
background-size: 14px 14px !important;
|
||||
background-position: 8px center !important;
|
||||
background-repeat: no-repeat !important;
|
||||
}
|
||||
|
||||
.circuit td:first-child, .circuit:hover td:first-child {
|
||||
background-image: url(images/tag.png) !important;
|
||||
}
|
||||
|
||||
.stream td:last-child {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stream td:last-child i {
|
||||
margin-left: 1px;
|
||||
padding: 1px 16px;
|
||||
min-width: 106px;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
color: #4a4;
|
||||
text-shadow: 0 1px 1px #fff;
|
||||
border: 1px solid #aca;
|
||||
box-shadow: inset 0 0 0 1px #fff;
|
||||
background: repeating-linear-gradient(135deg, rgba(240, 255, 240, .7) 1px, rgba(240, 255, 240, .7) 6px, rgba(210, 240, 210,.7) 7px, rgba(210, 240, 210,.7) 11px);
|
||||
background-size: 800% 800%;
|
||||
animation: progress 120s linear infinite both;
|
||||
}
|
||||
|
||||
.stream:hover td:last-child i {
|
||||
color: #bb6;
|
||||
border: 1px solid #cca;
|
||||
background: repeating-linear-gradient(135deg, rgba(255, 255, 240, .7) 1px, rgba(255, 255, 240, .7) 6px, rgba(240, 240, 220,.7) 7px, rgba(240, 240, 220,.7) 11px);
|
||||
background-size: 800% 800%;
|
||||
animation: progress 120s linear infinite both;
|
||||
}
|
||||
|
||||
@keyframes progress {
|
||||
from {
|
||||
background-position: 200%
|
||||
}
|
||||
to {
|
||||
background-position: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.streamID {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nowrap {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.stream .nowrap {
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stream .nowrap:first-of-type {
|
||||
padding-right: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.stream .nowrap:last-of-type {
|
||||
padding-left: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.internal, .exit, .directory, .hiddenservice {
|
||||
padding-left: 20px;
|
||||
display: inline-block;
|
||||
min-width: 120px;
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
background: url(images/internal.png) left center no-repeat;
|
||||
background-size: 16px 16px !important;
|
||||
}
|
||||
|
||||
.exit {
|
||||
background: url(images/exit.png) left center no-repeat;
|
||||
}
|
||||
|
||||
.directory {
|
||||
background: url(images/directory.png) left center no-repeat;
|
||||
}
|
||||
|
||||
.hiddenservice {
|
||||
background: url(images/hiddenservice.png) left center no-repeat;
|
||||
}
|
||||
|
||||
.rx::before, .tx::before, .target::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 16px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.tx::before {
|
||||
background: url(images/tx.png) left center no-repeat;
|
||||
background-size: 14px 14px;
|
||||
}
|
||||
|
||||
.rx::before {
|
||||
background: url(images/rx.png) left center no-repeat;
|
||||
background-size: 14px 14px;
|
||||
}
|
||||
|
||||
.target::before {
|
||||
margin-top: -6px;
|
||||
width: 15px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
background: url(images/target.png) left top no-repeat;
|
||||
background-size: 14px 14px;
|
||||
}
|
||||
|
||||
.rx, .tx, .target {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
#configsection, #configsection td {
|
||||
border: none;
|
||||
}
|
||||
|
||||
#configsection td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#configsection table td {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
#configuration {
|
||||
position: relative;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#configuration tr {
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
#configuration td:first-child {
|
||||
border-left: none !important;
|
||||
}
|
||||
|
||||
#configuration td:last-child {
|
||||
border-right: none !important;
|
||||
}
|
||||
|
||||
#configuration tr:last-child, #configuration tr:last-child td {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
#configuration th {
|
||||
width: 25%;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
}
|
||||
|
||||
#configuration tr:nth-child(n+3) td:first-child {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#configuration tr:hover td {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#configuration td:first-child {
|
||||
border-left: none !important;
|
||||
}
|
||||
|
||||
#configuration td:last-child {
|
||||
border-right: none !important;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
#configtitle {
|
||||
margin: -1px 0;
|
||||
padding: 8px;
|
||||
padding-left: 37px;
|
||||
width: calc(100% - 47px);
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
font-size: 12.5pt;
|
||||
font-weight: bold;
|
||||
border: 1px solid #7778bf;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
background: url(images/configure.png) 8px center no-repeat, linear-gradient(to bottom, #fcfcff 50%, #f2f2ff 50%, #efefff);
|
||||
background-size: 24px 24px, 100% 100% !important;
|
||||
}
|
||||
|
||||
#expand, #collapse {
|
||||
display: inline-block !important;
|
||||
position: absolute;
|
||||
top: calc(50% - 18px);
|
||||
right: -1px;
|
||||
font-size: 0;
|
||||
border-left: 1px solid #7778bf;
|
||||
}
|
||||
|
||||
#collapse {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#expand img, #collapse img {
|
||||
padding: 13px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#expand:hover img, #collapse:hover img {
|
||||
filter: drop-shadow(0 0 1px #f60);
|
||||
}
|
||||
|
||||
#configuration td:nth-child(2) code {
|
||||
color: #050;
|
||||
line-height: 115%;
|
||||
}
|
||||
|
||||
#configuration td:nth-child(2) code, #configuration td:nth-child(2) .nowrap {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#configuration tr:hover td:nth-child(2) code {
|
||||
color: #070;
|
||||
}
|
||||
|
||||
#configuration tr:hover td:nth-child(2) code::selection {
|
||||
color: #fff;
|
||||
background: #070;
|
||||
|
||||
}
|
||||
|
||||
#configuration .nowrap {
|
||||
font-style: italic;
|
||||
color: #41465f;
|
||||
}
|
||||
|
||||
#configuration i {
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.stream, .circuit {
|
||||
border-top: 1px solid #ddf;
|
||||
border-bottom: 1px solid #ddf;
|
||||
border-left: 1px solid #7778bf;
|
||||
border-right: 1px solid #7778bf;
|
||||
}
|
||||
|
||||
.circuit, .stream, #conncache tr, #ports tr, #configuration tr {
|
||||
border-top: 1px solid #ddf;
|
||||
border-bottom: 1px solid #ddf;
|
||||
}
|
||||
|
||||
tr:first-child td, tr:last-child, #configuration tr:last-child, #conncache tr:last-child, #ports tr:last-child {
|
||||
border-bottom: 1px solid #7778bf;
|
||||
}
|
||||
|
||||
#configuration tr:first-child, #configuration tr:nth-child(2),
|
||||
#conncache tr:first-child, #conncache tr:nth-child(2),
|
||||
#ports tr:first-child, #ports tr:nth-child(2) {
|
||||
border-top: 1px solid #7778bf;
|
||||
border-bottom: 1px solid #7778bf;
|
||||
}
|
||||
|
||||
#conncache tr:nth-child(2), #conncache tr:nth-child(2) th {
|
||||
border-bottom: 1px solid #7778bf !important;
|
||||
}
|
||||
|
||||
#conncache tr:nth-child(3), #conncache tr:nth-child(3) td {
|
||||
border-top: 1px solid #7778bf !important;
|
||||
}
|
||||
|
||||
td, #configuration td {
|
||||
border: 1px inset #dde;
|
||||
border-left-style: solid;
|
||||
border-right-style: solid;
|
||||
box-shadow: inset 0 0 0 1px #fff !important;
|
||||
}
|
||||
|
||||
.stream td, .stream:hover td, .circuit:hover td, #conncache tr:nth-child(n+3):hover td, #ports tr:nth-child(n+3):hover td, #configuration tr:nth-child(n+3):hover td {
|
||||
border: none;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
border-left: 1px solid #7778bf !important;
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
border-right: 1px solid #7778bf !important;
|
||||
}
|
||||
|
||||
.stream td:first-child {
|
||||
border-right: 1px solid #f8fff8 !important;
|
||||
}
|
||||
|
||||
.stream td:last-child {
|
||||
border-left: 1px solid #f8fff8 !important;
|
||||
}
|
||||
|
||||
.stream:hover td:first-child {
|
||||
border-right: 1px solid #ffe !important;
|
||||
}
|
||||
|
||||
.stream:hover td:last-child {
|
||||
border-left: 1px solid #ffe !important;
|
||||
}
|
||||
|
||||
table table tr:hover td {
|
||||
background-color: #ffe;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1480px) {
|
||||
#circuitmon th, #circuitmon td {
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
#circuitmon tr:nth-child(2) th:last-child {
|
||||
width: 60% !important;
|
||||
}
|
||||
|
||||
#circuitmon th, #conncache th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1000px) {
|
||||
body {
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
#container {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body, table {
|
||||
font-size: 9.5pt !important;
|
||||
}
|
||||
|
||||
#title {
|
||||
font-size: 14pt !important;
|
||||
}
|
||||
|
||||
th#circuitstatus, #configtitle {
|
||||
font-size: 12pt !important;
|
||||
}
|
||||
|
||||
#conncache tr:first-child th, #circuitstatus tr:first-child th, #ports tr:first-child th, #circuitmon tr:first-child th {
|
||||
font-size: 10.5pt;
|
||||
}
|
||||
|
||||
tr.subtitle th {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.flag, .nickname, .circuitcount {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.nickname {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.flag img {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.circuitcount {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
#refresh {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.stream td:last-child i {
|
||||
min-width: 96px;
|
||||
}
|
||||
|
||||
#circuitmon .circuit td:nth-child(3) i {
|
||||
min-width: 86px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1480px) {
|
||||
#circuitmon th:first-child, #circuitmon th:nth-child(3) {
|
||||
width: 12.5%;
|
||||
}
|
||||
}
|
48
resources/toggleConfig.js
Normal file
@ -0,0 +1,48 @@
|
||||
var expandConfig = document.getElementById("expandConfig");
|
||||
var collapseConfig = document.getElementById("collapseConfig");
|
||||
var config = document.getElementById("configuration");
|
||||
|
||||
function hideConfig() {
|
||||
if (!collapseConfig)
|
||||
config.style.display = "none";
|
||||
}
|
||||
|
||||
function showConfig() {
|
||||
if (collapseConfig)
|
||||
config.style.display = "block";
|
||||
}
|
||||
|
||||
function clean() {
|
||||
if (expandConfig) {
|
||||
expandConfig.remove();
|
||||
}
|
||||
if (collapseConfig) {
|
||||
collapseConfig.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function expand() {
|
||||
clean();
|
||||
var x = document.createElement("link");
|
||||
x.type="text/css";
|
||||
x.rel="stylesheet";
|
||||
x.href="/orchid/resources/expand.css";
|
||||
x.setAttribute("id", "expandConfig");
|
||||
document.head.appendChild(x);
|
||||
showConfig();
|
||||
}
|
||||
|
||||
function collapse() {
|
||||
clean();
|
||||
var c = document.createElement("link");
|
||||
c.type="text/css";
|
||||
c.rel="stylesheet";
|
||||
c.href="/orchid/resources/collapse.css";
|
||||
c.setAttribute("id", "collapseConfig");
|
||||
document.head.appendChild(c);
|
||||
hideConfig();
|
||||
}
|
||||
|
||||
function copyText() {
|
||||
document.execCommand("copy");
|
||||
}
|
@ -78,9 +78,13 @@
|
||||
</target>
|
||||
|
||||
<target name="war" depends="precompilejsp">
|
||||
<!-- add css and resources -->
|
||||
<copy todir="build/war/resources" overwrite="true" >
|
||||
<fileset dir="../resources" />
|
||||
</copy>
|
||||
<war destfile="build/orchid.war.jar" webxml="build/web.xml">
|
||||
<classes dir="./build/obj" includes="**/web/*" />
|
||||
<fileset dir="build/war" />
|
||||
<fileset dir="./build/war" />
|
||||
</war>
|
||||
</target>
|
||||
|
||||
|
@ -11,6 +11,9 @@ public interface Router {
|
||||
String getNickname();
|
||||
String getCountryCode();
|
||||
IPv4Address getAddress();
|
||||
String getPlatform();
|
||||
String getCountryName();
|
||||
int getUptime();
|
||||
int getOnionPort();
|
||||
int getDirectoryPort();
|
||||
TorPublicKey getIdentityKey();
|
||||
|
@ -36,7 +36,7 @@ public class Tor {
|
||||
|
||||
|
||||
private final static String implementation = "Orchid";
|
||||
private final static String version = "1.0.0";
|
||||
private final static String version = "1.2.2";
|
||||
|
||||
private final static Charset defaultCharset = createDefaultCharset();
|
||||
|
||||
|
@ -73,9 +73,9 @@ public class TorClient {
|
||||
return;
|
||||
}
|
||||
if(isStopped) {
|
||||
throw new IllegalStateException("Cannot restart a TorClient instance. Create a new instance instead.");
|
||||
throw new IllegalStateException("Cannot restart Orchid instance. Create a new instance instead.");
|
||||
}
|
||||
logger.info("Starting Orchid (version: "+ Tor.getFullVersion() +")");
|
||||
logger.info("Starting Orchid... [version: " + Tor.getFullVersion() + "]");
|
||||
verifyUnlimitedStrengthPolicyInstalled();
|
||||
directoryDownloader.start(directory);
|
||||
circuitManager.startBuildingCircuits();
|
||||
@ -99,7 +99,7 @@ public class TorClient {
|
||||
directory.close();
|
||||
connectionCache.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Unexpected exception while shutting down TorClient instance: "+ e, e);
|
||||
logger.log(Level.WARNING, "Unexpected exception while shutting down Orchid instance: " + e, e);
|
||||
} finally {
|
||||
isStopped = true;
|
||||
}
|
||||
@ -208,7 +208,7 @@ public class TorClient {
|
||||
throw new TorException(message);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
logger.log(Level.SEVERE, "No AES provider found");
|
||||
logger.log(Level.SEVERE, "WARNING! No AES provider found");
|
||||
throw new TorException(e);
|
||||
} catch (NoSuchMethodError e) {
|
||||
logger.info("Skipped check for Unlimited Strength Jurisdiction Policy Files");
|
||||
|
@ -42,7 +42,7 @@ public class CircuitBuildTask implements Runnable {
|
||||
circuit.notifyCircuitBuildStart();
|
||||
creationRequest.choosePath();
|
||||
if(logger.isLoggable(Level.FINE)) {
|
||||
logger.fine("Opening a new circuit to "+ pathToString(creationRequest));
|
||||
logger.fine("Selecting path for new circuit " + pathToString(creationRequest));
|
||||
}
|
||||
firstRouter = creationRequest.getPathElement(0);
|
||||
openEntryNodeConnection(firstRouter);
|
||||
@ -72,7 +72,7 @@ public class CircuitBuildTask implements Runnable {
|
||||
sb.append("[");
|
||||
for(Router r: ccr.getPath()) {
|
||||
if(sb.length() > 1)
|
||||
sb.append(",");
|
||||
sb.append(" -> ");
|
||||
sb.append(r.getNickname());
|
||||
}
|
||||
sb.append("]");
|
||||
|
@ -26,8 +26,10 @@ import com.subgraph.orchid.data.exitpolicy.ExitTarget;
|
||||
|
||||
public class CircuitCreationTask implements Runnable {
|
||||
private final static Logger logger = Logger.getLogger(CircuitCreationTask.class.getName());
|
||||
private final static int MAX_CIRCUIT_DIRTINESS = 300; // seconds
|
||||
private final static int MAX_PENDING_CIRCUITS = 4;
|
||||
// private final static int MAX_CIRCUIT_DIRTINESS = 300; // seconds
|
||||
private final static int MAX_CIRCUIT_DIRTINESS = 600; // 10 minutes (Tor default)
|
||||
// private final static int MAX_PENDING_CIRCUITS = 4;
|
||||
private final static int MAX_PENDING_CIRCUITS = 32; // Tor default
|
||||
|
||||
private final TorConfig config;
|
||||
private final Directory directory;
|
||||
@ -112,7 +114,7 @@ public class CircuitCreationTask implements Runnable {
|
||||
}
|
||||
});
|
||||
for(Circuit c: circuits) {
|
||||
logger.fine("Closing idle dirty circuit: "+ c);
|
||||
logger.fine("Closing idle dirty circuit \n* CircuitID: " + c);
|
||||
((CircuitImpl)c).markForClose();
|
||||
}
|
||||
}
|
||||
@ -124,7 +126,7 @@ public class CircuitCreationTask implements Runnable {
|
||||
|
||||
if(!directory.haveMinimumRouterInfo()) {
|
||||
if(notEnoughDirectoryInformationWarningCounter % 20 == 0)
|
||||
logger.info("Cannot build circuits because we don't have enough directory information");
|
||||
logger.info("Not enough directory information to build circuits");
|
||||
notEnoughDirectoryInformationWarningCounter++;
|
||||
return;
|
||||
}
|
||||
@ -228,7 +230,7 @@ public class CircuitCreationTask implements Runnable {
|
||||
return new CircuitBuildHandler() {
|
||||
|
||||
public void circuitBuildCompleted(Circuit circuit) {
|
||||
logger.fine("Circuit completed to: "+ circuit);
|
||||
logger.fine("Built new circuit \n* CircuitID: " + circuit);
|
||||
circuitOpenedHandler(circuit);
|
||||
lastNewCircuit.set(System.currentTimeMillis());
|
||||
}
|
||||
@ -270,7 +272,7 @@ public class CircuitCreationTask implements Runnable {
|
||||
return new CircuitBuildHandler() {
|
||||
|
||||
public void nodeAdded(CircuitNode node) {
|
||||
logger.finer("Node added to internal circuit: "+ node);
|
||||
logger.finer("Added " + node + " to internal circuit");
|
||||
}
|
||||
|
||||
public void connectionFailed(String reason) {
|
||||
@ -288,7 +290,7 @@ public class CircuitCreationTask implements Runnable {
|
||||
}
|
||||
|
||||
public void circuitBuildCompleted(Circuit circuit) {
|
||||
logger.fine("Internal circuit build completed: "+ circuit);
|
||||
logger.fine("Built new circuit \n* CircuitID: " + circuit);
|
||||
lastNewCircuit.set(System.currentTimeMillis());
|
||||
circuitManager.addCleanInternalCircuit((InternalCircuit) circuit);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ public class CircuitExtender {
|
||||
|
||||
|
||||
CircuitNode createFastTo(Router targetRouter) {
|
||||
logger.fine("Creating 'fast' to "+ targetRouter);
|
||||
logger.fine("Performing CREATE_FAST handshake with " + targetRouter);
|
||||
final TorCreateFastKeyAgreement kex = new TorCreateFastKeyAgreement();
|
||||
sendCreateFastCell(kex);
|
||||
return receiveAndProcessCreateFastResponse(targetRouter, kex);
|
||||
@ -50,7 +50,7 @@ public class CircuitExtender {
|
||||
private CircuitNode receiveAndProcessCreateFastResponse(Router targetRouter, TorKeyAgreement kex) {
|
||||
final Cell cell = circuit.receiveControlCellResponse();
|
||||
if(cell == null) {
|
||||
throw new TorException("Timeout building circuit waiting for CREATE_FAST response from "+ targetRouter);
|
||||
throw new TorException("Circuit build timeout waiting for CREATE_FAST response from " + targetRouter);
|
||||
}
|
||||
|
||||
return processCreatedFastCell(targetRouter, cell, kex);
|
||||
@ -89,14 +89,14 @@ public class CircuitExtender {
|
||||
}
|
||||
|
||||
private void logProtocolViolation(String sourceName, Router targetRouter) {
|
||||
final String version = (targetRouter == null) ? "(none)" : targetRouter.getVersion();
|
||||
final String targetName = (targetRouter == null) ? "(none)" : targetRouter.getNickname();
|
||||
logger.warning("Protocol error extending circuit from ("+ sourceName +") to ("+ targetName +") [version: "+ version +"]");
|
||||
final String version = (targetRouter == null) ? "n/a" : targetRouter.getVersion();
|
||||
final String targetName = (targetRouter == null) ? "n/a" : targetRouter.getNickname();
|
||||
logger.warning("Protocol error extending circuit from [" + sourceName + "] to [" + targetName + "] -> version: " + version);
|
||||
}
|
||||
|
||||
private String nodeToName(CircuitNode node) {
|
||||
if(node == null || node.getRouter() == null) {
|
||||
return "(null)";
|
||||
return "n/a";
|
||||
}
|
||||
final Router router = node.getRouter();
|
||||
return router.getNickname();
|
||||
@ -121,7 +121,7 @@ public class CircuitExtender {
|
||||
if(code == Cell.ERROR_PROTOCOL) {
|
||||
logProtocolViolation(source, extendTarget);
|
||||
}
|
||||
throw new TorException("Error from ("+ source +") while extending to ("+ extendTarget.getNickname() + "): "+ msg);
|
||||
throw new TorException("Error from [" + source + "] while extending to [" + extendTarget.getNickname() + "] " + msg);
|
||||
} else if(command != expectedCommand) {
|
||||
final String expected = RelayCellImpl.commandToDescription(expectedCommand);
|
||||
final String received = RelayCellImpl.commandToDescription(command);
|
||||
@ -134,7 +134,7 @@ public class CircuitExtender {
|
||||
|
||||
public CircuitNode createNewNode(Router r, byte[] keyMaterial, byte[] verifyDigest) {
|
||||
final CircuitNode node = CircuitNodeImpl.createNode(r, circuit.getFinalCircuitNode(), keyMaterial, verifyDigest);
|
||||
logger.fine("Adding new circuit node for "+ r.getNickname());
|
||||
logger.fine("Adding new circuit node [" + r.getNickname() + "]"); // we're extending the circuit with this node, not for it.
|
||||
circuit.appendNode(node);
|
||||
return node;
|
||||
|
||||
|
@ -261,19 +261,81 @@ public abstract class CircuitImpl implements Circuit, DashboardRenderable {
|
||||
protected abstract String getCircuitTypeLabel();
|
||||
|
||||
public String toString() {
|
||||
return " Circuit ("+ getCircuitTypeLabel() + ") id="+ getCircuitId() +" state=" + status.getStateAsString() +" "+ pathToString();
|
||||
return "<tr class=\"circuit\"><td title=\"Circuit ID\">" + getCircuitId() + "</td><td>" +
|
||||
getCircuitTypeLabel() + "</td><td>" +
|
||||
status.getStateAsString().replace("Open ", "").replace("[", "").replace("]", "") + "</td><td>" +
|
||||
// "bandwidth usage stats here</td><td> -->" +
|
||||
pathToString() + "</td></tr>";
|
||||
}
|
||||
|
||||
|
||||
protected String pathToString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
sb.append("<span class=\"circuitcontainer\">");
|
||||
for(CircuitNode node: nodeList) {
|
||||
Router r = node.getRouter();
|
||||
if(sb.length() > 1)
|
||||
sb.append(",");
|
||||
sb.append(node.toString());
|
||||
sb.append(" <span class=\"hidden\">-> </span>");
|
||||
sb.append("<span class=\"nodecontainer\"><span class=\"hidden\">[</span><span class=\"node\" onclick=\"copyText();\"><span class=\"flag\" data-country=\"");
|
||||
if (r != null) {
|
||||
if (r.getCountryName() != null)
|
||||
sb.append(r.getCountryName() + " (" + r.getAddress() + ")");
|
||||
else
|
||||
sb.append(r.getAddress());
|
||||
} else {
|
||||
sb.append("unknown");
|
||||
}
|
||||
sb.append("]");
|
||||
sb.append("\">");
|
||||
sb.append("<img height=\"11\" width=\"16\" src=\"/flags.jsp?c=");
|
||||
if (r != null && r.getCountryName() != null)
|
||||
sb.append(r.getCountryCode().toLowerCase().replace("--", "a0"));
|
||||
else
|
||||
sb.append("a0");
|
||||
sb.append("\"></span>");
|
||||
if (r != null) {
|
||||
String idHash = r.getIdentityHash().toString().toUpperCase();
|
||||
int uptime = r.getUptime();
|
||||
int bw = r.getObservedBandwidth();
|
||||
sb.append("<span class=\"nickname");
|
||||
if (r.getPlatform() != null || r.getUptime() > 0 || r.getObservedBandwidth() > 0)
|
||||
sb.append("\" data-ipv4=\"");
|
||||
|
||||
if (r.getPlatform() != null)
|
||||
sb.append(r.getPlatform().replace("Tor ", "").replace(" on ", " / ").replace("-alpha-dev", "-alpha"));
|
||||
|
||||
if (uptime > 0 && bw > 0)
|
||||
sb.append(" \u2022 ");
|
||||
if (bw > 0 && bw < 1048576)
|
||||
sb.append((bw / 1024) + " KB/s");
|
||||
else if (bw >= 1048576)
|
||||
sb.append(((bw / 1024) / 1024) + " MB/s");
|
||||
|
||||
if (uptime > 0)
|
||||
sb.append(" \u2022 Up: ");
|
||||
if (uptime > 172800)
|
||||
sb.append((((uptime / 60) / 60) / 24) + " days");
|
||||
else if (uptime > 1440)
|
||||
sb.append(((uptime / 60) / 60) + " hours");
|
||||
else if (uptime > 3600)
|
||||
sb.append(uptime / 60 + " minutes");
|
||||
else if (uptime > 0)
|
||||
sb.append(uptime + " seconds");
|
||||
|
||||
sb.append("\">");
|
||||
|
||||
sb.append("<a class=\"script\" href=\"https://metrics.torproject.org/rs.html#search/" + idHash + "\" target=\"_blank\">" + node.toString() + "</a>");
|
||||
|
||||
// <noscript> alternative lookup
|
||||
sb.append("<noscript>");
|
||||
sb.append("<a href=\"https://torstatus.blutmagie.de/router_detail.php?FP=" + idHash + "\" target=\"_blank\">" + node.toString() + "</a>");
|
||||
sb.append("</noscript>");
|
||||
|
||||
sb.append("</span><span class=\"hidden\"> (<b>" + r.getAddress() + "</b>)</span></span><span class=\"hidden\">]</span></span>");
|
||||
} else {
|
||||
sb.append("<span class=\"nickname unknown\"><i>unknown</i></span>");
|
||||
sb.append("</span><span class=\"hidden\">]</span></span>");
|
||||
}
|
||||
}
|
||||
sb.append("</span>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
@ -230,6 +230,8 @@ public class CircuitManagerImpl implements CircuitManager, DashboardRenderable {
|
||||
throw new OpenFailedException("Hidden services not supported");
|
||||
} else if(hostname.toLowerCase().endsWith(".exit")) {
|
||||
throw new OpenFailedException(".exit addresses are not supported");
|
||||
} else if (hostname.toLowerCase().endsWith(".i2p")) {
|
||||
throw new OpenFailedException(".i2p addresses are not supported");
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,13 +338,15 @@ public class CircuitManagerImpl implements CircuitManager, DashboardRenderable {
|
||||
if((flags & DASHBOARD_CIRCUITS) == 0) {
|
||||
return;
|
||||
}
|
||||
renderer.renderComponent(writer, flags, connectionCache);
|
||||
renderer.renderComponent(writer, flags, circuitCreationTask.getCircuitPredictor());
|
||||
writer.println("[Circuit Manager]");
|
||||
writer.println();
|
||||
writer.println("<table id=\"circuitmon\" width=\"100%\">\n<tr><th colspan=\"4\" align=\"left\">Circuit Monitor</th></tr>");
|
||||
writer.println("<tr class=\"subtitle\"><th align=\"left\">Circuit ID</th><th align=\"left\">Type</th>" +
|
||||
"<th align=\"left\">State</th><th align=\"left\" width=\"50%\">Participants</th></tr>");
|
||||
for(Circuit c: getCircuitsByFilter(null)) {
|
||||
renderer.renderComponent(writer, flags, c);
|
||||
}
|
||||
writer.println("</table>\n</td></tr>");
|
||||
renderer.renderComponent(writer, flags, connectionCache);
|
||||
renderer.renderComponent(writer, flags, circuitCreationTask.getCircuitPredictor());
|
||||
}
|
||||
|
||||
public InternalCircuit getCleanInternalCircuit() throws InterruptedException {
|
||||
|
@ -67,9 +67,9 @@ public class CircuitNodeImpl implements CircuitNode {
|
||||
|
||||
public String toString() {
|
||||
if(router != null) {
|
||||
return "|"+ router.getNickname() + "|";
|
||||
return router.getNickname();
|
||||
} else {
|
||||
return "|()|";
|
||||
return "<i>unknown</i>";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,21 +79,31 @@ public class CircuitPredictor implements DashboardRenderable {
|
||||
|
||||
public void dashboardRender(DashboardRenderer renderer, PrintWriter writer, int flags)
|
||||
throws IOException {
|
||||
|
||||
if((flags & DASHBOARD_PREDICTED_PORTS) == 0) {
|
||||
return;
|
||||
}
|
||||
writer.println("[Predicted Ports] ");
|
||||
if (getPredictedPorts().size() > 0)
|
||||
writer.print("<tr>");
|
||||
else
|
||||
writer.print("<tr class=\".hidden\" hidden>");
|
||||
writer.println("<td>\n<hr>\n<table id=\"ports\" width=\"100%\">\n<tr><th colspan=\"4\" align=\"left\">Predicted Ports</th></tr>");
|
||||
writer.println("<tr class=\"subtitle\"><th colspan=\"2\" align=\"left\" width=\"50%\">Port</th><th colspan=\"2\" align=\"left\">Last Seen</th></tr>");
|
||||
for(int port : portsSeen.keySet()) {
|
||||
writer.write(" "+ port);
|
||||
writer.write("<tr><td colspan=\"2\">" + port + "</td>");
|
||||
Long lastSeen = portsSeen.get(port);
|
||||
writer.write("<td colspan=\"2\">");
|
||||
if(lastSeen != null) {
|
||||
long now = System.currentTimeMillis();
|
||||
long ms = now - lastSeen;
|
||||
writer.write(" (last seen "+ TimeUnit.MINUTES.convert(ms, TimeUnit.MILLISECONDS) +" minutes ago)");
|
||||
String min = "minutes";
|
||||
if (ms > 59999 && ms < 120000)
|
||||
min = "minute";
|
||||
if (ms < 60000)
|
||||
writer.write("<span class=\"nowrap\">in the last 60s</span>");
|
||||
else
|
||||
writer.write("<span class=\"nowrap\">" + TimeUnit.MINUTES.convert(ms, TimeUnit.MILLISECONDS) + " " + min + " ago</span>");
|
||||
}
|
||||
writer.println();
|
||||
writer.println("</td></tr>");
|
||||
}
|
||||
writer.println();
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ public class CircuitStatus {
|
||||
|
||||
enum CircuitState {
|
||||
UNCONNECTED("Unconnected"),
|
||||
BUILDING("Building"),
|
||||
BUILDING("<i>Building…</i>"),
|
||||
FAILED("Failed"),
|
||||
OPEN("Open"),
|
||||
DESTROYED("Destroyed");
|
||||
@ -103,9 +103,9 @@ public class CircuitStatus {
|
||||
|
||||
private String getDirtyString() {
|
||||
if(!isDirty()) {
|
||||
return "Clean";
|
||||
return "<span class=\"nowrap\"><span class=\"hidden\">(</span>Clean<span class=\"hidden\">)</span></span>";
|
||||
} else {
|
||||
return "Dirty "+ (getMillisecondsDirty() / 1000) +"s";
|
||||
return "<span class=\"nowrap\"><span class=\"hidden\">(</span>Dirty: " + (getMillisecondsDirty() / 1000) + "s<span class=\"hidden\">)</span></span>";
|
||||
}
|
||||
}
|
||||
int nextStreamId() {
|
||||
|
@ -37,6 +37,6 @@ public class DirectoryCircuitImpl extends CircuitImpl implements DirectoryCircui
|
||||
|
||||
@Override
|
||||
protected String getCircuitTypeLabel() {
|
||||
return "Directory";
|
||||
return "<span class=\"directory\">Directory</span>";
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,6 @@ public class ExitCircuitImpl extends CircuitImpl implements ExitCircuit {
|
||||
|
||||
@Override
|
||||
protected String getCircuitTypeLabel() {
|
||||
return "Exit";
|
||||
return "<span class=\"exit\">Exit</span>";
|
||||
}
|
||||
}
|
||||
|
@ -104,15 +104,15 @@ public class InternalCircuitImpl extends CircuitImpl implements InternalCircuit,
|
||||
protected String getCircuitTypeLabel() {
|
||||
switch(type) {
|
||||
case HS_CIRCUIT:
|
||||
return "Hidden Service";
|
||||
return "<span class=\"hiddenservice\">Hidden Service</span>";
|
||||
case HS_DIRECTORY:
|
||||
return "HS Directory";
|
||||
return "<span class=\"hiddenservice\">HS Directory</span>";
|
||||
case HS_INTRODUCTION:
|
||||
return "HS Introduction";
|
||||
return "<span class=\"hiddenservice\">HS Introduction</span>";
|
||||
case UNUSED:
|
||||
return "Internal";
|
||||
return "<span class=\"internal\">Internal</span>";
|
||||
default:
|
||||
return "(null)";
|
||||
return "[null]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ public class StreamImpl implements Stream, DashboardRenderable {
|
||||
if(isClosed)
|
||||
return;
|
||||
|
||||
logger.fine("Closing stream "+ this);
|
||||
logger.fine("Closing " + this);
|
||||
|
||||
isClosed = true;
|
||||
inputStream.close();
|
||||
@ -140,7 +140,7 @@ public class StreamImpl implements Stream, DashboardRenderable {
|
||||
}
|
||||
|
||||
public void openDirectory(long timeout) throws InterruptedException, TimeoutException, StreamConnectFailedException {
|
||||
streamTarget = "[Directory]";
|
||||
streamTarget = "Directory Service";
|
||||
final RelayCell cell = new RelayCellImpl(circuit.getFinalCircuitNode(), circuit.getCircuitId(), streamId, RelayCell.RELAY_BEGIN_DIR);
|
||||
circuit.sendRelayCellToFinalNode(cell);
|
||||
waitForRelayConnected(timeout);
|
||||
@ -207,18 +207,20 @@ public class StreamImpl implements Stream, DashboardRenderable {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "[Stream stream_id="+ streamId + " circuit="+ circuit +" target="+ streamTarget +"]";
|
||||
return "StreamID=" + streamId + " Circuit=" + circuit + " Target=" + streamTarget;
|
||||
}
|
||||
|
||||
public void dashboardRender(DashboardRenderer renderer, PrintWriter writer, int flags) throws IOException {
|
||||
writer.print(" ");
|
||||
writer.print("[Stream stream_id="+ streamId + " cid="+ circuit.getCircuitId());
|
||||
writer.print("<tr class=\"stream\"><td title=\"Stream ID\">");
|
||||
writer.print("<b class=\"streamID\">Stream ID: </b>" + streamId + "</td>");
|
||||
// TODO: Indicate target if .onion > hiddenServiceManager.getStreamTo(hostname, port);
|
||||
writer.print("<td colspan=\"2\"><b class=\"target\" title=\"Target\">Target:</b> " + streamTarget + "</td><td>");
|
||||
if(relayConnectedReceived) {
|
||||
writer.print(" sent="+outputStream.getBytesSent() + " recv="+ inputStream.getBytesReceived());
|
||||
writer.print("<span class=\"nowrap\" title=\"Received\"><b class=\"rx\">Received:</b> " + (inputStream.getBytesReceived() / 1024) + " KB</span> " +
|
||||
"<span class=\"nowrap\" title=\"Sent\"><b class=\"tx\">Sent:</b> " + (outputStream.getBytesSent() / 1024) + " KB</span>");
|
||||
} else {
|
||||
writer.print(" (waiting connect)");
|
||||
writer.print("<i>Connecting…</i>");
|
||||
}
|
||||
writer.print(" target="+ streamTarget);
|
||||
writer.println("]");
|
||||
writer.println("</td></tr>");
|
||||
}
|
||||
}
|
||||
|
@ -177,39 +177,39 @@ public class CellImpl implements Cell {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Cell: circuit_id="+ circuitId +" command="+ command +" payload_len="+ cellBuffer.position();
|
||||
return "Cell: CircuitID=" + circuitId +" Command=" + command +" PayloadLength=" + cellBuffer.position();
|
||||
}
|
||||
|
||||
public static String errorToDescription(int errorCode) {
|
||||
switch(errorCode) {
|
||||
case ERROR_NONE:
|
||||
return "No error reason given";
|
||||
return "\n* Reason: No error reason given";
|
||||
case ERROR_PROTOCOL:
|
||||
return "Tor protocol violation";
|
||||
return "\n* Reason: Tor protocol violation";
|
||||
case ERROR_INTERNAL:
|
||||
return "Internal error";
|
||||
return "\n* Reason: Internal error";
|
||||
case ERROR_REQUESTED:
|
||||
return "Response to a TRUNCATE command sent from client";
|
||||
return "\n* Reason: Response to a TRUNCATE command sent from client";
|
||||
case ERROR_HIBERNATING:
|
||||
return "Not currently operating; trying to save bandwidth.";
|
||||
return "\n* Reason: Not currently operating; trying to save bandwidth.";
|
||||
case ERROR_RESOURCELIMIT:
|
||||
return "Out of memory, sockets, or circuit IDs.";
|
||||
return "\n* Reason: Out of memory, sockets, or circuit IDs.";
|
||||
case ERROR_CONNECTFAILED:
|
||||
return "Unable to reach server.";
|
||||
return "\n* Reason: Unable to reach server.";
|
||||
case ERROR_OR_IDENTITY:
|
||||
return "Connected to server, but its OR identity was not as expected.";
|
||||
return "\n* Reason: Connected to server, but its OR identity was not as expected.";
|
||||
case ERROR_OR_CONN_CLOSED:
|
||||
return "The OR connection that was carrying this circuit died.";
|
||||
return "\n* Reason: The OR connection that was carrying this circuit died.";
|
||||
case ERROR_FINISHED:
|
||||
return "The circuit has expired for being dirty or old.";
|
||||
return "\n* Reason: The circuit has expired for being dirty or old.";
|
||||
case ERROR_TIMEOUT:
|
||||
return "Circuit construction took too long.";
|
||||
return "\n* Reason: Circuit construction took too long.";
|
||||
case ERROR_DESTROYED:
|
||||
return "The circuit was destroyed without client TRUNCATE";
|
||||
return "\n* Reason: The circuit was destroyed without client TRUNCATE";
|
||||
case ERROR_NOSUCHSERVICE:
|
||||
return "Request for unknown hidden service";
|
||||
return "\n* Reason: Request for unknown hidden service";
|
||||
default:
|
||||
return "Error code "+ errorCode;
|
||||
return "\n* Reason: Error code " + errorCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import java.util.Set;
|
||||
|
||||
import com.subgraph.orchid.BridgeRouter;
|
||||
import com.subgraph.orchid.Descriptor;
|
||||
import com.subgraph.orchid.Directory;
|
||||
import com.subgraph.orchid.Router;
|
||||
import com.subgraph.orchid.RouterDescriptor;
|
||||
import com.subgraph.orchid.crypto.TorPublicKey;
|
||||
import com.subgraph.orchid.data.HexDigest;
|
||||
@ -18,7 +20,9 @@ public class BridgeRouterImpl implements BridgeRouter {
|
||||
private HexDigest identity;
|
||||
private Descriptor descriptor;
|
||||
|
||||
private Directory directory;
|
||||
private volatile String cachedCountryCode;
|
||||
private volatile String cachedCountryName;
|
||||
|
||||
BridgeRouterImpl(IPv4Address address, int port) {
|
||||
this.address = address;
|
||||
@ -29,6 +33,14 @@ public class BridgeRouterImpl implements BridgeRouter {
|
||||
return address;
|
||||
}
|
||||
|
||||
public String getPlatform() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getUptime() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public HexDigest getIdentity() {
|
||||
return identity;
|
||||
}
|
||||
@ -41,6 +53,10 @@ public class BridgeRouterImpl implements BridgeRouter {
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
public void setDirectory(Directory directory) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
@ -88,6 +104,15 @@ public class BridgeRouterImpl implements BridgeRouter {
|
||||
return cc;
|
||||
}
|
||||
|
||||
public String getCountryName() {
|
||||
String cn = cachedCountryName;
|
||||
if (cn == null) {
|
||||
cn = CountryCodeService.getInstance().getCountryNameForAddress(getAddress());
|
||||
cachedCountryName = cn;
|
||||
}
|
||||
return cn;
|
||||
}
|
||||
|
||||
public int getOnionPort() {
|
||||
return port;
|
||||
}
|
||||
|
@ -11,12 +11,15 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
public class TorConfigImpl implements TorConfig {
|
||||
|
||||
private void resetDefaults() {
|
||||
dataDirectory = toFile("~/.orchid");
|
||||
circuitBuildTimeout = toMS(60, TimeUnit.SECONDS);
|
||||
circuitStreamTimeout = 0;
|
||||
// circuitStreamTimeout = 0; // a value of 0 will cause a request for an unavailable resource to never fail?
|
||||
circuitStreamTimeout = toMS(90, TimeUnit.SECONDS);
|
||||
circuitIdleTimeout = toMS(1, TimeUnit.HOURS);
|
||||
newCircuitPeriod = toMS(30, TimeUnit.SECONDS);
|
||||
maxCircuitDirtiness = toMS(10, TimeUnit.MINUTES);
|
||||
@ -34,14 +37,19 @@ public class TorConfigImpl implements TorConfig {
|
||||
fascistFirewall = false;
|
||||
firewallPorts = toIntList("80,443");
|
||||
safeSocks = false;
|
||||
safeLogging = true;
|
||||
// safeLogging = true; // ineffective as target info displays in logs
|
||||
safeLogging = false;
|
||||
warnUnsafeSocks = true;
|
||||
clientRejectInternalAddress = true;
|
||||
handshakeV3Enabled = true;
|
||||
handshakeV2Enabled = true;
|
||||
hsAuth = new TorConfigHSAuth();
|
||||
useNtorHandshake = AutoBoolValue.AUTO;
|
||||
useMicrodescriptors = AutoBoolValue.AUTO;
|
||||
// full descriptors OOMs for < 192MB
|
||||
if (SystemVersion.getMaxMemory() < 180*1024*1024L)
|
||||
useMicrodescriptors = AutoBoolValue.AUTO;
|
||||
else
|
||||
useMicrodescriptors = AutoBoolValue.FALSE; // pull the full descriptors for extra info e.g. uptime stats
|
||||
useBridges = false;
|
||||
bridgeLines = new ArrayList<TorConfigBridgeLine>();
|
||||
}
|
||||
@ -77,7 +85,6 @@ public class TorConfigImpl implements TorConfig {
|
||||
private boolean useBridges;
|
||||
private List<TorConfigBridgeLine> bridgeLines;
|
||||
|
||||
|
||||
private static long toMS(long time, TimeUnit unit) {
|
||||
return TimeUnit.MILLISECONDS.convert(time, unit);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ public class ConnectionCacheImpl implements ConnectionCache, DashboardRenderable
|
||||
//throw new IllegalStateException("ConnectionCache has been closed");
|
||||
throw new ConnectionFailedException("ConnectionCache has been closed");
|
||||
}
|
||||
logger.fine("Get connection to "+ router.getAddress() + " "+ router.getOnionPort() + " " + router.getNickname());
|
||||
logger.fine("Connecting to [" + router.getNickname() + " (" + router.getAddress() + ":" + router.getOnionPort() +")]");
|
||||
while(true) {
|
||||
Future<ConnectionImpl> f = getFutureFor(router, isDirectoryConnection);
|
||||
try {
|
||||
@ -168,23 +168,29 @@ public class ConnectionCacheImpl implements ConnectionCache, DashboardRenderable
|
||||
if((flags & DASHBOARD_CONNECTIONS) == 0) {
|
||||
return;
|
||||
}
|
||||
if (!getActiveConnections().isEmpty()) // not working.. conncache section should be displayed only when active circuits
|
||||
writer.print("<tr><td>\n");
|
||||
else
|
||||
writer.print("<tr hidden><td>\n");
|
||||
printDashboardBanner(writer, flags);
|
||||
for(Connection c: getActiveConnections()) {
|
||||
if(!c.isClosed()) {
|
||||
renderer.renderComponent(writer, flags, c);
|
||||
}
|
||||
}
|
||||
writer.println();
|
||||
// close connection cache table
|
||||
writer.print("</table>\n</td></tr>\n");
|
||||
}
|
||||
|
||||
private void printDashboardBanner(PrintWriter writer, int flags) {
|
||||
final boolean verbose = (flags & DASHBOARD_CONNECTIONS_VERBOSE) != 0;
|
||||
if(verbose) {
|
||||
writer.println("[Connection Cache (verbose)]");
|
||||
} else {
|
||||
writer.println("[Connection Cache]");
|
||||
}
|
||||
writer.println();
|
||||
writer.print("<hr>\n<table id=\"conncache\" width=\"100%\">\n" +
|
||||
"<tr><th colspan=\"5\" align=\"left\">Connection Cache</th></tr>\n<tr class=\"subtitle\">" +
|
||||
"<th align=\"left\" width=\"25%\">Guard Node <span title=\"Circuits available for immediate use\">(Circuits)</span></th>" +
|
||||
"<th align=\"left\" width=\"25%\">Idle</th>" +
|
||||
"<th align=\"left\" width=\"25%\" title=\"Observed node bandwidth\">Bandwidth</th>" +
|
||||
"<th align=\"left\" width=\"25%\">Uptime</th>" +
|
||||
"</tr>\n");
|
||||
}
|
||||
|
||||
List<Connection> getActiveConnections() {
|
||||
|
@ -190,7 +190,7 @@ public class ConnectionImpl implements Connection, DashboardRenderable {
|
||||
try {
|
||||
output.write(cell.getCellBytes());
|
||||
} catch (IOException e) {
|
||||
logger.fine("IOException writing cell to connection "+ e.getMessage());
|
||||
logger.fine("IOException writing cell to connection: " + e.getMessage());
|
||||
closeSocket();
|
||||
throw new ConnectionIOException(e.getClass().getName() + " : "+ e.getMessage());
|
||||
}
|
||||
@ -298,7 +298,7 @@ public class ConnectionImpl implements Connection, DashboardRenderable {
|
||||
try {
|
||||
circuit = circuitMap.get(cell.getCircuitId());
|
||||
if(circuit == null) {
|
||||
logger.warning("Could not deliver relay cell for circuit id = "+ cell.getCircuitId() +" on connection "+ this +". Circuit not found");
|
||||
logger.warning("Could not deliver relay cell for CircuitID=" + cell.getCircuitId() + " on connection " + this + ". Circuit not found");
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
@ -356,7 +356,7 @@ public class ConnectionImpl implements Connection, DashboardRenderable {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "!" + router.getNickname() + "!";
|
||||
return "[" + router.getNickname() + "]";
|
||||
}
|
||||
|
||||
public void dashboardRender(DashboardRenderer renderer, PrintWriter writer, int flags) throws IOException {
|
||||
@ -370,9 +370,57 @@ public class ConnectionImpl implements Connection, DashboardRenderable {
|
||||
if(circuitCount == 0 && (flags & DASHBOARD_CONNECTIONS_VERBOSE) == 0) {
|
||||
return;
|
||||
}
|
||||
writer.print(" [Connection router="+ router.getNickname());
|
||||
writer.print(" circuits="+ circuitCount);
|
||||
writer.print(" idle="+ (getIdleMilliseconds()/1000) + "s");
|
||||
writer.println("]");
|
||||
if (circuitCount > 0) {
|
||||
String idHash = router.getIdentityHash().toString().toUpperCase();
|
||||
writer.print("<tr><td><span class=\"nodecontainer\"><span class=\"hidden\">[</span><span class=\"node\" onclick=\"copyText();\">" +
|
||||
"<span class=\"flag\" data-country=\"");
|
||||
if (router.getCountryName() != null)
|
||||
writer.print(router.getCountryName() + " (" + router.getAddress() + ")");
|
||||
else
|
||||
writer.print(router.getAddress());
|
||||
writer.print("\"><img height=\"11\" width=\"16\" src=\"/flags.jsp?c=" +
|
||||
router.getCountryCode().toLowerCase().replace("--", "a0") + "\"></span><span class=\"nickname\"");
|
||||
|
||||
|
||||
if (router.getPlatform() != null)
|
||||
writer.print(" data-ipv4=\"" + router.getPlatform().replace("Tor ", "").replace(" on ", " / ") + "\"");
|
||||
writer.print(">");
|
||||
|
||||
writer.print("<a class=\"script\" href=\"https://metrics.torproject.org/rs.html#search/" + idHash + "\" target=\"_blank\">" +
|
||||
router.getNickname() + "</a>" +
|
||||
|
||||
// <noscript> alternative lookup
|
||||
"<noscript>" +
|
||||
"<a href=\"https://torstatus.blutmagie.de/router_detail.php?FP=" + idHash + "\" target=\"_blank\">" +
|
||||
router.getNickname() + "</a>" +
|
||||
"</noscript>" +
|
||||
|
||||
"</span><span class=\"hidden\"> (</span><b>" + router.getAddress() + "</b>" +
|
||||
"<span class=\"hidden\">)</span></span><span class=\"hidden\">]</span></span> <span class=\"circuitcount\">" + circuitCount + "</span></td>");
|
||||
writer.print("<td><span class=\"nowrap\">" + (getIdleMilliseconds() / 1000) + "s</span></td>");
|
||||
writer.print("<td>");
|
||||
long bw = router.getObservedBandwidth();
|
||||
writer.print("<span class=\"nowrap\">");
|
||||
if (bw > 0 && bw < 1048576)
|
||||
writer.print((bw / 1024) + " KB/s");
|
||||
else if (bw >= 1048576)
|
||||
writer.print(((bw / 1024) / 1024) + " MB/s");
|
||||
else
|
||||
writer.print("unknown");
|
||||
writer.print("</span></td>");
|
||||
writer.print("<td><span class=\"nowrap\">");
|
||||
int uptime = router.getUptime();
|
||||
if (uptime > 172800)
|
||||
writer.print((((uptime / 60) / 60) / 24) + " days");
|
||||
else if (uptime > 7200)
|
||||
writer.print(((uptime / 60) / 60) + " hours");
|
||||
else if (uptime > 120)
|
||||
writer.print(uptime / 60 + " mins");
|
||||
else if (uptime > 0)
|
||||
writer.print(uptime + " secs");
|
||||
else
|
||||
writer.print("unknown");
|
||||
writer.print("</span></td></tr>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ public class RouterImpl implements Router {
|
||||
private Descriptor descriptor;
|
||||
|
||||
private volatile String cachedCountryCode;
|
||||
private volatile String cachedCountryName;
|
||||
|
||||
protected RouterImpl(Directory directory, RouterStatus status) {
|
||||
this.directory = directory;
|
||||
@ -38,6 +39,7 @@ public class RouterImpl implements Router {
|
||||
throw new TorException("Identity hash does not match status update");
|
||||
this.status = status;
|
||||
this.cachedCountryCode = null;
|
||||
this.cachedCountryName = null;
|
||||
this.descriptor = null;
|
||||
refreshDescriptor();
|
||||
}
|
||||
@ -147,6 +149,15 @@ public class RouterImpl implements Router {
|
||||
}
|
||||
}
|
||||
|
||||
public String getPlatform() {
|
||||
final RouterDescriptor rd = downcastDescriptor();
|
||||
if (rd != null) {
|
||||
return rd.getPlatform();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getNickname() {
|
||||
return status.getNickname();
|
||||
}
|
||||
@ -173,6 +184,15 @@ public class RouterImpl implements Router {
|
||||
}
|
||||
}
|
||||
|
||||
public int getUptime() {
|
||||
final RouterDescriptor rd = downcastDescriptor();
|
||||
if (rd != null) {
|
||||
return rd.getUptime();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasBandwidth() {
|
||||
return status.hasBandwidth();
|
||||
}
|
||||
@ -237,7 +257,7 @@ public class RouterImpl implements Router {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Router["+ getNickname() +" ("+getAddress() +":"+ getOnionPort() +")]";
|
||||
return "[" + getNickname() + " (" + getAddress() +":" + getOnionPort() + ")]";
|
||||
}
|
||||
|
||||
public String getCountryCode() {
|
||||
@ -249,6 +269,15 @@ public class RouterImpl implements Router {
|
||||
return cc;
|
||||
}
|
||||
|
||||
public String getCountryName() {
|
||||
String cn = cachedCountryName;
|
||||
if (cn == null) {
|
||||
cn = CountryCodeService.getInstance().getCountryNameForAddress(getAddress());
|
||||
cachedCountryName = cn;
|
||||
}
|
||||
return cn;
|
||||
}
|
||||
|
||||
private RouterDescriptor downcastDescriptor() {
|
||||
refreshDescriptor();
|
||||
if(descriptor instanceof RouterDescriptor) {
|
||||
|
@ -14,6 +14,7 @@ import com.subgraph.orchid.directory.parsing.DocumentParsingHandler;
|
||||
/*
|
||||
* This class contains the hardcoded 'bootstrap' directory authority
|
||||
* server information.
|
||||
* https://github.com/torproject/tor/blob/release-0.4.1/src/app/config/auth_dirs.inc
|
||||
*/
|
||||
public class TrustedAuthorities {
|
||||
|
||||
@ -27,7 +28,8 @@ public class TrustedAuthorities {
|
||||
"authority Faravahar orport=443 v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC",
|
||||
"authority gabelmoo orport=443 v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281",
|
||||
"authority bastet orport=443 v3ident=27102BC123E7AF1D4741AE047E160C91ADC76B21 204.13.164.118:80 24E2 F139 121D 4394 C54B 5BCC 368B 3B41 1857 C413",
|
||||
"authority Bifroest orport=443 bridge 37.218.247.217:80 1D8F 3A91 C37C 5D1C 4C19 B1AD 1D0C FBE8 BF72 D8E1",
|
||||
// bridges don't work with orchid? and non-bridges listed after bridges don't work either
|
||||
// "authority Bifroest orport=443 bridge 37.218.247.217:80 1D8F 3A91 C37C 5D1C 4C19 B1AD 1D0C FBE8 BF72 D8E1",
|
||||
};
|
||||
|
||||
private final List<DirectoryServer> directoryServers = new ArrayList<DirectoryServer>();
|
||||
|
@ -86,8 +86,8 @@ public class KeyCertificateImpl implements KeyCertificate {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "(Certificate: address="+ directoryAddress +":"+ directoryPort
|
||||
+" fingerprint="+ fingerprint +" published="+ keyPublished +" expires="+ keyExpires +")"+
|
||||
return "[Certificate: address=" + directoryAddress + ":" + directoryPort
|
||||
+ " fingerprint=" + fingerprint + " published=" + keyPublished + " expires=" + keyExpires + "]" +
|
||||
"\nident="+ identityKey +" sign="+ signingKey;
|
||||
}
|
||||
}
|
||||
|
@ -42,8 +42,8 @@ public class RouterStatusImpl implements RouterStatus {
|
||||
void setRejectedPorts(String portList) { this.exitPorts = ExitPorts.createRejectExitPorts(portList); }
|
||||
|
||||
public String toString() {
|
||||
return "Router: ("+ nickname +" "+ identity +" "+ digest +" "+ address +" "+ routerPort +" " + directoryPort
|
||||
+" "+ version +" "+ exitPorts +")";
|
||||
return "Router: [" + nickname + " " + identity + " " + digest + " " + address + " " + routerPort + " " + directoryPort
|
||||
+ " " + version + " " + exitPorts + "]";
|
||||
}
|
||||
public String getNickname() {
|
||||
return nickname;
|
||||
|
@ -24,7 +24,9 @@ public enum RouterDescriptorKeyword {
|
||||
WRITE_HISTORY("write-history"),
|
||||
EVENTDNS("eventdns", 1),
|
||||
CACHES_EXTRA_INFO("caches-extra-info", 0),
|
||||
EXTRA_INFO_DIGEST("extra-info-digest", 1),
|
||||
// could now be 1 or 2
|
||||
// https://github.com/torproject/torspec/commit/0f03581e748d4867a009d3d9473d61a400a3f5c1
|
||||
EXTRA_INFO_DIGEST("extra-info-digest"),
|
||||
HIDDEN_SERVICE_DIR("hidden-service-dir"),
|
||||
PROTOCOLS("protocols"),
|
||||
ALLOW_SINGLE_HOP_EXITS("allow-single-hop-exits", 0),
|
||||
|
@ -11,6 +11,8 @@ import java.util.logging.Logger;
|
||||
|
||||
import com.subgraph.orchid.data.IPv4Address;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
public class CountryCodeService {
|
||||
private final static Logger logger = Logger.getLogger(CountryCodeService.class.getName());
|
||||
private final static String DATABASE_FILENAME = "GeoIP.dat";
|
||||
@ -48,6 +50,71 @@ public class CountryCodeService {
|
||||
"WS", "YE", "YT", "RS", "ZA", "ZM", "ME", "ZW", "A1", "A2", "O1",
|
||||
"AX", "GG", "IM", "JE", "BL", "MF", "BQ", "SS", "O1" };
|
||||
|
||||
private static final String[] COUNTRY_NAMES = { "N/A", "Asia/Pacific Region",
|
||||
"Europe", "Andorra", "United Arab Emirates", "Afghanistan",
|
||||
"Antigua and Barbuda", "Anguilla", "Albania", "Armenia", "Curacao",
|
||||
"Angola", "Antarctica", "Argentina", "American Samoa", "Austria",
|
||||
"Australia", "Aruba", "Azerbaijan", "Bosnia and Herzegovina",
|
||||
"Barbados", "Bangladesh", "Belgium", "Burkina Faso", "Bulgaria",
|
||||
"Bahrain", "Burundi", "Benin", "Bermuda", "Brunei Darussalam",
|
||||
"Bolivia", "Brazil", "Bahamas", "Bhutan", "Bouvet Island",
|
||||
"Botswana", "Belarus", "Belize", "Canada",
|
||||
"Cocos (Keeling) Islands", "Congo, The Democratic Republic of the",
|
||||
"Central African Republic", "Congo", "Switzerland",
|
||||
"Cote D'Ivoire", "Cook Islands", "Chile", "Cameroon", "China",
|
||||
"Colombia", "Costa Rica", "Cuba", "Cape Verde", "Christmas Island",
|
||||
"Cyprus", "Czech Republic", "Germany", "Djibouti", "Denmark",
|
||||
"Dominica", "Dominican Republic", "Algeria", "Ecuador", "Estonia",
|
||||
"Egypt", "Western Sahara", "Eritrea", "Spain", "Ethiopia",
|
||||
"Finland", "Fiji", "Falkland Islands (Malvinas)",
|
||||
"Micronesia, Federated States of", "Faroe Islands", "France",
|
||||
"Sint Maarten (Dutch part)", "Gabon", "United Kingdom", "Grenada",
|
||||
"Georgia", "French Guiana", "Ghana", "Gibraltar", "Greenland",
|
||||
"Gambia", "Guinea", "Guadeloupe", "Equatorial Guinea", "Greece",
|
||||
"South Georgia and the South Sandwich Islands", "Guatemala",
|
||||
"Guam", "Guinea-Bissau", "Guyana", "Hong Kong",
|
||||
"Heard Island and McDonald Islands", "Honduras", "Croatia",
|
||||
"Haiti", "Hungary", "Indonesia", "Ireland", "Israel", "India",
|
||||
"British Indian Ocean Territory", "Iraq",
|
||||
"Iran, Islamic Republic of", "Iceland", "Italy", "Jamaica",
|
||||
"Jordan", "Japan", "Kenya", "Kyrgyzstan", "Cambodia", "Kiribati",
|
||||
"Comoros", "Saint Kitts and Nevis",
|
||||
"Korea, Democratic People's Republic of", "Korea, Republic of",
|
||||
"Kuwait", "Cayman Islands", "Kazakhstan",
|
||||
"Lao People's Democratic Republic", "Lebanon", "Saint Lucia",
|
||||
"Liechtenstein", "Sri Lanka", "Liberia", "Lesotho", "Lithuania",
|
||||
"Luxembourg", "Latvia", "Libya", "Morocco", "Monaco",
|
||||
"Moldova, Republic of", "Madagascar", "Marshall Islands",
|
||||
"Macedonia", "Mali", "Myanmar", "Mongolia", "Macau",
|
||||
"Northern Mariana Islands", "Martinique", "Mauritania",
|
||||
"Montserrat", "Malta", "Mauritius", "Maldives", "Malawi", "Mexico",
|
||||
"Malaysia", "Mozambique", "Namibia", "New Caledonia", "Niger",
|
||||
"Norfolk Island", "Nigeria", "Nicaragua", "Netherlands", "Norway",
|
||||
"Nepal", "Nauru", "Niue", "New Zealand", "Oman", "Panama", "Peru",
|
||||
"French Polynesia", "Papua New Guinea", "Philippines", "Pakistan",
|
||||
"Poland", "Saint Pierre and Miquelon", "Pitcairn Islands",
|
||||
"Puerto Rico", "Palestinian Territory", "Portugal", "Palau",
|
||||
"Paraguay", "Qatar", "Reunion", "Romania", "Russian Federation",
|
||||
"Rwanda", "Saudi Arabia", "Solomon Islands", "Seychelles", "Sudan",
|
||||
"Sweden", "Singapore", "Saint Helena", "Slovenia",
|
||||
"Svalbard and Jan Mayen", "Slovakia", "Sierra Leone", "San Marino",
|
||||
"Senegal", "Somalia", "Suriname", "Sao Tome and Principe",
|
||||
"El Salvador", "Syrian Arab Republic", "Swaziland",
|
||||
"Turks and Caicos Islands", "Chad", "French Southern Territories",
|
||||
"Togo", "Thailand", "Tajikistan", "Tokelau", "Turkmenistan",
|
||||
"Tunisia", "Tonga", "Timor-Leste", "Turkey", "Trinidad and Tobago",
|
||||
"Tuvalu", "Taiwan", "Tanzania, United Republic of", "Ukraine",
|
||||
"Uganda", "United States Minor Outlying Islands", "United States",
|
||||
"Uruguay", "Uzbekistan", "Holy See (Vatican City State)",
|
||||
"Saint Vincent and the Grenadines", "Venezuela",
|
||||
"Virgin Islands, British", "Virgin Islands, U.S.", "Vietnam",
|
||||
"Vanuatu", "Wallis and Futuna", "Samoa", "Yemen", "Mayotte",
|
||||
"Serbia", "South Africa", "Zambia", "Montenegro", "Zimbabwe",
|
||||
"Anonymous Proxy", "Satellite Provider", "Other", "Aland Islands",
|
||||
"Guernsey", "Isle of Man", "Jersey", "Saint Barthelemy",
|
||||
"Saint Martin", "Bonaire, Saint Eustatius and Saba", "South Sudan",
|
||||
"Other" };
|
||||
|
||||
private final byte[] database;
|
||||
|
||||
public CountryCodeService() {
|
||||
@ -56,9 +123,13 @@ public class CountryCodeService {
|
||||
|
||||
private static byte[] loadDatabase() {
|
||||
final InputStream input = openDatabaseStream();
|
||||
final File dataDir = new File(I2PAppContext.getGlobalContext().getConfigDir(), "plugins/orchid/geoip");
|
||||
final File dbFile = new File(dataDir, DATABASE_FILENAME);
|
||||
if(input == null) {
|
||||
logger.warning("Failed to open '"+ DATABASE_FILENAME + "' database file for country code lookups");
|
||||
logger.warning("Failed to open '" + DATABASE_FILENAME + "' database file in " + dataDir + " for country code lookups");
|
||||
return null;
|
||||
} else {
|
||||
logger.info("Loaded '" + dataDir + "/" + DATABASE_FILENAME + "' for country code lookups");
|
||||
}
|
||||
try {
|
||||
return loadEntireStream(input);
|
||||
@ -82,7 +153,7 @@ public class CountryCodeService {
|
||||
}
|
||||
|
||||
private static InputStream tryFilesystemOpen() {
|
||||
final File dataDir = new File(System.getProperty("user.dir"), "data");
|
||||
final File dataDir = new File(I2PAppContext.getGlobalContext().getConfigDir(), "plugins/orchid/geoip");
|
||||
final File dbFile = new File(dataDir, DATABASE_FILENAME);
|
||||
if(!dbFile.canRead()) {
|
||||
return null;
|
||||
@ -95,7 +166,8 @@ public class CountryCodeService {
|
||||
}
|
||||
|
||||
private static InputStream tryResourceOpen() {
|
||||
return CountryCodeService.class.getResourceAsStream("/data/"+ DATABASE_FILENAME);
|
||||
final File dataDir = new File(I2PAppContext.getGlobalContext().getConfigDir(), "plugins/orchid/geoip");
|
||||
return CountryCodeService.class.getResourceAsStream(dataDir + "/" + DATABASE_FILENAME);
|
||||
}
|
||||
|
||||
private static byte[] loadEntireStream(InputStream input) throws IOException {
|
||||
@ -119,6 +191,10 @@ public class CountryCodeService {
|
||||
return COUNTRY_CODES[seekCountry(address)];
|
||||
}
|
||||
|
||||
public String getCountryNameForAddress(IPv4Address address) {
|
||||
return COUNTRY_NAMES[seekCountry(address)];
|
||||
}
|
||||
|
||||
private int seekCountry(IPv4Address address) {
|
||||
if(database == null) {
|
||||
return 0;
|
||||
@ -140,7 +216,7 @@ public class CountryCodeService {
|
||||
if(xx >= COUNTRY_BEGIN) {
|
||||
final int idx = xx - COUNTRY_BEGIN;
|
||||
if(idx < 0 || idx > COUNTRY_CODES.length) {
|
||||
logger.warning("Invalid index calculated looking up country code record for ("+ address +") idx = "+ idx);
|
||||
logger.warning("Invalid index calculated looking up country code record for: [" + address + "] idx = " + idx);
|
||||
return 0;
|
||||
} else {
|
||||
return idx;
|
||||
@ -150,7 +226,7 @@ public class CountryCodeService {
|
||||
}
|
||||
|
||||
}
|
||||
logger.warning("No record found looking up country code record for ("+ address + ")");
|
||||
logger.warning("No country code record found for: " + address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ import com.subgraph.orchid.config.TorConfigBridgeLine;
|
||||
import com.subgraph.orchid.config.TorConfigInterval;
|
||||
import com.subgraph.orchid.config.TorConfigParser;
|
||||
import com.subgraph.orchid.dashboard.Dashboard;
|
||||
import com.subgraph.orchid.geoip.CountryCodeService;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.*;
|
||||
@ -94,7 +95,7 @@ public class OrchidController implements ClientApp, TorInitializationListener, O
|
||||
if (_mgr != null)
|
||||
_mgr.register(this);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Orchid ready");
|
||||
_log.info("Orchid plugin ready");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +107,8 @@ public class OrchidController implements ClientApp, TorInitializationListener, O
|
||||
throw new IllegalStateException();
|
||||
changeState(STARTING);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Starting Orchid");
|
||||
_log.info("Starting Orchid plugin...");
|
||||
|
||||
// TODO config dir
|
||||
try {
|
||||
_logger = new OrchidLogHandler(_context);
|
||||
@ -114,6 +116,7 @@ public class OrchidController implements ClientApp, TorInitializationListener, O
|
||||
_client.getConfig().setDataDirectory(_configDir);
|
||||
loadConfig(_client.getConfig());
|
||||
_client.addInitializationListener(this);
|
||||
CountryCodeService.getInstance();
|
||||
_client.start();
|
||||
} catch (RuntimeException t) {
|
||||
// TorException extends RuntimeException,
|
||||
@ -191,7 +194,7 @@ public class OrchidController implements ClientApp, TorInitializationListener, O
|
||||
return;
|
||||
changeState(STOPPING);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Stopping Orchid");
|
||||
_log.info("Stopping Orchid plugin...");
|
||||
if (_mgr != null)
|
||||
_mgr.unregister(this);
|
||||
if (_client != null) {
|
||||
@ -204,13 +207,13 @@ public class OrchidController implements ClientApp, TorInitializationListener, O
|
||||
}
|
||||
changeState(STOPPED);
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Orchid stopped");
|
||||
_log.info("Orchid plugin stopped");
|
||||
}
|
||||
|
||||
public Socket connect(String host, int port) throws IOException {
|
||||
if (host.equals("127.0.0.1") || host.equals("localhost") ||
|
||||
host.toLowerCase(Locale.US).endsWith(".i2p"))
|
||||
throw new IOException("unsupported host " + host);
|
||||
throw new IOException("unsupported host: " + host);
|
||||
ClientAppState state = _state;
|
||||
if (state != RUNNING)
|
||||
throw new IOException("Cannot connect in state " + state);
|
||||
@ -224,7 +227,7 @@ public class OrchidController implements ClientApp, TorInitializationListener, O
|
||||
IOException ioe = new IOException("connect error");
|
||||
ioe.initCause(e);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Error Connecting to " + host + ':' + port, ioe);
|
||||
_log.debug("Error connecting to " + host + ':' + port + "\n* Reason:" + ioe.getMessage());
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
@ -246,11 +249,11 @@ public class OrchidController implements ClientApp, TorInitializationListener, O
|
||||
DataHelper.loadProps(p, _configFile);
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("error loading config file", ioe);
|
||||
_log.warn("Error loading Orchid config file \n* Reason: " + ioe.getMessage());
|
||||
return;
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Loading " + p.size() + " configuations");
|
||||
_log.info("Loading " + p.size() + " Orchid configuration options");
|
||||
TorConfigParser tcp = new TorConfigParser();
|
||||
for (Map.Entry e : p.entrySet()) {
|
||||
String k = (String) e.getKey();
|
||||
@ -318,7 +321,7 @@ public class OrchidController implements ClientApp, TorInitializationListener, O
|
||||
tc.setWarnUnsafeSocks((Boolean) tcp.parseValue(v, "BOOLEAN"));
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Unknown config entry " + k + " = " + v);
|
||||
_log.warn("Invalid Orchid config entry: " + k + " = " + v);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -331,7 +334,8 @@ public class OrchidController implements ClientApp, TorInitializationListener, O
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
(new Dashboard()).renderComponent(pw, 0xff, _client.getCircuitManager());
|
||||
pw.close();
|
||||
out.write(DataHelper.escapeHTML(sw.toString()));
|
||||
out.write(sw.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,25 @@ public class OrchidLogHandler extends Handler {
|
||||
public void publish(LogRecord record) {
|
||||
Log log = _mgr.getLog(record.getLoggerName());
|
||||
int level = toI2PLevel(record.getLevel());
|
||||
log.log(level, record.getMessage(), record.getThrown());
|
||||
// fix logs so they don't spew html tags; various cleanups & detritus removal
|
||||
log.log(level, "[Orchid] " + record.getMessage().replaceAll("<noscript>((?:.*?\r?\n?)*)</noscript>", "")
|
||||
.replaceAll("<.+?>", " ")
|
||||
.replaceAll("Building…", "")
|
||||
.replaceAll("…", "...")
|
||||
.replaceAll("Target=", "\\\n* Target: ")
|
||||
.replaceAll("Circuit=", "CircuitID=")
|
||||
.replaceAll("Exit ", "")
|
||||
.replaceAll("Open ", "")
|
||||
.replaceAll("( )+", " ")
|
||||
.replaceAll("\\(.+?\\)", "")
|
||||
.replaceAll("\\[ ", "\\[")
|
||||
.replaceAll(" \\]", "\\]")
|
||||
.replaceAll(" +\\]", "\\]")
|
||||
.replaceAll(" =", "=")
|
||||
.replaceAll("= ", "=")
|
||||
.replaceAll("\\] -> \\[", " -> ")
|
||||
.replaceAll("\\( ", "\\(")
|
||||
.replaceAll(" \\)", "\\)"), record.getThrown());
|
||||
}
|
||||
|
||||
private static int toI2PLevel(Level level) {
|
||||
|
@ -9,6 +9,7 @@ import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.subgraph.orchid.Tor;
|
||||
import com.subgraph.orchid.TorConfig;
|
||||
|
||||
import net.i2p.app.ClientAppManager;
|
||||
@ -16,6 +17,8 @@ import net.i2p.orchid.OrchidController;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Translate;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* From base in i2psnark
|
||||
*/
|
||||
@ -30,10 +33,10 @@ public class OrchidServlet extends BasicServlet {
|
||||
|
||||
private static final String DEFAULT_NAME = "orchid";
|
||||
public static final String PROP_CONFIG_FILE = "orchid.configFile";
|
||||
private static final String DOCTYPE = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
|
||||
private static final String FOOTER = "</div></body></html>";
|
||||
private static final String DOCTYPE = "<!DOCTYPE HTML>\n";
|
||||
private static final String FOOTER = "</div>\n<span id=\"endOfPage\" data-iframe-height></span>\n</body>\n</html>";
|
||||
private static final String BUNDLE = "net.i2p.orchid.messages";
|
||||
|
||||
private static final String RESOURCES = "/orchid/resources/";
|
||||
|
||||
public OrchidServlet() {
|
||||
super();
|
||||
@ -118,7 +121,7 @@ public class OrchidServlet extends BasicServlet {
|
||||
*/
|
||||
private void doGetAndPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
String method = req.getMethod();
|
||||
// this is the part after /i2psnark
|
||||
// this is the part after /orchid
|
||||
String path = req.getServletPath();
|
||||
resp.setHeader("X-Frame-Options", "SAMEORIGIN");
|
||||
|
||||
@ -127,67 +130,126 @@ public class OrchidServlet extends BasicServlet {
|
||||
resp.setContentType("text/html; charset=UTF-8");
|
||||
|
||||
PrintWriter out = resp.getWriter();
|
||||
out.write(DOCTYPE + "<html>\n" +
|
||||
"<head>\n" +
|
||||
"<title>");
|
||||
out.write(_t("Orchid Controller"));
|
||||
out.write(DOCTYPE + "<html>\n<head>\n<title>");
|
||||
out.write(_t("Orchid Tor Client"));
|
||||
out.write("</title>\n");
|
||||
|
||||
out.write("<meta http-equiv=\"Content-Security-Policy\" content=\"script-src \'self\' \'unsafe-inline\';\">\n");
|
||||
out.write("<link rel=\"icon\" href=\"" + RESOURCES + "images/favicon.png\">\n");
|
||||
out.write("<link href=\"" + RESOURCES + "orchid.css\" rel=\"stylesheet\" type=\"text/css\">\n");
|
||||
out.write("<noscript><style>.script, #expand, #collapse {display: none !important;} " +
|
||||
"#configuration {display: table !important;} *::selection {color: #fff; background: #77f;}" +
|
||||
".node:active {cursor: text;}</style></noscript>\n");
|
||||
out.write("</head>\n");
|
||||
out.write("<body><div><h3>Plugin Status</h3>");
|
||||
out.write("<body id=\"orchid\" onload=\"hideConfig();\">\n<div id=\"container\">\n<table id=\"main\" width=\"100%\">\n" +
|
||||
"<thead><tr><th id=\"title\" align=\"left\">Orchid Tor Client</th></tr></thead>\n");
|
||||
out.write("<tbody>\n<tr><td>\n<hr>\n<table id=\"status\" width=\"100%\">\n<tr class=\"subtitle\">" +
|
||||
"<th width=\"33%\">Status</th><th width=\"33%\">Registered with I2P</th><th width=\"33%\">Plugin Version</th></tr>\n");
|
||||
out.write("<tr><td align=\"center\">");
|
||||
OrchidController c = _manager;
|
||||
String status = c.getState().toString();
|
||||
if (c != null) {
|
||||
out.write("Status is: " + c.getState());
|
||||
ClientAppManager cam = _context.clientAppManager();
|
||||
if (cam != null)
|
||||
out.write("<br>Registered? " + (cam.getRegisteredApp("outproxy") != null));
|
||||
if (status.equals("RUNNING"))
|
||||
out.write("<span id=\"running\">Running</span>");
|
||||
else if (status.equals("STARTING"))
|
||||
out.write("<span id=\"starting\">Starting...</span>");
|
||||
else
|
||||
out.write("<br>Not registered, no client manager");
|
||||
out.write("<h3>Circuit Status</h3><pre>");
|
||||
// not really in HTML for now
|
||||
out.write("" + c.getState());
|
||||
} else {
|
||||
out.write("Not initialized");
|
||||
}
|
||||
out.write("</td><td align=\"center\">");
|
||||
ClientAppManager cam = _context.clientAppManager();
|
||||
if (c != null && cam != null) {
|
||||
if (cam.getRegisteredApp("outproxy") != null) {
|
||||
// out.write("<span id=\"registered\">" + (cam.getRegisteredApp("outproxy") != null) + "</span>");
|
||||
out.write("<span id=\"registered\">Yes</span>");
|
||||
} else {
|
||||
out.write("<span id=\"starting\">In progress...</span>");
|
||||
}
|
||||
} else {
|
||||
out.write("<span id=\"notregistered\" title=\"Not registered, no client manager\">Not registered, no client manager</span>");
|
||||
}
|
||||
out.write("</td>" +
|
||||
"<td align=\"center\">" +
|
||||
Tor.getFullVersion() + "</td></tr>\n</table>\n");
|
||||
if (c != null) {
|
||||
out.write("<hr>\n<!-- Circuit Status -->\n<tr><th id=\"circuitstatus\" align=\"left\">Circuit Status " +
|
||||
"<span id=\"refresh\" style=\"float: right;\"><a href=\".\">Refresh</a></span></th></tr>\n");
|
||||
out.write("<tr><td>\n<hr>\n");
|
||||
// yes, really in HTML now!
|
||||
c.renderStatusHTML(out);
|
||||
out.write("</pre>");
|
||||
out.write("</table>\n</td></tr>\n<!-- end Circuit Status -->\n");
|
||||
TorConfig tc = c.getConfig();
|
||||
if (tc != null)
|
||||
out.write("<tr id=\"configsection\"><td>\n<hr>\n<div id=\"configtitle\"><b>Configuration Parameters</b> \n" +
|
||||
"<a class=\"script\" id=\"expand\" href=\"#\" onclick=\"clean();expand();\"><img alt=\"Expand\" src=\"/orchid/resources/images/expand.png\" title=\"Expand\"></a>\n" +
|
||||
"<a class=\"script\" id=\"collapse\" href=\"#\" onclick=\"clean();collapse();\"><img alt=\"Collapse\" src=\"/orchid/resources/images/collapse.png\" title=\"Collapse\"></a></div>\n");
|
||||
out.write(getHTMLConfig(tc));
|
||||
} else {
|
||||
out.write("Status is: UNINITIALIZED");
|
||||
}
|
||||
out.write("<script src=\"" + RESOURCES + "ajaxRefresh.js\" type=\"application/javascript\"></script>\n");
|
||||
out.write("<script src=\"" + RESOURCES + "toggleConfig.js\" type=\"application/javascript\"></script>\n");
|
||||
out.write(FOOTER);
|
||||
}
|
||||
|
||||
private static String getHTMLConfig(TorConfig tc) {
|
||||
File configPath = I2PAppContext.getGlobalContext().getConfigDir();
|
||||
String slash = System.getProperty("file.separator");
|
||||
StringBuilder buf = new StringBuilder(1024);
|
||||
buf.append("<h3>Configuration</h3>");
|
||||
buf.append("<table cellspacing=\"8\">");
|
||||
buf.append("<tr><th>Config</th><th>Value</th></tr>");
|
||||
buf.append("<tr><td>Bridges</td><td>").append(tc.getBridges()).append("</tr>");
|
||||
buf.append("<tr><td>Circuit Build Timeout</td><td>").append(tc.getCircuitBuildTimeout()).append("</tr>");
|
||||
buf.append("<tr><td>Circuit Idle Timeout</td><td>").append(tc.getCircuitIdleTimeout()).append("</tr>");
|
||||
buf.append("<tr><td>Circuit Stream Timeout</td><td>").append(tc.getCircuitStreamTimeout()).append("</tr>");
|
||||
buf.append("<tr><td>Client Reject Internal Address</td><td>").append(tc.getClientRejectInternalAddress()).append("</tr>");
|
||||
buf.append("<tr><td>Enforce Distinct Subnets</td><td>").append(tc.getEnforceDistinctSubnets()).append("</tr>");
|
||||
buf.append("<tr><td>Entry Guards</td><td>").append(tc.getNumEntryGuards()).append("</tr>");
|
||||
buf.append("<tr><td>Entry Nodes</td><td>").append(tc.getEntryNodes()).append("</tr>");
|
||||
buf.append("<tr><td>Exclude Exit Nodes</td><td>").append(tc.getExcludeExitNodes()).append("</tr>");
|
||||
buf.append("<tr><td>Exclude Nodes</td><td>").append(tc.getExcludeNodes()).append("</tr>");
|
||||
buf.append("<tr><td>Exit Nodes</td><td>").append(tc.getExitNodes()).append("</tr>");
|
||||
buf.append("<tr><td>Fascist Firewall</td><td>").append(tc.getFascistFirewall()).append("</tr>");
|
||||
buf.append("<tr><td>Firewall Ports</td><td>").append(tc.getFirewallPorts()).append("</tr>");
|
||||
buf.append("<tr><td>Handshake V2 Enabled</td><td>").append(tc.getHandshakeV2Enabled()).append("</tr>");
|
||||
buf.append("<tr><td>Handshake V3 Enabled</td><td>").append(tc.getHandshakeV3Enabled()).append("</tr>");
|
||||
buf.append("<tr><td>Long Lived Ports</td><td>").append(tc.getLongLivedPorts()).append("</tr>");
|
||||
buf.append("<tr><td>Max Circuit Dirtiness</td><td>").append(tc.getMaxCircuitDirtiness()).append("</tr>");
|
||||
buf.append("<tr><td>Max Client Circuits Pending</td><td>").append(tc.getMaxClientCircuitsPending()).append("</tr>");
|
||||
buf.append("<tr><td>New Circuit Period</td><td>").append(tc.getNewCircuitPeriod()).append("</tr>");
|
||||
buf.append("<tr><td>Safe Logging</td><td>").append(tc.getSafeLogging()).append("</tr>");
|
||||
buf.append("<tr><td>Safe Socks</td><td>").append(tc.getSafeSocks()).append("</tr>");
|
||||
buf.append("<tr><td>Strict Nodes</td><td>").append(tc.getStrictNodes()).append("</tr>");
|
||||
buf.append("<tr><td>Use Bridges</td><td>").append(tc.getUseBridges()).append("</tr>");
|
||||
buf.append("<tr><td>Use Microdescriptors</td><td>").append(tc.getUseMicrodescriptors()).append("</tr>");
|
||||
buf.append("<tr><td>Use NTor Handshake</td><td>").append(tc.getUseNTorHandshake()).append("</tr>");
|
||||
buf.append("<tr><td>Warn Unsafe Socks</td><td>").append(tc.getWarnUnsafeSocks()).append("</tr>");
|
||||
buf.append("</table>");
|
||||
buf.append("<hr>\n<table id=\"configuration\" width=\"100%\">\n");
|
||||
buf.append("<tr><td class=\"notice\" colspan=\"3\">");
|
||||
buf.append("The configuration is stored at <code>" + configPath + slash + "plugins" + slash + "orchid" + slash + "orchid.config</code>. " +
|
||||
"Any changes will require a restart of the plugin to take effect. " +
|
||||
"For more information on the configuration options, see <a href=\"https://www.torproject.org/docs/tor-manual.html.en\" target=\"_blank\">Tor's Online Manual</a>.");
|
||||
buf.append("</td></tr>\n");
|
||||
buf.append("<tr><th align=\"left\">Setting Name</th><th align=\"left\">Value <i>(hint)</i></th><th align=\"left\" width=\"50%\">Notes</th></tr>\n");
|
||||
buf.append("<tr><td>Bridges</td><td><code>");
|
||||
if (!tc.getBridges().isEmpty())
|
||||
buf.append(tc.getBridges());
|
||||
buf.append("</code></td><td>See <a href=\"https://bridges.torproject.org\" target=\"_blank\">bridges.torproject.org</a></td></tr>\n");
|
||||
buf.append("<tr><td>Circuit Build Timeout</td><td><code>").append(tc.getCircuitBuildTimeout()).append("</code> <span class=\"nowrap\">(")
|
||||
.append(tc.getCircuitBuildTimeout() / 1000).append(" seconds)").append("</span></td><td>Time limit for new circuit build</td></tr>\n");
|
||||
buf.append("<tr><td>Circuit Idle Timeout</td><td><code>").append(tc.getCircuitIdleTimeout()).append("</code> <span class=\"nowrap\">(")
|
||||
.append((tc.getCircuitIdleTimeout() / 1000) / 60).append(" minutes)</span>").append("</td><td>Expire circuit if unused for period</td></tr>\n");
|
||||
buf.append("<tr><td>Circuit Stream Timeout</td><td><code>").append(tc.getCircuitStreamTimeout()).append("</code> <span class=\"nowrap\">(")
|
||||
.append(tc.getCircuitStreamTimeout() / 1000).append(" seconds)</span>").append("</td><td>Timeout for trying new circuit if stream fails</td></tr>\n");
|
||||
buf.append("<tr><td>Client Reject Internal Address</td><td><code>").append(tc.getClientRejectInternalAddress()).append("</code></td><td>Reject connection attempts to internal addresses</td></tr>\n");
|
||||
buf.append("<tr><td>Enforce Distinct Subnets</td><td><code>").append(tc.getEnforceDistinctSubnets()).append("</code></td><td>Don't use nodes from the same /16 in a single circuit</td></tr>\n");
|
||||
buf.append("<tr><td>Entry Nodes</td><td><code>");
|
||||
if (!tc.getEntryNodes().isEmpty())
|
||||
buf.append(tc.getEntryNodes());
|
||||
buf.append("</code></td><td>List of desired entry nodes</td></tr>\n");
|
||||
buf.append("<tr><td>Exclude Exit Nodes</td><td><code>");
|
||||
if (!tc.getExcludeExitNodes().isEmpty())
|
||||
buf.append(tc.getExcludeExitNodes());
|
||||
buf.append("</code></td><td>Don't use specified nodes as Exits</td></tr>\n");
|
||||
buf.append("<tr><td>Exclude Nodes</td><td><code>");
|
||||
if (!tc.getExcludeNodes().isEmpty())
|
||||
buf.append(tc.getExcludeNodes());
|
||||
buf.append("</code></td><td>Don't use specified nodes in any circuit</td></tr>\n");
|
||||
buf.append("<tr><td>Exit Nodes</td><td><code>");
|
||||
if (!tc.getExitNodes().isEmpty())
|
||||
buf.append(tc.getExitNodes());
|
||||
buf.append("</code></td><td>Limit Exit nodes to those specified</td></tr>\n");
|
||||
buf.append("<tr><td>Fascist Firewall</td><td><code>").append(tc.getFascistFirewall()).append("</code></td><td>Only connect to nodes using <Firewall Ports></td></tr>\n");
|
||||
buf.append("<tr><td>Firewall Ports</td><td><code>").append(tc.getFirewallPorts()).append("</code></td><td>Specify outgoing ports if behind strict firewall</td></tr>\n");
|
||||
buf.append("<tr><td>Handshake V2 Enabled</td><td><code>").append(tc.getHandshakeV2Enabled()).append("</code></td><td>Use version 2 of the Tor handshake</td></tr>\n");
|
||||
buf.append("<tr><td>Handshake V3 Enabled</td><td><code>").append(tc.getHandshakeV3Enabled()).append("</code></td><td>Use version 3 of the Tor handshake</td></tr>\n");
|
||||
buf.append("<tr><td>Long Lived Ports</td><td><code>").append(tc.getLongLivedPorts()).append("</code></td><td>Map these ports to high-uptime nodes</td></tr>\n");
|
||||
buf.append("<tr><td>Max Circuit Dirtiness</td><td><code>").append(tc.getMaxCircuitDirtiness()).append("</code> <span class=\"nowrap\">(")
|
||||
.append((tc.getMaxCircuitDirtiness() / 1000) / 60).append(" minutes)</span>").append("</td><td>Max time to use circuit before cycling</td></tr>\n");
|
||||
buf.append("<tr><td>Max Client Circuits Pending</td><td><code>").append(tc.getMaxClientCircuitsPending()).append("</code></td><td>Max number of circuit builds in progess</td></tr>\n");
|
||||
buf.append("<tr><td>New Circuit Period</td><td><code>").append(tc.getNewCircuitPeriod()).append("</code> <span class=\"nowrap\">(")
|
||||
.append(tc.getNewCircuitPeriod() / 1000).append(" seconds)</span>").append("</td><td>Period of new circuit build consideration</td></tr>\n");
|
||||
buf.append("<tr><td>Number of Entry Guards</td><td><code>").append(tc.getNumEntryGuards()).append("</code></td><td>Number of long-term entry guards to use if <Use Entry Guards> is set to <i>true</i></td></tr>\n");
|
||||
buf.append("<tr><td>Safe Logging</td><td><code>").append(tc.getSafeLogging()).append("</code></td><td>Scrub sensitive strings (eg. addresses) from logs</td></tr>\n");
|
||||
buf.append("<tr><td>Safe Socks</td><td><code>").append(tc.getSafeSocks()).append("</code></td><td>Reject requests that only provide ip address</td></tr>\n");
|
||||
buf.append("<tr><td>Strict Nodes</td><td><code>").append(tc.getStrictNodes()).append("</code></td><td>Use excluded nodes if necessary for network task</td></tr>\n");
|
||||
buf.append("<tr><td>Use Bridges</td><td><code>").append(tc.getUseBridges()).append("</code></td><td>Use <bridges> to connect to network</td></tr>\n");
|
||||
buf.append("<tr><td>Use Entry Guards</td><td><code>").append(tc.getUseEntryGuards()).append("</code></td><td>Select a few long-term entry nodes and stick with them</td></tr>\n");
|
||||
buf.append("<tr><td>Use Microdescriptors</td><td><code>").append(tc.getUseMicrodescriptors()).append("</code></td><td>Use bandwidth-saving info to build circuits</td></tr>\n");
|
||||
buf.append("<tr><td>Use NTor Handshake</td><td><code>").append(tc.getUseNTorHandshake()).append("</code></td><td>Use the ntor circuit-creation handshake</td></tr>\n");
|
||||
buf.append("<tr><td>Warn Unsafe Socks</td><td><code>").append(tc.getWarnUnsafeSocks()).append("</code></td><td>Warn when requests only provide ip address</td></tr>\n");
|
||||
buf.append("</table>\n</td></tr>\n</table>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>net.i2p.orchid.web.OrchidServlet</servlet-name>
|
||||
<url-pattern>/</url-pattern>
|
||||
<url-pattern></url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
|