[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r10032: Simulator is running, everything but impl package has JavaDo (in puppetor/trunk: . doc src src/de src/de/uniba src/de/uniba/wiai src/de/uniba/wiai/lspi src/de/uniba/wiai/lspi/puppetor src/de/uniba/wiai/lspi/puppetor/examples src/de/uniba/wiai/lspi/puppetor/impl src/net src/net/freehaven src/net/freehaven/tor src/net/freehaven/tor/control src/net/freehaven/tor/control/examples)
- To: or-cvs@xxxxxxxxxxxxx
- Subject: [or-cvs] r10032: Simulator is running, everything but impl package has JavaDo (in puppetor/trunk: . doc src src/de src/de/uniba src/de/uniba/wiai src/de/uniba/wiai/lspi src/de/uniba/wiai/lspi/puppetor src/de/uniba/wiai/lspi/puppetor/examples src/de/uniba/wiai/lspi/puppetor/impl src/net src/net/freehaven src/net/freehaven/tor src/net/freehaven/tor/control src/net/freehaven/tor/control/examples)
- From: kloesing@xxxxxxxx
- Date: Wed, 25 Apr 2007 16:14:58 -0400 (EDT)
- Delivered-to: archiver@seul.org
- Delivered-to: or-cvs-outgoing@seul.org
- Delivered-to: or-cvs@seul.org
- Delivery-date: Wed, 25 Apr 2007 16:15:33 -0400
- Reply-to: or-talk@xxxxxxxxxxxxx
- Sender: owner-or-cvs@xxxxxxxxxxxxx
Author: kloesing
Date: 2007-04-25 16:14:34 -0400 (Wed, 25 Apr 2007)
New Revision: 10032
Added:
puppetor/trunk/doc/
puppetor/trunk/doc/howto.aux
puppetor/trunk/doc/howto.dvi
puppetor/trunk/doc/howto.pdf
puppetor/trunk/doc/howto.tex
puppetor/trunk/doc/logging
puppetor/trunk/license
puppetor/trunk/logging.properties
puppetor/trunk/readme
puppetor/trunk/src/
puppetor/trunk/src/de/
puppetor/trunk/src/de/uniba/
puppetor/trunk/src/de/uniba/wiai/
puppetor/trunk/src/de/uniba/wiai/lspi/
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventListener.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventSource.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
puppetor/trunk/src/net/
puppetor/trunk/src/net/freehaven/
puppetor/trunk/src/net/freehaven/tor/
puppetor/trunk/src/net/freehaven/tor/control/
puppetor/trunk/src/net/freehaven/tor/control/.cvsignore
puppetor/trunk/src/net/freehaven/tor/control/Bytes.java
puppetor/trunk/src/net/freehaven/tor/control/ConfigEntry.java
puppetor/trunk/src/net/freehaven/tor/control/EventHandler.java
puppetor/trunk/src/net/freehaven/tor/control/NullEventHandler.java
puppetor/trunk/src/net/freehaven/tor/control/PasswordDigest.java
puppetor/trunk/src/net/freehaven/tor/control/TorControlCommands.java
puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection.java
puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection0.java
puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection1.java
puppetor/trunk/src/net/freehaven/tor/control/TorControlError.java
puppetor/trunk/src/net/freehaven/tor/control/TorControlSyntaxError.java
puppetor/trunk/src/net/freehaven/tor/control/examples/
puppetor/trunk/src/net/freehaven/tor/control/examples/.cvsignore
puppetor/trunk/src/net/freehaven/tor/control/examples/DebuggingEventHandler.java
puppetor/trunk/src/net/freehaven/tor/control/examples/Main.java
Log:
Simulator is running, everything but impl package has JavaDocs
Added: puppetor/trunk/doc/howto.aux
===================================================================
--- puppetor/trunk/doc/howto.aux 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/doc/howto.aux 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,8 @@
+\relax
+\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{1}}
+\@writefile{toc}{\contentsline {section}{\numberline {2}Installation}{2}}
+\@writefile{toc}{\contentsline {section}{\numberline {3}Example 1: Accessing public Web server over Tor}{3}}
+\@writefile{toc}{\contentsline {section}{\numberline {4}Example 2: Advertising hidden service to public Tor network}{5}}
+\@writefile{toc}{\contentsline {section}{\numberline {5}Example 3: Advertising and accessing hidden service over public Tor network}{6}}
+\@writefile{toc}{\contentsline {section}{\numberline {6}Example 4: Advertising and accessing hidden service over private Tor network}{7}}
+\@writefile{toc}{\contentsline {section}{\numberline {7}Known issues}{9}}
Property changes on: puppetor/trunk/doc/howto.aux
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/doc/howto.dvi
===================================================================
(Binary files differ)
Property changes on: puppetor/trunk/doc/howto.dvi
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:mime-type
+ application/octet-stream
Added: puppetor/trunk/doc/howto.pdf
===================================================================
--- puppetor/trunk/doc/howto.pdf 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/doc/howto.pdf 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,1159 @@
+%PDF-1.2
+9 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F1
+/FontDescriptor 8 0 R
+/BaseFont/XEEAJR+CMR17
+/FirstChar 33
+/LastChar 196
+/Widths[249.6 458.6 772.1 458.6 772.1 719.8 249.6 354.1 354.1 458.6 719.8 249.6 301.9
+249.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 249.6 249.6
+249.6 719.8 432.5 432.5 719.8 693.3 654.3 667.6 706.6 628.2 602.1 726.3 693.3 327.6
+471.5 719.4 576 850 693.3 719.8 628.2 719.8 680.5 510.9 667.6 693.3 693.3 954.5 693.3
+693.3 563.1 249.6 458.6 249.6 458.6 249.6 249.6 458.6 510.9 406.4 510.9 406.4 275.8
+458.6 510.9 249.6 275.8 484.7 249.6 772.1 510.9 458.6 510.9 484.7 354.1 359.4 354.1
+510.9 484.7 667.6 484.7 484.7 406.4 458.6 917.2 458.6 458.6 458.6 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 576 772.1 719.8 641.1 615.3 693.3
+667.6 719.8 667.6 719.8 0 0 667.6 525.4 499.3 499.3 748.9 748.9 249.6 275.8 458.6
+458.6 458.6 458.6 458.6 693.3 406.4 458.6 667.6 719.8 458.6 837.2 941.7 719.8 249.6
+458.6]
+>>
+endobj
+12 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F2
+/FontDescriptor 11 0 R
+/BaseFont/NJEYML+CMR12
+/FirstChar 33
+/LastChar 196
+/Widths[272 489.6 816 489.6 816 761.6 272 380.8 380.8 489.6 761.6 272 326.4 272 489.6
+489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 272 272 272 761.6 462.4
+462.4 761.6 734 693.4 707.2 747.8 666.2 639 768.3 734 353.2 503 761.2 611.8 897.2
+734 761.6 666.2 761.6 720.6 544 707.2 734 734 1006 734 734 598.4 272 489.6 272 489.6
+272 272 489.6 544 435.2 544 435.2 299.2 489.6 544 272 299.2 516.8 272 816 544 489.6
+544 516.8 380.8 386.2 380.8 544 516.8 707.2 516.8 516.8 435.2 489.6 979.2 489.6 489.6
+489.6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 611.8 816
+761.6 679.6 652.8 734 707.2 761.6 707.2 761.6 0 0 707.2 571.2 544 544 816 816 272
+299.2 489.6 489.6 489.6 489.6 489.6 734 435.2 489.6 707.2 761.6 489.6 883.8 992.6
+761.6 272 489.6]
+>>
+endobj
+15 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F3
+/FontDescriptor 14 0 R
+/BaseFont/SFIZFA+CMBX12
+/FirstChar 33
+/LastChar 196
+/Widths[342.6 581 937.5 562.5 937.5 875 312.5 437.5 437.5 562.5 875 312.5 375 312.5
+562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 312.5 312.5 342.6
+875 531.3 531.3 875 849.5 799.8 812.5 862.3 738.4 707.2 884.3 879.6 419 581 880.8
+675.9 1067.1 879.6 844.9 768.5 844.9 839.1 625 782.4 864.6 849.5 1162 849.5 849.5
+687.5 312.5 581 312.5 562.5 312.5 312.5 546.9 625 500 625 513.3 343.8 562.5 625 312.5
+343.8 593.8 312.5 937.5 625 562.5 625 593.8 459.5 443.8 437.5 625 593.8 812.5 593.8
+593.8 500 562.5 1125 562.5 562.5 562.5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 675.9 937.5 875 787 750 879.6 812.5 875 812.5 875 0 0 812.5
+656.3 625 625 937.5 937.5 312.5 343.8 562.5 562.5 562.5 562.5 562.5 849.5 500 574.1
+812.5 875 562.5 1018.5 1143.5 875 312.5 562.5]
+>>
+endobj
+18 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F4
+/FontDescriptor 17 0 R
+/BaseFont/XCHVXE+CMR10
+/FirstChar 33
+/LastChar 196
+/Widths[277.8 500 833.3 500 833.3 777.8 277.8 388.9 388.9 500 777.8 277.8 333.3 277.8
+500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 277.8 777.8 472.2 472.2 777.8
+750 708.3 722.2 763.9 680.6 652.8 784.7 750 361.1 513.9 777.8 625 916.7 750 777.8
+680.6 777.8 736.1 555.6 722.2 750 750 1027.8 750 750 611.1 277.8 500 277.8 500 277.8
+277.8 500 555.6 444.4 555.6 444.4 305.6 500 555.6 277.8 305.6 527.8 277.8 833.3 555.6
+500 555.6 527.8 391.7 394.4 388.9 555.6 527.8 722.2 527.8 527.8 444.4 500 1000 500
+500 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 625 833.3
+777.8 694.4 666.7 750 722.2 777.8 722.2 777.8 0 0 722.2 583.3 555.6 555.6 833.3 833.3
+277.8 305.6 500 500 500 500 500 750 444.4 500 722.2 777.8 500 902.8 1013.9 777.8
+277.8 500]
+>>
+endobj
+21 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F5
+/FontDescriptor 20 0 R
+/BaseFont/GCOOBE+CMR7
+/FirstChar 33
+/LastChar 196
+/Widths[323.4 569.4 938.5 569.4 938.5 877 323.4 446.4 446.4 569.4 877 323.4 384.9
+323.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 323.4 323.4
+323.4 877 538.7 538.7 877 843.3 798.6 815.5 860.1 767.9 737.1 883.9 843.3 412.7 583.3
+874 706.4 1027.8 843.3 877 767.9 877 829.4 631 815.5 843.3 843.3 1150.8 843.3 843.3
+692.5 323.4 569.4 323.4 569.4 323.4 323.4 569.4 631 507.9 631 507.9 354.2 569.4 631
+323.4 354.2 600.2 323.4 938.5 631 569.4 631 600.2 446.4 452.6 446.4 631 600.2 815.5
+600.2 600.2 507.9 569.4 1138.9 569.4 569.4 569.4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 706.4 938.5 877 781.8 754 843.3 815.5 877 815.5
+877 0 0 815.5 677.6 646.8 646.8 970.2 970.2 323.4 354.2 569.4 569.4 569.4 569.4 569.4
+843.3 507.9 569.4 815.5 877 569.4 1013.9 1136.9 877 323.4 569.4]
+>>
+endobj
+24 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F6
+/FontDescriptor 23 0 R
+/BaseFont/DMIVYN+CMR6
+/FirstChar 33
+/LastChar 196
+/Widths[351.8 611.1 1000 611.1 1000 935.2 351.8 481.5 481.5 611.1 935.2 351.8 416.7
+351.8 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 351.8 351.8
+351.8 935.2 578.7 578.7 935.2 896.3 850.9 870.4 915.7 818.5 786.1 941.7 896.3 442.6
+624.1 928.7 753.7 1090.7 896.3 935.2 818.5 935.2 883.3 675.9 870.4 896.3 896.3 1220.4
+896.3 896.3 740.7 351.8 611.1 351.8 611.1 351.8 351.8 611.1 675.9 546.3 675.9 546.3
+384.3 611.1 675.9 351.8 384.3 643.5 351.8 1000 675.9 611.1 675.9 643.5 481.5 488
+481.5 675.9 643.5 870.4 643.5 643.5 546.3 611.1 1222.2 611.1 611.1 611.1 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 753.7 1000 935.2 831.5
+805.5 896.3 870.4 935.2 870.4 935.2 0 0 870.4 736.1 703.7 703.7 1055.5 1055.5 351.8
+384.3 611.1 611.1 611.1 611.1 611.1 896.3 546.3 611.1 870.4 935.2 611.1 1077.8 1207.4
+935.2 351.8 611.1]
+>>
+endobj
+27 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F7
+/FontDescriptor 26 0 R
+/BaseFont/MAMUVX+CMR8
+/FirstChar 33
+/LastChar 196
+/Widths[295.1 531.3 885.4 531.3 885.4 826.4 295.1 413.2 413.2 531.3 826.4 295.1 354.2
+295.1 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 295.1 295.1
+295.1 826.4 501.7 501.7 826.4 795.8 752.1 767.4 811.1 722.6 693.1 833.5 795.8 382.6
+545.5 825.4 663.6 972.9 795.8 826.4 722.6 826.4 781.6 590.3 767.4 795.8 795.8 1091
+795.8 795.8 649.3 295.1 531.3 295.1 531.3 295.1 295.1 531.3 590.3 472.2 590.3 472.2
+324.7 531.3 590.3 295.1 324.7 560.8 295.1 885.4 590.3 531.3 590.3 560.8 414.1 419.1
+413.2 590.3 560.8 767.4 560.8 560.8 472.2 531.3 1062.5 531.3 531.3 531.3 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 663.6 885.4 826.4 736.8
+708.3 795.8 767.4 826.4 767.4 826.4 0 0 767.4 619.8 590.3 590.3 885.4 885.4 295.1
+324.7 531.3 531.3 531.3 531.3 531.3 795.8 472.2 531.3 767.4 826.4 531.3 958.7 1076.8
+826.4 295.1 531.3]
+>>
+endobj
+29 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 2118
+>>
+stream
+xÚXK㶾çWðfªj%+ºí|Ëô¸&¿u'äZz7Ëy
QE¹>îßMÌþóÒÙ>|2eÝêZ(Bêö|ÞyêÂ4ûAõ#=uÃáÓW5øn2¥ÊaU¹e¹ÏëáÇØ'T©ÉïÎÍù>[¨¢J¶¹Q|Þlµ}6V§s+áÛ.»¹Þ'jJÜvµYµB£ÖFúáSª©;·ÊÚÄÒÈMw§UJd{]ï&ÔL~*rB
N(R¦ûàNþ©cøÌùèfYt»®ïf7û÷ÏGÏÝ8üaê°G:qܹlòó^¥dú:ÝC¥ÈT^ùØq -èl)xèÝOÝ2º¡ÅA- ¾úÝ""¹
+y<h;óÄ;%ôY6Áa÷öRµÙæY>)ÈÐS7Y»aCÃÄalÁ·`Ä¿'÷ù¼¬ÎLõnê|àñ~Aë¿k?JwÅÜàúË<O$>¾ÛÙÁY ¼5¼tspáò¨Æ?ìyE=(l¡ïfHÌ3LX4»-4Iß(°/Äg6!QúÝ<1}ßݼytÃÁO|x<E6ê\=.a''îF²DëÕÜdjä8ü
>±uÃ+<À=³yCwòì÷WvnXKVd/ì>à°yÚ,ù¿nàexî_ÜÃ+85Ób+vbèØÄZ±Z)ÒXC ¼7ø/\q}Þ!QhÉVÅæÍce,êZlGNãã)Ö:$Bxâh+©Ô¸Óe`XãÐQ¢@"./´ñè·""é*ý/£þé[ÏíÛ@0Gno).Ü÷J ]»#§@ëo_µJ¸Â%¦Ô{ÐÂ02ö½@ç*+n@bïÐVihÉ55ÊÔê¤.º8¸ùzäÚGËã ©Ëëúzìn5ÓJIÃÑ]ICma@YÜ/ý÷=FUÐÎk^ºlw¨1ßëÐlVPðs¦æf¾Ó/É}ÆÕu§Ì{Ù,Î!«¦ÕÁ×3®}rsÄ>IöMcÖ$ðØÛäÝ1Ôbueí!MZÉ
ÛW/ìzOk#tÔ×$ÃÉ%äÆOÔsõÿÕ>qPL#·OÜáP+ 1ù/Ù+endstream
+endobj
+31 0 obj
+<<
+/F1 9 0 R
+/F2 12 0 R
+/F3 15 0 R
+/F4 18 0 R
+/F5 21 0 R
+/F6 24 0 R
+/F7 27 0 R
+>>
+endobj
+6 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 31 0 R
+>>
+endobj
+36 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F8
+/FontDescriptor 35 0 R
+/BaseFont/PQEIII+CMTT10
+/FirstChar 33
+/LastChar 196
+/Widths[525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525
+525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525
+525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525
+525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525
+525 525 525 525 525 525 525 525 525 525 525 525 525 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 525 525 525 525 525 525 525 525 525 525 0 0 525
+525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525
+525 525]
+>>
+endobj
+37 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 2329
+>>
+stream
+xÚÍXKã6¾ï¯ÐQÚD½s :ÉÌîØd°c`¤÷ K´ÍmYòêÑÎO½(ÉîÎ1ÀQÅb±XõÕòÞÑ×ßïwß|½B©·;xyªÒÄÛZe·ûñ7·É#¿ë7Û(ÏüÖù×ÎöÌÄqûU×ÖS52õ²Ùé].ÛÊ0ËÙÃÔ³iQÎ8<l¶qøçr£SÿukR_XmËïåíËB¦Óæ?»¼ÀÛ¡*XÅî+ >Êgr
0Î4B
L,Ü¡@Áu¿íùÒÌYe¯m
§¾kíïλ ×"±».±ù¾ÄnqèjúÓÞÙkþØ;½a¼¤Dø@¢Y+34÷w})0;Âa'Vüj»v»Ø8Ödcáàx>UÙ> <°3MÙc8¨»Öà(A\öpæÚ%¹ß²f^R
+×+A`ö,>X§çh%ÌRÿÃ|ätO?gtNHö#áÓÉ)Ò$ué#¥ôCßy4³§D@ÉiL%pnÁÙôGYÒtGHD´)vTü.Û÷A
+ÆÃó/*RXswèa.ÐúH
qª,!<_gXÊÊåN9Ú,
Êã©y´aÒÊz¶ÇCäMnèÖb(ØR#}jSöÆ9Ð;Ð
+
ÀäÉ!+Qùöð*kK(ð² 6-°UÖüiïíÑdgq2Å)9þ¦z"X¨zâÜeÖag°GÆ÷
+ë>)æ«+MÐrÆÉHr.}¿äl]ÓPïÓØJsùGº÷+)¿Ú"ðè8{LãB{üü
\OýÍt¨ÃoÍÌØé¸Wñ%NÜÚteÍ%P»¢ÅK}û2ífÔJ»¨ÓÖ5tKuW Þà6tä/UEæÄÄTÚñ½? p´D%Ã#$¦¤r~£"rpbÎøKdCKB÷$°3µïâ;ß-Ëfî=c·Ú÷³#¶q¦%
ÇÙ]Ï$ÁFLQÿ¼á+'ñjÊt.w1e
+~SÖç)áiA,.M§X%Àn+Q¨<Y] £²!ö4ÐDÄþ8$Z¢ã/¹ßPÜÁæùèÇÄ)-×G©vÅ>8߸Ó9q"¥ä×m8"å_Ôò³p~hùzÀ1uðÅÉíµÿí µtÅTdêÛ(Z²0Üäp·-÷h¿'¹ãK,Ê6éÇð6_K¼Ø·×6,ªæÝ+sÓpênYC6?ñÆ÷ÐqÁ1
+þ[¾iÈÛê½eS¡×çZ»/ÕÕV5ÃÅäbÃ(f½Õc
%¨òyÚCaú·ÙRnú_àÙe%ú¯R£ýFüº¡/ÜPì:ÖTùÙW÷{¿\#¨³n´C#ýßëQ¸ÝÝ)øaç*ö®^âÁmBávöÂPÚ}6Þúz*rd¦UãtAÁRk2/ì«äiWý7º k&\=8ùÃàÜ-ÁWkëºR.ßÅ|Ë7èg¬m3A~à?X¢¡_Ã+¼¢Ù÷D×åB®Ë0IÕbæß8ªP9ê©JÒ1^3âT9·¢¬9wÃtÃ!k¯4Ï0ÈIµÚPZÐ=LÏI»:¤Wõ|°ºæÕÔ.eòWý$²ü
[ÄÀSß/fÎôÊðqg]é}æÎê*®¾ÿ,QÐëÒAw¦h¹$1[5w3 õ0±6+Í=QÇùÝÓFJ|Ç|,x±*2ß[iäÇÃ}À|Ç\PPQTZ¬+â;åþiÇÅD~îÀûØq²NâYÇ=Îw[þò,ëæÚu_Cè0¶/TKÍ!ú·?+endstream
+endobj
+38 0 obj
+<<
+/F4 18 0 R
+/F3 15 0 R
+/F5 21 0 R
+/F8 36 0 R
+/F6 24 0 R
+/F7 27 0 R
+>>
+endobj
+33 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 38 0 R
+>>
+endobj
+43 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F9
+/FontDescriptor 42 0 R
+/BaseFont/NENDNC+CMSY10
+/FirstChar 33
+/LastChar 196
+/Widths[1000 500 500 1000 1000 1000 777.8 1000 1000 611.1 611.1 1000 1000 1000 777.8
+275 1000 666.7 666.7 888.9 888.9 0 0 555.6 555.6 666.7 500 722.2 722.2 777.8 777.8
+611.1 798.5 656.8 526.5 771.4 527.8 718.7 594.9 844.5 544.5 677.8 762 689.7 1200.9
+820.5 796.1 695.6 816.7 847.5 605.6 544.6 625.8 612.8 987.8 713.3 668.3 724.7 666.7
+666.7 666.7 666.7 666.7 611.1 611.1 444.4 444.4 444.4 444.4 500 500 388.9 388.9 277.8
+500 500 611.1 500 277.8 833.3 750 833.3 416.7 666.7 666.7 777.8 777.8 444.4 444.4
+444.4 611.1 777.8 777.8 777.8 777.8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 777.8 277.8 777.8 500 777.8 500 777.8 777.8 777.8 777.8 0 0 777.8
+777.8 777.8 1000 500 500 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8
+777.8 777.8 1000 1000 777.8 777.8 1000 777.8]
+>>
+endobj
+44 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 2104
+>>
+stream
+xÚXKã6¾çW}´½³È!ÉÎ$Ù²¦s÷ ¶i[hYôT{üï·^Õê`.UU®"ëñUQAÅqpèñkðóò É,öADU¬Ëÿü+ÌVë$IðýötîÕjey¼[ó:ÚnµÝp@rÇÇÛÈçUê_²Ð*ó¼ÊâPfêUÏøÿ>ü+Hó(
ÖI5Y}X5Y¨÷Ã<h¢¦ÄÝY.M¢:a¹#î(Kíuø
UZ
í*Ã+sfÆh/V5é'ngÈRiØzX¥ex=éÑö¤" [:*¶üÿù¼ªå¸ðBÇ-(ò¶ 6ð12v-Ùï\%§áN3Øgîèå7iQ+QÔC|nýbr¸£:E'&nõº£i]§h;ëÄȳ½§x°AÐhÓ{¦ë'QÑwOä~Ãc¼ã¸6S\Ñ-°NiÏ´L§gLì?Fu_®ì¦KçÓÆÑ}ÎèYgÞq¬Ê©P[g¸Ê³¨\«¢ª Ö¿ãmLüø_þý73äN9ëµJçgûVxAñb¯ûÎs*ºGMÅi¹ïe
Ôâ7w4z<ÚèúBÂtL,\aêàsPŹöÿó dçËDeßfKw×?Ùksò11ê£òë$Á[æÝÀ#USö²I^ÒÞ>c1£
¦
+k)Mí3úÑÝÀ0Ã]OáN @y#ùõr¬EJäqTøì03lÅäåÛý1òXáCÃb«d;Ób^6exîÛðöq}pªfîqÕNZÁ NÜ>wËÄMòâ+eZ¥Ryb .ç3$|àW;ê6åWU
Ñ3
+É+ܳ"½wØ9/ð|º;¼.XçwÔM_^Ijß+}<F-Ðäú<«ç®®Ox°Äx+ ·z|>R{[
SµW}Íò+Ê[Iïj`Læ)`S!&îÛÚÂ<´ 8¿¥y}ÀÙËx6 ¥®øÞJO @Req|sJBV
ÚªÞ
+©3Û>ö=J0|áªÀÔêWXÍøìÛqàÄZV¤¼¡@,^qÆ:Óð½àl;Àë ,Íg£EñwjM@XO¸Áa+µJxè#ÔÑL1ÊÏiôëÚÞ(Ym¨Ò*ÞHöFÙ¿T©Îðr\(Þ
8RJ© r l[nk-¡æ\ÿãØõ")ÑXWiþ¦Àmo àM
Ü&þàP#³ùíÓwL²Ýaà»F¾Q±oÍD$6ÞÌÈdPx¥1M«¾c£<y'Uîu¹lc®T8OU@hù!Ò~*óSãïÄ%Ýä5Ý>TÄ%Àçªg7YâY¨ÙóÎÿ®ÞKþvÄýI®^Ú_^õ±ù]÷¦{í'ùw¥ÂÛÅ9@O¨Û üxkÖyãùÓ+endstream
+endobj
+45 0 obj
+<<
+/F3 15 0 R
+/F4 18 0 R
+/F9 43 0 R
+/F5 21 0 R
+/F8 36 0 R
+/F6 24 0 R
+/F7 27 0 R
+>>
+endobj
+40 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 45 0 R
+>>
+endobj
+48 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 2186
+>>
+stream
+xÚXÝsã¸
ï_áÙ'y&æJ$õÕÎ=ävm®{Ùtãö¦Swn±ÕÈOëÍtú¿ H}ÙéE/AAàG@¹îb·ÐÍÅ÷7r³8XlaÀx±
+8ýÅæã?»jÉCç´\ Áª«ÎfA¯&n©Ú%`÷LÌÁV:uWy¹cËm~Z¸ç±ØHVßÛåJúBÏÅÝ+"Ò"W%îÛÚÄP§¼(:.W°ªªú@ZýÖ¦5¶%R«-}ék}ï/ËH8l¹\áÜwG:Ê
+5]yÅr¬nZFI^Â<ðg¾¿zãɹÓ?àÚëã±ÈÓÍa±<X:ö/Íä´H-¬¸ÏSÜgO{¤IIü¦MêÈv_«$£imE¼©Qôºüp,9õçÍæîÉOë
ñÈÈFÁÈM¨ÙåßP9¸èNeµj3¥Ì sVuö¹çüÖV$ÏÜÀ<3¼¥ö·ûÑVS&uî4OÚé¤tj·ÓcWÓÉF5WÈÉppP;ZIéJRWS%9ÏÔíFY'±_>üåáêi¬w õî#§wÁ?ÎçGG¯¸ÒÀhþ¾DKÁZEë¶Î;ùîJ«Â]ó±ßN'¶«ª]¡X¦`Nä^ÌÐuùvù§¹Ë|Gc-Tpú"9(ÝoÍq'sBÐhðn°vKò5\²ËVÕÔBY×Ã
L«ÙR8Ouu°¢¼Å=² sÀÙì5R+@¦à.IýBìÄâ¹úؽ{8//Í$juLj¸êÙODk¹úc¢àyWW]]!Yx¤ð¤+ÞvRB;>©ÎN:æöªÝþÇT+ÆðU[çGJiKÁ-uKBÿÐüÿü`|£ÜàÂÀsRèÙ,Ù:+& ð°ÁÈå%7FÔãÒi+·ËzqAÎdÉØC-AToè³tb
Blà;ÿVéïÑÁgþNqsõj½Å7y þ¤ËJÄcÑã-qÑ<ÛâñÒKÍfÃÇîªÉ45.?u8vÇFY IK¼äxÈÐë<%y¡Ú3ëj¨²¬íÔðÄùëäVxf·ùJf Õ Ò}ÌYg;¹[²ÎíÆÚmÎ ÌãP+QGÃ)´Ü+ÎæÒÁ=qI%=lêXÑHç
%ÄMUm2!ïìaÀiúøz~ªÌ¥ìÂBL)9çÆ^Htã¼O/¸Ípû
+ÈÑ(ʾ#cnÏ:º~ÆÙè)8}t
+ÚÏl2µã¼rr#&å
+?f!T
+>"÷± 8üaWúãZça^SøLÄIÒe&nÌ5âî×_o¾|ýù¼¦ÀHÑ?Gõqcñi¸¥i¦#¿[Ã[þ¶×øxSÕ×åË4ít&8<Ȭ5¸lâ_m~íOõ_W×öϤdºö5
+p½þbF2ú^ÒXÚéßyÁßy«3ì³yZ³ïÚ:wU¦ËYZ3Ç«¸VÿÃÿ+endstream
+endobj
+49 0 obj
+<<
+/F4 18 0 R
+/F8 36 0 R
+>>
+endobj
+47 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 49 0 R
+>>
+endobj
+52 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 1949
+>>
+stream
+xÚXKsÛ8¾ï¯PåDME ¯lí!gãLåÚÃj°K¡H
=Sóß
Pe'>h4èà,da8ÛÌÌçß·ËWÄ,LÈÙòvD,gãgùþ¿/¢ÇÁÅÚí+=_ð8øëùB4xSÜÍEè¶/»²ÞÀÌmYº&ÆN·wåÚîêúîÅüËg\2>[D)Ë9«*×ó
Hã`9ÏEÐ8A{<ä0Ð~ǯ>ÈYÎòo¼ )<bYd¤,·p#8|ÝÔÅB«®Ô]4h§2ñbøU OIËèãtB®Q'\p[{:£rF
\\Î3j ®!(±@Eà Ãg¨ÒKP4Ê¡ÇAÔì°îsÛ;¢¨7>à$ZýÇ+Æýjò/úX]MûÀZÚWÁiøÕüçpÿð¹)4ÉÚãôDº=Ñ5ì Ôð½xI,èÇ!·çøçð¥"«Ô=&B#Ì):Äè¿ï5ZéÈ`pH0FÉ -JºAãÙ"1uÂø:a¢M*ëÛtIöÁ)Kq#½Æ`ÆQïxkµÓSÛFqÂDfÃ])\2badY[Èâp¿-äT*@4¤FJÊàëJw¾lfB7å§Lä~kµñ¬
S+(U³Ù îvo¨é4$ÅEÈà5}OK ®#J¿U= ±5)
ÃTE$¯vAâ,+KnH`çGbb´FÕUµmºþµÉGøRÁï%Å4AU×Í+ÍçïMG0`×êÓF_a8ÔY)£c¸Âòºªæa¨JË¥Ã,
=v *ÓÒ|ÕUæ;ÇVýàÎcßë¡×lÌÿ®|ú³ã{8¸ÿCýáxÒbË-´+3+endstream
+endobj
+53 0 obj
+<<
+/F3 15 0 R
+/F4 18 0 R
+/F9 43 0 R
+/F8 36 0 R
+>>
+endobj
+51 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 53 0 R
+>>
+endobj
+56 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 1880
+>>
+stream
+xÚXYsÛ6~ï¯Ðä¥TÇbI¼ÒéC;I&Z>Ô,Â&©ò°ìüúîÅCÎ/"°X,öÂÍ?f·3ú¼=_þxÍBíGz¶¼%¡ÆE¢ð³|ùÏa¨bïüÞlw;_DZyÑÓùBgÚ{VÜÍÀuëWÞò¢)¬V¶éévEaËùË_fJûj¶S?èÆÖwn
ÂÓØP$ÈU )ÚÛu×·âÕå<W^%¥mçQèíñ§ª?£ì/ô,÷óYð9*ô³ÎY®];³Ük×F5sÐ~°&´ßùnÌeJ»+§u#ÔR÷kÐP¥Þz/nj³µ{\B
i±ªÝ+ÍfóÀ,kÓ[ðIèçìøëùöXwÝMëÛÌ@UÂüÖì_¬ª²qMÛð¬ºáo'Ãñø»ºBҳĪ¦å%SÛATiW-Dlmûs% ':/çYÄÁQ9g8»þ|T¬áEø\%b`%"+f¤A5³YHnQyuÕÅÝyZ·¥t½Ñ0M"=ú?ê¯mãN!¹>ÈCt Lo`xn»Ú®*bÃ0£ÆrÔCIÓÈXô&0ÁÆME>«>÷ò
·á}Ê
±"®T¢A
¾-ÂH<ÙÀr?ê@×ûðXJ¤ý$½k×ùpuµCɪny=
-#ïò÷<<eVþ±yTûAø¿Øþ+ÃccøØx9Á§ýMÏ3H¦µeÃLR\èdðsÌ#RNvÔöÔVèFèý%Âqst8}äéËþt
+ý(kNý¡a5úþx1A>¾(¦º@>&¢ù¢¦0%j¢vÆLÎXÔöèFßÍIüI®e;×0ÌW¾:0)&%&MkjRÇnë6¦æ A~ûm»ÚÞªk"ù
£àå¢Ò~Ázà¤À $à.E¸ÓI0)´>¸.ÊöéCV%áØQéDq:Âw\JH`£<OT) ©2¥ÅÁÀ#nÇÙ1ré'`°¯]Û2oÞ$g;RmS ¬,vnÃ@³Ätª 16XvËKk3fS¨Wz´®+q×=äJáC=W2+Cw»ejËÒ¯K& äp²fÙh¶©SI´xÆ5¦¼O8ïq*Ç9éá¾(ø X¨$ñÞ`7½5Òí6)ð©$Å+ÄÚ
ýBe,¡=à\¾f±ÅÞ÷¡îH'õÁÕn¯K¯
+eâ~uÚ>3PÂÎðGª +Õ 4*Î`ú}À2ÚÔUÒÄø@ÎÝ;,ôÈ2E ¦ò4ìv×Ê]×:õ^/yôê|ñ¾ÎäH=i¾õ´ùþ¾á}Uéx\Ϩ<ÓÐÈBdÞð×ðçkð
¿Ë|5¼Qÿ<Öc+endstream
+endobj
+57 0 obj
+<<
+/F3 15 0 R
+/F4 18 0 R
+/F9 43 0 R
+/F8 36 0 R
+>>
+endobj
+55 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 57 0 R
+>>
+endobj
+60 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 1907
+>>
+stream
+xÚXÝoÛ6ß_aôIbE"õÙaiëlº$K¼Ã2ÄÄBeÉÓG`Øÿ¾û dÉr»<<ïx¿;Þ5slÇ=ÎèóãìÝêô<ÅvÌV³ÀC¶~Vþ´nuõí6ÏÕde1_øÂj"óøþºÙÕ;©´j4ïÞ0çf
+Ç?ÿkõóÌ-\צÊGf*Ðs¦®ëmU>¿¸ö£n®, d¥¨ðÍQÑïóLÍÄÈß0÷tæìLî ÇÅPcä¯æºÂéôÜëQ^øvÍ6¸´á|I«<%lJà"´æ"²tªÍ;$1Ç
+?à+¤ ÕfõÚ°ðÀ"Î
ã,<.Î_3Ï=^x½Yë¬brû^¶0mö|áõi DtT4¬ÍÒà¸F§¼¼i฿*"F':Ä|+.7ÛÍÜÈ
y¶³
<[Æ=!ÝÂ=Q=Á×byùáâòÇ88!<º¶cqCªw³üõ·åíê0el±1e·Î4<&ØÊ+¤í{Yj/ë8øÚPèF9ôA02ðUrDÜQðÃ`ÞB¿Ø¿kUó`¸ÑÄJoóL_Ê ÿþT¢À. z(
+ûr»
+ÓaãÜpé:=bò¯xÖéRCLV ½A]=}àé~cØm¬ËJt}ÂC$p«,Tax9ÃHáõ±?Ì`)÷G8ú2¾ãÁåÎ3ùþò×mü;-U¨G8«JÓa|ÿ?ÚÑäër¹_z½ÜaX¼Gñ²D¬w}´o$ÄcQ7UwbÅa0̸
(2QÁ<&& 4I¹¡Ì,&¯ÔfüÝ""DÑ]C³åÊQ
GaÝö B©SôµøáÏ0Ú|ptý'0ºöuÝÜ;Ë7à{¾ã8f`é×+AÍ°¼·z#Ï:KæÒPl²ãÜë2Hw]ZðIÁ*tapÆS±Q5¡D Wð%=xÕ@!\ûy ¬æ±y
À,û¹töe<+$"F~Eá¸ç
+«<to\Ä%]PjTÅM·ySv91EÆ>µøHÍ'Jh9¥eÕi×n]ñ,
v-ÜÆÕþ
Õ¹°êÕD·Ü+JTÜ
¬¥TÔ¬^xm<ÜÃ+endstream
+endobj
+61 0 obj
+<<
+/F8 36 0 R
+/F4 18 0 R
+/F3 15 0 R
+>>
+endobj
+59 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 61 0 R
+>>
+endobj
+64 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 1744
+>>
+stream
+xÚWKsÛ6¾÷Whr¢fL@j§$migD·ºF,%R%);Îï¾(>$ÇNíÉ Àb±Ø]ì~»
AÎf4ü1{½|ùv1[;[®g
ìbæ[$ñlùÛßÞ²jþÏòÏoÍƶ?VÖÕÌ4g8¢©½´pÜífî«$ôVU9W×ÖÕ {R¯ª[\F^ªèIÆûôáÍ_îcRÂôá##qèThæ'*ãEÁíÍÊ|îk½¼¨y2K8l¶¦wO?A=§ÛÔ3¹M?ÆmúA·éMÿ8·égryÛÌn3OqyºÛ~+j·j«únê9m8ÉQôpì6é±Ût¤F*G¨rnÓQÒMGfè\&t;ÓÍÑßé¶ø¹M=ÛìcÜft=ã6ûX·Ù»íoÐRHTß,ÀâMÖâ,õ¶EӲḶ* à5æiBNÔî
+ÔNèÐ7E»çÍär¸zòxá0î+NPÅSwÄßã7ÛÂñÓãÅâY1ÝÕëªÞ5üªµû÷à¶aCSW<=Æ¿¸íH«Ð+ÝzáUx!xÅO= @Ã+P'a<4ÂÂÐòá?xvÝ
¥özlã=éò
+ÒøeeÐ86ÎÜm3ô<Îè§+úR£&Qmbô+endstream
+endobj
+65 0 obj
+<<
+/F9 43 0 R
+/F4 18 0 R
+/F8 36 0 R
+>>
+endobj
+63 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 65 0 R
+>>
+endobj
+68 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 1567
+>>
+stream
+xÚWÝsÛ6ß_áëËä]¬ê[Öv»Øk6×Îçú0ïrDÛÊKQqrûç#µ×í À +´Z³×r.Y¦<ZUc¤^(ÉV|õþN[õ/5HËaoGýÇ×ðLm
ül"¾¬%T7?ÐÜ[óî/(âÕÒÁB1¹ÕH¥2×Êù¾G¾ñ}k#QïUÚ~
µÄc\;ñøVÅ×H%NÀíLöÄEM¼ì9Õôó¦ÞÁ[É»$«Ñq;S÷alNT*j"+?LþX?ú^SÂMÌé0¦ÄzþBªµ©ÔàkÊÄW <<lÝ/s$²ZâÔø¦j*¤$3
eräÞ*÷ diÞ,ä|U{wh½³=Ë%6;ë]S¨tO³Lß*}u ¥ÖbÍÊjEÖå*-lË
+A7ºHãé'¢¶|kÓYè åúÖèz2¤²\ÂÃHsªÈbÃXP+5¥v7}¢ÂyÃÛ÷¨Sô´ñî(Î\·oá÷F@é'
ó>`¬ Ö
+i>ÒÑÐfo
+ýÝØ-Z߯«C&=vØKj@dðì%ßý>qèàs
+>¯GÅ vì~²WÉøMrLK&½&h¡Ù?dLqxäK$}|L1ÄÞîÏ+Bñ
¨¿oäS-æ
4Å5®~¬Òæ
;«HŪÍ_2cJá¹ÓÂZáÏO¯~ÇÏïþo"é;¹æñ5#Jì¯($mjꥧ#^°P#óê2>glá8¡µ5/@3_ cxÜõaªÏÊ×ß~©Ý °èRÏî#YÎ~ø:YsÍ
+endstream
+endobj
+69 0 obj
+<<
+/F4 18 0 R
+/F8 36 0 R
+/F3 15 0 R
+/F9 43 0 R
+>>
+endobj
+67 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 69 0 R
+>>
+endobj
+8 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-33 -250 945 749]
+/FontName/XEEAJR+CMR17
+/ItalicAngle 0
+/StemV 53
+/FontFile 7 0 R
+/Flags 4
+>>
+endobj
+7 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1095
+/Length2 4446
+/Length3 533
+/Length 5179
+>>
+stream
+xÚígXmÇiRBïE©H½÷"M@HR0TC&
+RD@4¥wé"EºPP¤#°ÑgßG÷y÷Ë^ûm¯ù2¿sÎý¿ÿsæÌ-Äoa%¡À!õpX? iIi( mj)+Õù!¡´4 éï\¤å¡2в ó Â<<ý+&GÁaXÀæçÄEà04`
£~A+D",P~pOÀöEþ#± öí®®¦%ø¯OúWÒÂúYùü-û³úKÿfb{ð¨@ÀQJRJJXH¼ÿõtùébá8
+K
+9y+ÇåÆVØû0
+ó½ã
+JÝho×{[¤ÜÐÆoïVôºÈ0+N:dt5DMRIô3ýcJpm¤ÛWÁKÆQO¼R9[ØÍü:ºVº°ÀòDaQfä{t¼ ,KU°ÜÜðô¿»÷î÷õ'ùá#lÔÆa×êjgÊi>ÑÐÏ?Ç0¥ÒyöY}ÄómE«CóX²d§æIæË5>¶Î~È/nlBµîxÈÞº)<´CY+f½¾%qÈoTxìRÌÖ ±s×miS¸{8üä(pväÞîD%?5lm7rbaâÝohúì¾\ãýÝ1ù]x.Õ=WÂÐcçã©ÍåÛYá½ÚuU!AYÞô»V8ûI[ð-$âTpðŸ´Öj)Y3#'aä²~iÞd!Öäæ
+Þ+ÜY0a0,õ³Í
è¬+nßÖFÁEË1FU½µã¼KÑ-C:±Ñ3W#ÚEh?y>Ñ1 _ÎzÊ#
¾#<¿ð%jæ¦ÑDÈsk¶w®býµz(8òòËw*¾m\¾ÃS"7èX[dÓ1)É]0X®åZläVÍü¾QÃ~¤÷XUÚx@Û»FE0!/`ÅÉÄ7©$ìçxÊ
+,/¸Æ}uob«ÅCìë§;y1þ#_ùl¨ãqk ¹|I».åíÁÂç\¡m÷ÖRçæýàHsß^Ú>o°$®ÊÆ_[t*i7|£¥'8Q»TÙ!Ì~:¾ç=éqÍB~¶¾Z¦Ù$
Z)RnéShi:mÉLßÕÛdÖLjÿÎÕÜ©ÅÂçOZI.«©² ¼
+|ÉçÌÄ!Lw¼mN)ñíÓñõ©yÄ©fJÂ2ÙKZrõç9<QR¸#Ú9sÝ«³¤ø¦4¦/½¿µ9ë|8ÑéáÛ'öñ÷¨ãÂÁ|©OÓå´y.]²á9.^lçbù'}3K /~äá¸i©xª¸æ§i¥èd<ÌéÐãNPPñU¥@ûV÷ÅM¾³åz_t{ÜOp·¿-°"{÷L²ÈHä
÷¨oØë¤úU,ªÞõR.}J?¼c±Hå
Zw^B²K>áÕ_ÐfÐÖ{í4¦yM²b×DÄõ²D¬r
+j88MA¿ØNÝÕ?=íóYÅäQ"{éÂ÷pn¡=ë¦7\äwlÍÔ>d÷u-Sú,Yô|_m]
+4a'yÕ"ÂaD¡8guKpqM±üöÕõ¼2%ÐïSÏ^º®ùå¸pÍF(¶ýÁIwÉTÛÝjÆî3®»ÎwKìdFQH¤·!õÁéïQg¯LêQ#Ì-Àý&çoðpù<T˳¾¥(>A<§)º6æÎ\UðØ$³vP41ÝBh©siæ¦Z?Êð-Î}¾½ÄÜi|åâG:ZÜÓnsÈ{Í-r¾k±<Æú¹¬BM^ÛÅàdQ©ÃFk,ôÝÌÚ,_ÏÖÝ´õÁù©Éå`aåtQñWöUEÆbÙ
BV7_äÅöÌMgfp*í«é³/´¨'ul#'WTù<2÷I¬?>òï0x2c2q6^Óõ#Dÿ%çvëSgèNCWY2ò±¿u£ÑWPvfÏÒ\Ý;ÏtòR¥ÉþâU
Ùu¦M5Ìuñ± ÓÕ,åy}ìÇÄ92(µ×õtòJ[UÒÉØ;Ci¥¾Eÿ»PEC^î»78])Z Ø 3BûAhÜßèÝØO«´ÐáÖæ2ßÍýö ßÂjÿn$P;*Îmäqdÿ;Ì-Êh_Te³bz5ÎÌÁN§¼è¼@t»aÌfÿ`CáÂç0¶RZóoº{ËÜÒàxÇmWù3Ô6j;©¢ë;J!Q çNá÷ýZÝ×7?ÜÖB=YûÈv±pÞÚæ`] ×ÂÀüh\ßÇ_voã=þ'ÓM%²gm¹õcUÁ÷F_jDÌ<æïVÆy6¶Nµ9
v¯Oy&=Å&@4êd_
.×OxBÔÚÊA
öåÇcÆ?¨¥y$(ót?]N«½,òqo®¿ùðÉædï´²
+X^RÀä,ÜoPÔe¡¼Y»½/Tò~Üló`¤üãzHz¯³ñôWêk5[fjʶL\q$×õúárH¢ñ>fúÒº:9²X½XÏý£mÜÍ[<váE¡Î&rÁiBuZÃNÝ×C
@Ëé§(é¾sù¨ñZ$I2»v /hq¼°kð5þ!ÄÆùú5,ù¹ëNB¤ºRn¬³°ìrnJècúÀÂÉ#ÿÞòPÖÀnïõüàqÍít<jIèËfwlÜV¼ËÃ8éáÏ×V1ÉÝÂyêÅàF6áOÓ-,¢!ÓÜIYó[·wé*Í8رbææ]jÆWÙr)Ïz\&aÜeyQI
Lj³E>Ù=¸=kýÃâuº·éÙÅÎBT×y1CÑÁMÎ&g$_¤^SXÌ"?õ^Z÷4`ÿð\U¬.w~Ìjpź95FÍâ"4ó¥F'¸M$¡a·¸LÈiÄÓXÔÉÍÁBùÛ>ÔÎÞ;Ïâå>¿¦( EÒY%nb.´5MfÛ+ï!%ÚS¦E26°ÓðÔÉÀY»Ò+^ëM®!báM;ao0[ØÒ}²û RØû£9'1äôU¶M0âùdÐmuöűUjjtïé§|*}Év ÍÏFU,©´°
âJòñâVRñzÜi
®ÏCíJ\2j{»v÷ò/ÍØ,:¹ö¤ð¼(§°]Ö¬h£¼ö¯t¼þÃÜóf×å9¨aw×0äo´ÚUçÒÊrÔhswÝìË$½`wåv?$¥âÝAçèw?ÿÈMS"âAiïíóªGe7]ßèJòx^-¹Ì#üüäË.ôöPtâûnjgYoXk¡Oé¬×bmïDISÆZ¼'ô¼W³Ú<7!°SdBnyÅS2úv?½mãýÍWUhàºçù!-ò2Ö² ¹ò7ÕEÕRÇnv¦ÐÒüg3ßÂLJÑ·°
L+endstream
+endobj
+11 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-34 -251 988 750]
+/FontName/NJEYML+CMR12
+/ItalicAngle 0
+/StemV 65
+/FontFile 10 0 R
+/Flags 4
+>>
+endobj
+10 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1091
+/Length2 4694
+/Length3 533
+/Length 5429
+>>
+stream
+xÚíg8k·Çh£GÔ(#!º1J0$ºD1ÁÌ0F2z¢ H½A0z!"ÑÞKÂì}Þsö{¾ë|;×y/Ïﯵîÿ½îõðñèßU¢Â4PH(Xªê%`1q+(ó§õpý×Ò#Úà(@ð)$¸¢.@(Ì+
+GBúжõ¦@Ò@o0Â@à$Da)@BS|ö(4à×}JIAv(Âöü§rr%\
+ú·$yÁпiq óø7Ø
ýHAî°G0ä_Ê
ÂN¶¿ üfô·0ᨠØo( 9üTøoHÈuþ
f]~C9 ñ7 ο!a#Ôo(A8ûoH°þ
é7$4êïó ?%ûþû4¨¨ °ÞR@Q B«äde2Òâ¾ÿ5Î wóÝQ#4W\\FNêÕÎ!1ü~AûÛà c aav´t&8±§ðþ½Ö«¦
£ú¦ÜõéLæÁ-*R±1§°Pgý½qïúQÉGÜá~ºòqÖì%SÌBöý³ûÒè%Ñ5æ@-°¨zp¦´·5)Ýô4ãÚÝÅÃV{:Òí×<-1nîÇÍ?]ÀAìFÔk'¿´9¨ C¾W
3²zVH0ú"$CÏwDý?Uk:UåÔd¾»²7¹¤©© ?h5ïÓu+±²°ûð{'µ'§"óú³¯Æ,Ù<vK:MISVö;¹àJ
~%
Æ[fä-¢
¦Ó#BR·wÚö¡q\R×-¯(6õwwq¯Ï§³¦Þ;ZR Ý7ö!¸eÅÚQËá#{Æ*c:~ÏhÖÍo¦K
³{'ÛÜ;¿úaJ~Æk¥<kÇ$þm2ó&Iö'ªòô¶tz³Æ%&~ê32þ¢áZ-ë_<êÞ~iÖZ£èÓ"°X¢Ïj5Vôýí×ÏÍ2åÒbØoSn,óJ
å%§ÿôè[Í Øc(D@Äüà2X ÏT¸eèÀÙFûªÀSçâÓuvçÊtÑLûÑÇL;òØ!Ùf>§µ34äd» ùd{³6§¿1Ûä*Ø'Õ?ÖÝÔÉ9*3ø{¾_j4#ì!`*3nLµX¤í¤ò´Ún\ÝbJ^ò@$yÿpÓÕÞU>>\òNppìèûev>»téîLX=n·9ÐäõrçØ&4S[çñÀåÔñ¶ëêC´AR§Riin+ÕdÙsV®+LæEßþ/¾óHµæÉ-@(ÉdººÎÇ,§Â~içÌC{¬öºNpAÞÉÖÒ?ïÊЧo´Ê8zCUg½±w¥KàYî§lõ$ÏöØæ|33ÅxË.#TÄûübaâulÌþ;éWã_wk¸5²¯ÓZ¼²®¾ðhÛ^:çûgc½¥D®¬[JÐuìU¢`fBÄ'ÿ{îâ[R4/öîºðÇ>§ûÒÏî=À[Û9GÉcóS_ÕMX/³¾Í£oqÜüÂUÏr² $Mü¢y¬òr%NÖ$ÇÔø~¹ýþdøÀôö`g¡ÊùüÌVªÖ'ñp¡Í'²ÞhôÉo÷×ËòµüC{ÜâÄO8é¹A]{,C®%x9ãRme95ÒZÕ.©ô±³xØXvÂm+¦2xñ~ØR©oS û(ß@
+Ñþ@9weÄ7û6sR)|ÉÈ(îÊàLhù:§¸tãùgµ}Pcî zò@æq¼;µ&«l.SIZf g Î
¤ ®FnEnZ÷-?hÚbG½È%³Fw[ÿ¬U½¤+I~Oÿ<\Ö1·Ð2~ÕXÝʧrÑX^C¥×åi«+Þ´þ4[2>¨,&Râæã
y
Ù#i" u%óVP çNVâë+s¹'=ôW·4¾ºð+#
}©å'KÔôß,xººZ
ó#è
¢m+G§VÕ},ËÌ÷©Ôî²ß?Τ_ÂÞ+ÿGÛð)_%¶b¡]ÍnV°Ê#Z¥Ð¦vªï)|Y%ïÉÔÄ6GkÏÖO¼üØAb¤
K=ÑÁE2õÂz¿ïb§¹ÀÛ9æs/ï÷å¿~ýaêMÒ{["¥ùÎ7°âËøÔ ËesQ]ëT¦
Î!Fò4ªj?¾çªãü$ËÀlE¬bò÷ã³Há·
+ÙDL¶ÂqÁ|û¶y^ÌÉå¢Þd&|QJ>í#³ò°Ên)
¦hº¤¦È÷:½Üîz#^¯Þ-mØÔ4÷4&ÈûALc ûÊK½éfê3ßv%
0ú¤n²îßò
^.xÉuSwéÜKJº=¥·<AD¯SÑ
©#ªâ58sQækîZáCËë{¹qZ
+h,D¡ä?àB$;Ê/§ïÓÊ.^ÐФrð¼4£üïö4ÛìÏJÂÅãÌx¹DÍ{ zjâÜI¡ÞÏk.7®q<W £ÓTM×d]¥ÞB31¸âI×
@E=-éAWT¯ìóPm~'ÖÒÔ+â\gnö<á
+ä´þá£æpìý 5uÑÚ+ô
çDòtÈJf<Øfüy&µFH(-ÜÉ,ºUG@ºÓÙ_:üva×àhì¢y¹A~'¶gýsê½ëscjÃ5 M¶í¯÷%»µPGLda\³
+
<Búj:´í«Íjõ´È0é«ìÛmùnß:[¥jf¤j+Ðñâ{04ÓT
+ºú³ Lzè2!|kS
YW2¦ÓYÓ[VÓ-[\àÄüäÍÒKÞ)KõN«kµÐo 3Tê¼@â=éeêHÙíY÷v£}MK>©~MôTý´YôTÕ
ùqÆbG]D$5ÜÔ¤»5æsò=Çþ4ÆÈÅͬ)ôÎd$Ô õw^ÊYê 6Õ§[÷Wù^Ë~©µ5oAnÆ
ýÂæÅ_üîpéî0ÆÀ[´öãsäIÅIm¯¼¬¡Äî¤å~+`?/dÃüÔ~nWº4ýÃl«nÑÐY$f
ÖJV@¤×+ª¬wÚz¤ÝëáP>ÇÜZ!?¿
+¸ XÑ&9(Ö_WUÞJJëÆZD5¿«WUròª)RðwT$
+ó¹äçãZ|Ã|Z*~±äk¶?¯{üEuÔÇÅç#W\õä`¬3î$Hé2¡øÈÓYí4©ÂBêÉN+±·»gpÞ-W¨Þ{!ufâx>G9jÖ^¶ÌlÙ}~õµ}ÈâÒü+ÔòhbgjSÚeûâ|[»]bù/6AüfT1
æÄLv,»~By®oz)öñ5z4,9È.¯o>ýVÃêÐ&=»sb«o]aéöT©TÀ|G?{s@vÞ#ávíMçkÅGÙs_èÃ*Ý;w¢)r8'CîVjÆ]p²èÖfµ!G«ßq~j1¼ØtËÞ,·GðÆ@ñs¦»»4°êò»Ú#uÜÏ.ß8úµIlhå¼-óiÃ/ióGü*V®bN
+0{QN¢´Øæè+ÈÒµQÕl¸áÚ¤aZÅgëé^ôT½Ü7NÚ\_;«Üß9ÐøDÿJ²å»µgý ¬ÚsX>ÒÃÈáy¦¹l£_×àQômc£äÊ®|âåÊscWHámóªôLí\È$̸/³·v)z#+G{uRûþ2]éx.CÌÎ9Ú.Qk/°²3h.fÓì)rõ¨¬oûÁæbê^^4ä}7ÿÚ»6±ì8ßõR¥ù¥6ëÇ_}Fr¦Û@gÑ*L¯]ÙZ/´RòL´Êö*©ð^(¾ÀYë\Wþ±ýò# êÀå ßE,òL°Ù* "ÂÔÄÚ_ye''ÝÍ[üwçùÚíZz9Go\>\scïób
M\_%.ñ3°Z»ÝÌNêÂqdº½0)ÿàuêE![ÂÕø<céÎIã>z9XÅ9®ý(})d}+[9¦)#æîBKç²À=ø|Ïp(8bÄut?¶ÐbÂãqÒæ]SئðQ]¯4öOÉÍëÿ
+ç8ôj>+q<áPÊËZ"U´HAs.æÍô)>Ù%¡j83L ³;çë¬Åú[ýü7óòçUQkÇ#@ïohã&ªÖcs§Ùòø$*±¤»=d,¦7ëúö{FF½A7?ºÚê=°Fø»Ùø©LÑùÈáoúÆÞFÃÉûW¤INôöN7©ÌSÖ2ÜZniYã÷)é~_PV&k-äN01æ/òg94Z#ÝêTéL
+6ìÕõN?BáÚnODvR
¿_Ùõ8ÃÏD¯»øñ³2©ßv »`¨» ݵ7 ¡±P=± åõï÷p
>÷eÇFO3ïf®¨t)vS½3ÈQÝS¥á$Êáîê5õì"o5V^Ño£1JöÕòæl)çjÇJi»m[õ¥@Ø
+ÙÕýþî¢j©!ÕçÇ+I=>µÕ{ ¦îþZktý¥áÛ7bàýÝcJ_MrH6òWJ±´·ëÛXýi\j69#¦´.x5¤õáÕLùú9}yõ\eD2vÝÀ}}eþtéDânòè~9#aÝ{mÃo1¶Ëò1%âÉdÕöxQ ¿-'ØñcÊÞàÓÞ
ÝÔ?"¥¤íÄ%(Ì@ea÷£9C®Ôq¿åñ~Já~
¾q):$§¼ÜA«óyY:¬$õoÄÇ5+endstream
+endobj
+14 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-53 -251 1139 750]
+/FontName/SFIZFA+CMBX12
+/ItalicAngle 0
+/StemV 109
+/FontFile 13 0 R
+/Flags 4
+>>
+endobj
+13 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1300
+/Length2 6348
+/Length3 533
+/Length 7162
+>>
+stream
+xÚíeXm×ïI¥»%r:DºD ArfÈa¡¤A:¤CAIénnABºÞû}nÝÏ¿ìcÛÇëËüÖ®õÿk×u\ÜìzP¤5L@
e+ñ©F°¿Í=DÂÿ:Ú
+nQDØÂa+
²ò"¼
ö(Ì+öG+HX³mèÙ5ìÄ°ý1v ÆýQuü1²ð?£ëô
1º?£ü1vþ1º¨?ð¯¶þIýn Æû±áñ1ïÐßóú§ûáC¤§73d æ0JcF äû¿& ì]Ü`êÊó#$$ß_Q
+C ÿõ:Á<9ÿf{Ìsy D´öØ÷XN,[&8ê¾Lê5îæÐ
õÃñ§ÂBõ§I/¶õ¢RÎyÙ^½Ðk95o÷òbú0GÇoÓß`º|"S½¡®2Á5ïÁj-L+rÄÍñÉç@sÐÎMìÕÝ/f Lz§+V"Vë±d `Í,ÓO·JPj¯O"ÔN¢!×G~cjëq#'ÍÏØTkæe£}ÛwÓËõ)S*¨~
úÝmÄ%¢¾eùÖ×yÎR½î¼æó(++s©%kÂ?*'J½ô+Ñ,6íMæº^©ãÕç¨2ÆYx:ÈØ#ý
Z£+¹g dÅÐÑ%|øg©E¹¸´sEÂÐc"»ÃO¸fÍ++%W¬òzñjÒßV'F«6Þ$Iî/«ó ë,7¦»Åò7¾¥pgØ;=Æßäûs×ðÖKO¢^u¿`±¯$Ò<b^ö¡éÔÄÚWëý¡-oþ^ËqsðÞÝÈr~3³ü`¬z¡ÍìeUAãðtìùÏåëÙBÃeUñÜÜ5[o`c~Q¶Üz`¦! Ì$,uºêÕ*_h³éi±vÚ(¢Z§*Ðð~r~©¤¡ÀzçeL¨l¹a"÷{:e/U|òîAé½üP:2£[.Pµ%&²ëðcèRóüÌ& X3¨+
ÿG\Xíç®ÃÈýÂjþz®ãZøüÇÍ÷1Jðr(ØÁRm.¤XÔ,]î|pge÷g¯Hg¤ÏòÄÞ-¬¥¬YHM÷g2ÞoùC÷©ï|âÇ5j°2yW${ù¤ôIfå/¿P$F_Oz,ìàw`»ÓYf÷Iô¶ÆôziãQ-ôV\{dZÆßßu]-¤0'EÈY{®CÞÝlÛTÚâtø¶³<j\ðy¼W4IJÈñUÙIÀÜ1ÙUá~=o÷òw:9Å}Àj£±ÝÕ1[Ú1¾*_y&g+sãéý2¡0¨®Ö7Dñ"·1¹þÖÙlÐóùÃýtëïl >ýØ\nß
KE:3p£ÌDÌåN( ÜB°u®¹JÆGì5v;^û¨ñBúA/=&å°x«ãJæ¸!¼RwÔwú&Aú7¥[ªMôØx}*9BÈÄbn¯ùµh»ª+ÆH:]ÅǪ}A¦RÐó|«ly®*rØtsT«!ÇïNóÜ0PPyDK¼¢/¦>£©©ý Ç}Ý_»3Pp;´Y=¿ßp¾ÃààÒoMjÉ0®"=ªÃéúùWäâ±p'Ùø(+û
GùÐËKf¤ fÆhXE.%¸Ã;hGÒööª8ÚyI{óúÖÑÔo¬9 äMôø²·S?c7g¶å:ryëO¥0¡7
+ÚdBhOxOÎûY:)Eï2à*?J»UÎzÏtñâ\Lí9°DjoþµH[TÛ©ÁxÙ_óêdÒ`½åýw¢G%ÌJW,3R©ØeP:@¯åµîÇÆͯ+e³÷V¦ÝÃT£/£¯´YwÞ/Å(¨mJk[¾ªN`âéÅR5>²Vp'
tRëbéS=¹s_\
]70Ô>å¤dw÷JßÇÅÏ¢iXK¶L-±k6¼°£7µïèIT×Ùm]ö+,EÏçíí¼xóÈ}Ñí@÷rËâ58ÌuTÏãkC['{æ;ê[Ú¹J6v9LàßGÄ=ëG¥!^FÒJÕyyQG1|&ßæ[51OØrOcSÍ·]i¢½6DëÈiÞ{¥WEhîÒ/%½Üÿ&cãïF,Ý)¹d ÆF?úºúMI{~B¡pñL}ÿðÂòÂ}"4[a"ò/1Ó8Û]Ùma&ýþ½õgE*´"HÒøÍH-MYám
+Öá(ð-|tÎòÑ4»ö:?fóëWò5ö£`w²Í":u:µ^M c[itÇJ×ÆÇèZª¿æo¡\'ÅfU`hrc
+ÝÇ:àH¦d;ßg@X®û½ùFÈÃ#þ÷®æG<\ñý¤üìÜ>¤4.aòd|ú~\×éç]E®½5Þ÷.ôôzO¤Fõ4¯hÑT´¬}''¡µ[¥ßRïÔçéjCåü²:Ê+ÖTñih×CÃÌyßñ&ZpÍ}9¬´o«¿GuP<s9àäa_ÎdUÖ1Ó
Ïemk'+ãó1E°HÖD4 ×·¸O`£z¬>;S[¿IvÿöÔcÈ
+¹C°G *·´i3|MLÎr[Z4/øt'o¨ääRðú²,iWÒôà
ÐbÞíndÏ,ZÖíôj9±Øýeý¾?Ø5qrhHÈ+¼«-Ac[ñêñ×$ûÛ!nÝ!ZZþÇ°W½úkª·y·'sÜÌ8öuºs5®ÓoîÝ6¸Í}¨å÷FõâeçnbÄÅGã$;ðÝ9ð¤Lë³ EÿqÉð(ö
JÓþË(0{Î
þbäÓàÑC¶§
Sð×0<iòNþçC+7Ë(ÅôDdËÛèZÈA,9/S¥®ÇðëÂ#zF̪æ-iò«M×Å!c<ÀÊ$ICTÚÃd±z¢Tíu6u>¹Pó×VhÀRîJlÍ%<p@Ó²¨,brñ*óíëh³<Nà>á},ÚtáûÉóÄ3Gg®
t+î¶Éã(,à¥ïûúÁË_R¾æóäk<_
-çÀ?;#hnGõº?ìNÞæ·È,ò«=öylF
¬Z3ËH¼>âc+Éýª[¹æá
s1d²+QíÙKéY¥Ms¡T÷«±<S'¶K½5Ô^>kO`Ë](ÝoY®>ä¡5©Î][ùå@9{_)SâGUBN?¿âyõibȼ*&Hõ û/>Âõ2Ô}ÅÏ8éjµýr0-G¦Õêò=´cZ}Ü]$e%XÆ3Pç7¬!÷«.0mlNb×zÚE·ÜKÿSÝÌè|Üg±kFYÅÝçô t¢*§|íøÝó»iKyúßäW¢kÐ[úP¶P1ôÔ&®¢õëÿI¦Çóår§uN<_ø£&EVÈ!¤Ã{¶V¬}þþNBÙñVÔy_ýkå«ÅÊfT~ÞÖÔ>Vh-H£]5{Neè)i¨ÊÞE
X:T3ÃjOkÎÀ'N"ëq».zúÊÔÈéîã7¶í¥£ãûhõuÞ¢Îk»ûpTD:ÈûÆ9(fEtwÝ6R¼ôÅëxkÑÇF+ô¸!N½ýÓAoW ÒÈëÒoÄòü\ñ½ÃZãhIVT{`9Lù¥EnÝäÎó7'Ä;®VÞ ÛðÇ)Q¤V"bÄmåìÜrýåBWU¨ÍçK:G¥E=úHµ«óä¤þ²d5DôuRnõçbÒV¢÷r9$¡aÖÃ,ás1-Ur§hõ¤¯T+)GÖbïþØZËÐnhµzæÞï^/ÄÊXÙ"ЪßKÙò¿$éÝIiF[rª]î;=4»'ÿQÿ±iåv7u"ËÍöQ4ÿìàÚ·Ä*Ýï(¿¶@4_¼bþy¡_áã_ÍùÄ!_ÂGÌÅA©JµFMhéúàð¼Ýô°³Úpõ¢Tüg¯JL»pßÞo¬l¿N@pÎÄwõ¤YZ^8µb¢(%üÙ+giû|
+ßéÈ.ÑG¯Ûl ç»OÎ%¿o)&@±,ò
:ïlówN3´}
ùj~§{q§§R§ý|RQkÛ~´·#
+
+Åó z=\ØÑ4mõÄ1s+¬7©#§»I£=ÄäÖé3cuéz÷èêWá®ÐCo»>Îpàcµ§Ã
+|×â(*»XÆIöæ^
+ôâ:QGSä1G{õÚ¶±Î®'4õC¶¼6y >ÚNÞD¹ø}Ãï2Z¡Tâ7su
ïz¢]pÉâì#èþÀsÐþaÓ´þ¯×ÚYÈGf¦÷, ¼.ó±s¨!B§dFâÔ+ÿ1ð3q$ºÞÄÉÆ4Zº¡Bêi MäÓVÞð@pt¾³ÔKQ|áól}jn
+;*-°MC5ý»Â«æÔPÑÔ[¥I[âYïö·z^9i¼;%Ü0¹dÄ50ìîù¬ýXQ¿íùbQ(YªD+t²B9ýDY$
+endstream
+endobj
+17 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-251 -250 1009 969]
+/FontName/XCHVXE+CMR10
+/ItalicAngle 0
+/StemV 69
+/FontFile 16 0 R
+/Flags 4
+>>
+endobj
+16 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1867
+/Length2 13928
+/Length3 533
+/Length 14981
+>>
+stream
+xÚíµeTϸ»N qww
®ÁwwîNpÜÝÝ-wwà6üÎ÷$÷þçËù6kÅêgW½{?µ«ÞjrbEezac[C¸=33@Tî3I\Ôt2·µ:x+þ³¿³ù_ÌùÏø+dòWù¿¢ÿkò{v#[kkàÈ»ÈæOãýÙ÷*¶ÆB\+8ébïTÛ5tbÞI½Þ:x|bºàv$·44R@ò2@GÜwÏYªV/1ã/Ìl3.æ6g^îU'Nâtâ8JEögg»Zd´}PHüô¢yN·;;J¬ÙÌé8jÖí`Öù1?#ÅÙ¡mrk¯D²(Ëzíúq3·Ê#9ä#ªÅ-ÈËrñ§/ a`à*¨*²üR.\Àñ¼FHÌ:¥>ÆÒÖ ZcJX)(Ç.ó´âoñ¥b|pOáÈ-f²=|Ýß"Pù|bºúG~Þå±ì?¨RyL)gx=¨w*z¢!xN·,gíì@ì-ÑÛ1QÔ U®ÜúVü¬»ìUÉKbÑALÄÀïõÉ)dL£0jéeº92"ídÐiQíÓáÅé×UnExª¢Ý4`/ç/H_×EraºN7Û2Õ
ÚND¨uÆge~né[f.<ö}æsÄvÝÜ+«ä,ÂBëÀ?3ÃÁ="Cô:¬V¬
"¶¥X¡¢|BjÛÔ(DÌmZ_ÜìCÉÆl0ôc$³ÒTsî
ÂSóÊwÈ(ßO|YäëÙæ1ô®Nðæã¾)m2ù_ç«OÃ^º*)ÓÌÑ@ýàË-}¶ø
ËLÑN¹`îTGñ;ìÍ7ø*|ÞXevÓÂÀà:w5W«Q¨Ï
+æî~³nÝáù¼Ý±æ>BÔ¤a@IÆK{¹ üC{ö]§t+Vfú?ÒÇ(B>[ÆÒÂMdûòCKZs§Yú¯¿|I¢ßÜ<ÚÀ,Xé]jû6P?½xgXQÐøì9SàF)ÝØ"#ú
2Dê;UE'è+kg¡ET#
+¿È¿ðò/ó7@n¾è²,´G+zµ®
Z§Ìy¯uv?±1'l
Ïç'®ôÌÁ@æ¹àkÕlTáphuå/7AZ#m+àCºª>A{\ßD\<0ß8ÝÐ1ðûSöo¹Võn
+ÜZé9r£³.ï¬t÷± îª6 KÇÐ$Ûd"pÓðw&à.ñø_©¾Â²äºSâÍ
+py4°Ý
+Iõ1üêò)ÉAú +&táLû¢'7ïÍ0C9ÒEd§h©»®4 e¾"hÌPJ
+ç\g]yÎéÍ1 |©F2xÆÛ²Êü-OksØUªÝfWçË,ø}cúÓEdög«Þ~Ê(¯ÑhùXîJ0½¶Úßí* ,û>²KU%t¸,,þ*{]N
+X¨¾5/³æ#3ýcíäü XEÁãÓûCèû¦%¶`bÒdáÚÇ2guÂ=ù*p%ÔÄ2ÌÊðFñäx®75úð£»îØDûâ¾ øêÑç<·vߧd»·»0±FYGåÖ"þ6 OHSÇe<åýJ6ªóRî12Ê7éáÎ÷½%j=Æåøôå&èêZl
+]^ dÞWì¬ÍöG²ÚYªs;w{uðmëÊYDVç®Â¾®ÀðÐ[ø´£Çù4Qu¹Á,hqîö«Ç9:·×DöVÒ±¸
¡b ì¦ò¡)¢ÝÎPÕ>r¹vsïøaL)Íuz;òÍ
^[1õ¡qtºèÅâ8<Y³çWö*òKQD½uÝ%É!tBA4X½îJ>î« p9¬ìg¡«ì ñyÂ^ºóEâ¼GÖ9º¯
+xnØqÏ1ËÅYÍpâw^ìß(:X NPkYy³Ñwü¬Öð]æ&?eëãy
kÏü\ÞÛÛïè+VnÒsgìÞÐ#¢öÝèxö+ëZvïrg³Et(>åèì®^n":ºÙn(rÞÅføv¶OÞ4µ÷~|¡Îu¹+àj¦8»aË}Bè$k®QÝ~èdÉâ6iH@/ÙkÍQ
+Áàj ¦×øÈþËù[KrGÕhÜ+E¤Ügµ/²)È%Ðpîñ>J¶±(%QG¡xbx«zì)«|óÛm¥îCTñäP¢iñ,´ÓÉØiÑ×þáMÕõB!^C|z·8òfGkà¡IÕLãD÷lîùl(Íkz·ºnYÚÎ2½w`Â.ûÝøà.rÀiv´¾«ÚÁSaÔ>ãìÄd÷Ö 8? ¨[#¬³ú¬ÃK
+1HÕðF#\Ý&äæ`ÛÒê#ì¿Ý&ë].JÉLÏß,G8kÇ$9ÎÒ×Xiã<"Bðîi±ßFÀ!Z
DªÔËu¸nIa];d¥h
·@66>R¼¥QÐö'¥VÌs×þÆ2ejóDÏõ{¬1<~¼¾=çÓÞg+æÆffÏDzüsH9åÓF4C ;:ÚÄÕôe\©.È9mí
Ù°µK0Â`¦Op+H§ÝÊNÐl1UëYG¼ìÚ*X+sé>v¨ÊTã§û³6Oû4´ÃòJûVÓkÍ
!9ª~mV½õÝ÷¨U
+wdÕX*ïÈÃ>÷Ôéä@\l'Ð`ªKü0öÐBó)§¢|÷£sßúz&öø´°Ïá
Ts8ð{ÛàK°ú÷èo*& ¾¤éÉûEm:-®PwýIØ\Ja-ÈÇÔÍÞÅbVìHµe3bä!Ù(\[,UQpBRÈ?hÓmPZ¶&s.=·§Ë#¿ï@¶AÄx«F:×ìñ¥d©Üwë¼J¤c¹AwO^K«PE殤È)>ÆñZçú( Ýbû=p²>ܹwAoÇÛoX(ÃzÈA+±1ç$Nt¾ÖüÒÁÏJ\$Ô»ùöyIf#v²æSf%ï;ü®ÒE2³VöóT3æîC¼9aÜÚ£çó.õ_ýO`0L´Õu¸îC«
²Uó'$Zõý>ÙÓö²5Ïqn%OÒ±Ø>íi /þ«¸þ:oÁ_ïíâ5É+Í{¼Å¨?ø¥mº7)<¢Ü¥ÚÆÞa79[Á÷ôâ:@ñò ymÚÀûvQø\òÈ9%_!6-$IÀJ[erX¥õaÝ p£´àÐ! [
Æï(/¾8³weuÕâ<fOÍ V¦þ©Ã&·³ìVñ-ÈÖÑtA¬½I?Af (kßF|ôiHd²NÍF¸G§¸"¸å{b/Ïñá¶ÎÛ^o´¹.8`2Æý gºeÚR7S(oJ!KË*ÇmsN(NýòUµYH.ܬZy_M-FÝ7?Ù×&®;{ülu
ÎûQáL^ÃiÓN>ÐX5ÖrNl.a£ÂáÆ}Ò#Û4ÖõÂq6·tÝ{ÆU@Ñ3;ÊD*¢;©,4ú03Ñå(dï¤'ì®Ñ
K´7¦né÷{ô3
nJ(´ú<OfB¨.·´5X%²\öLçïò¥ûÕÿ´âÉRê¨)ÙSéî67¯`t5Ô}´.ò-A:HÆöCmÔ*Ù*üfA¿¬$x{IJêñl5ê Ùéÿ¸JÎLF§?/.Eñ{SÙd³Õ£è©N{.ñÂ79?¹ã@Ö:åÕÅìTÆ=m´ÐÓvææñkñì3
Ì>C³êWVµô*Zb *CVnIffÖ-aôDK·ÈþíÀÝ4aïëü¬éqóNlàÙÆ/j
Öè!ÌjÏJTÌÞñ+gQ\ëï ßÇÜC³¢Né +ÿ(hzQÚã5nGÏ7Gâ¬àÑM3)åÊ÷w7¼¢É?)§Á\ªcDºòòö£ÝÓ|å×KîÐ F&+fÁÊ||sILúJôúâ6CKëç8
cât²F²²Å,Èmo'G]$ëµó¸Qc¥°¢6
ó(wü{æL¥¼òSNKkKxåÖöÜ5ºSÙ,ÅW\Ã4É!\£úà'b¯Æ. \7`Ì(þVã%÷Ozo?
÷x
+óÉKrÈN¬¹¾Ão?lzU+ÔIZ}ÐHdïÇÒVºKjy
+÷o}ûz:RLÐ<ùé fv&7a¼û¢nòG3¤{0ÙÂí£×Áïá²YÎOaÛæ§]hÃì
+v¥îÇø>3UðyÇq-£k+¾&É;¢ú±ØQ\G3yfݵsE¸oªÓ`)Æ0TU¯zü DÝóÛº+.GFgÓg IC[h~ FÁÔ²TY½{6@ôt ÜN?§@µX3Üö(ËÁ¥dÝï[`5Ù0¯'èÎã+PÓêaà¼5KS4´X):Öá·yÍXn¶KuäÎ4çæ«åM1B_ÁYµß Ahµ¿}ÁÑÐô!92 7þÄÈcHY~çèêʳ±ñÃXiÈÅÚê3öÙK
¿gÿÏÂj¹Oì)ó)2H¨¯ µ eB}@õÞ
+2jùÍAÕm@eé¬+²(¤µ8>Û«}®ù<w6Ìq¤8:µwH~Ý-f#ð/QâpV5Àýr¶Z¹½UÕõUZ7A74·
+Àé?&g5bÅhñPÈËU1«*à(JobL4*8Ãà´Ðöé_µËZÖ ¥y[üRv>·±fÌOøº£R¶#Ös$ñ=|ÉNÀzBg>6Uzº~OIã"îæ/õímkýÀPǼªÒÜùTwQ6úêD«¹4ãðÂ*ù¤·måÀ+I]>#æGç)Ö[ºj!¸Ýßo¹§
ãÏ~t
í0èXüöúy`o×*N]¥À»~iT½dÁrh¸¹nÓöæ'p²+ü+U
+ãw¡öæ!e²ëïÚ"ÂÏwZ³"z@@Ä DÔùWk(¸ fé íS+k^¡%QSMÇ8WÝ/"ÃVßß~ɸ¿ZË1FR¨ÌÚzLâ©Vº2ïüF¦´Ç~2í5n¢
jsÜê¹PÎPsîÂN$)hÈö:GiKßå%`m H°PjjÀÝyg2Gr]+æ|ÀBqTåÀysíBìû.ò15ÍÖkÌÛÇVFéM=|û)ÄåIûßÑ5ëmã&á$,ä?ÓÏËt'
d¹Ç7vâf¡{ Ím¼ttW²µQ(¨xÑ&à{ô¹,B}]3°DË
+yOJÎï¯ðÿÌ-,%k¬äÏàw<{²¦é. @¥1@MÉbó×òäo¼.PLÿºÌ:1VÐiÔùù1VYÓ±bòºRNÚ3}pTÚ<*¦S)Ò"¡²´FéXW|æ¥Fn'Åeî
©ÕÒtB¨FîV7ÝúYmÍ4_ÔØÉfÿ¡E#ZJr¡X[p·$pì*#`ÃçL÷¡JdBiS~ÛÌ%{}: ì4:öÎ.'ßÀ;Ôöú
¹®yD¬=ätä6ÕWÔ@ ³:åZR¥ ÄòÓèó®!²Høi±8í§s>÷%ÈÍÈlZ&:©Sf>$'ñÍÚ5ÜC![H*âÀ˺`ËÇrRÓyq´Ø+?d#º'¨¯úqê\gLû2;dC¹ÒÔßÆ2¬?-DHý®[ÚõN Ø_b?ð¨øv'ê¬,´}ã8Øða»ÌâcÊá¸ÄQwHMÁ±0õ
)Qlí£ÎÂl$²,>¤õ¯ÜµÄÃíVq.®²Ù(åaÊ2¾»ïÏëW,åùnMÕù#ìk×õ(c²ÁêÑIí
+(©W¨F¿ëö¡Ê^î+IZv¬iýÔ¢é~"ÁºÃ"8¨ÍФZäN{G á-FÆÁ4<h!u½±k£tû7f?PÝÁ5ÁnÝ~²5ânF&,äÁÏïóØ=Ëâ¡Jsû1/º à®sÌÌ,Ë&|>øH1äczæ
+s"KlÅ´ÇBfüFð´°ÔZ«´ÌßT%æÊNoç>e<©×?ÍAA¨ôDXÿB¢¤xÚê«-î?`ÔK]±u![¶}ÔÄã¦W;¢Úvþþø¤X¿ÒHÃXê(Ix§'m÷4¦
+óPÑpN8,TúA76óÅvhñ¯¹ªÐtÚhø¾zÁdÉüo×éMaîfOd{u[ê^ð[¹áøºRBIIºw SäÈ=ÏKuÁÂpqPq%ð¬µ+J}êFNii|U1ËÑz#ò~p+§évéÌ's+l¢ç§2ÿÿ[©-nßÒ_Éy·U)umÏøRá1OÐta?´c\°ë3!G
jäb(¤UrN`·V»ÀßKãã5Øo$k%àòÅ»JòkvÜ'g¤LQe!ÃëöûOv°³ÓÛ5QwrƼÕ×n5¢ØÀý*.ªJOÛßÓ$¦3ÿ"TÓz ÉW%.º/ LÂÌ7¸×oØ6@èFqë%b½ìº$ !ÙÐòEøwPuÖËXÈ+Xpª2ãAǨ8ãksKr5Ü
~lpð&÷eU.Ædî-ÞïðãìkL7R=!0¥¥ó/F%mtL WZþ=@2LLQÉÏFçÄþ¦W]:E
+Ìj(ØàTxòHmëðæn<ÕºeËohc]?uäå-þ\¶é
Ô§ÛÈYM,°ìÂeYiÏKÁ{Cú!.!p">ÒßïÐßK°pÉY+^©&¦9¥ÃÑÇúö÷C3"¤«BÊ\ÿNßfo
íq"Ûÿû¹Ho7CÉQD
ÄÔ5òUEHèÌ[ø§ìL«ëDÏR=¢½KB¥¢(|«*Ú¾W<îGQmð¼eµ+é2:
µö'^K¸0ÕOofzÎJ#ÊÜ2¯"ÇTðW9&j¤_(L¤§RRÆBäª&¿®4X3ò$ïW/ÀJvþR¸;6Ð×3È(ÆöA«»J@¢®Iù(d ÇÐü©· ¬jX®í7@fz²9ý2××òÙlò§í³êÇúÚ;¶x²æ\Ü3<BFÍÐï5C×x43ÇÅH.®A+Ê7¦Ã1bY§$·ß ¢c}4N²z5íÇ"ûüHBå~<±l?7â,åkP$ßYÉÓíµ'âñeaÍÕÚÕÖÎMÒÄÌùÁód0·ÒëAåàUßÀÝÒ%Úré>pÈËîöÞ<°ÕpìÖ
ï¦l
·÷& YØyËËÎS;D~À x£×èrä·%Ásoz´£·Àåõí=dJë ®}»iª¿H¨$Ë3tNæ*[¼à[D
µ÷n
©Ö_é7VÁPzÕ0t¾~ü bÔ#Ûþí6Ä@1)aéK½=Ûáâb§àTÅòÕé1ìóÇÌF7Çce2½è¯r+åÕ
á9ór5jLæ>ôÕÝ=FÔâ)¹Å 8ÉWð}àcÿX*d¢ÖéÄeÚõªæ£{JË+ð½<¬·¬H¡ëìû¶v¦ù][[0§-ú¯P/ù5v!£»ù±©ÚVH ó]=ø®k.9¼(?(Cî®é`û+}Ähöj5{µ´&ùQIÜé5ÐU±(ý«o¶tø¬¾¡1O³¯Úé:UÍJJ
uµºY©óBÄ7îS(uA¡û,ÿ×@jT¼õ-=Åëq¥±eÏÓÓ'=S<·;¸'/©»0¾ú×Ñs«ªñü½$;BëK¨FÞÊ`Qw8µlmFÂä]¿SÏE/eÀlö¥ÐÈýÄx]ßÝñfÿjü·SåÇAáüÞ ¡çÌoç+FÁSqÿS
+ØøÔc~í_Õý]%viªÃ´L'*ª{]rvB²R.äb(b¤ÑO×E+òRL5½jËGÇ'BýTÏ4ÚRï¥w0%p±Éàf a¬ª=±vE×e°í^ÚÕÍ2?§Þò¥Ý>Â÷l®¯Q»!ß|=Tùv KAQrÌpüóÔø+¤o âÍTM?}â¤ÞÄaØ¥LöÈ4ÔÅwâ¨sãT[®µìhYþädgÞCàØ9¸ºo7 äNÞ[Á)8@»@Hû¡
Ë/;ÔGþÄ
+Ñ4þÚÚ7A_O¿ý°ÄÓ(vxî¯ ä#ä¿ÊÃ5ÍÿàqÁOR ³8oëL^hÈCw¥®´(Ñä<¡ÕÙ
+»uFÉå¨Î/.Ú YÆpÕÙõZK¿ÂÉV@CöDÃÜÙSLô®7Yáë_ÙAl®¶Ý`!Úäãv´Û[¹²oh!²ÄÊc2·Ñ#©il~)Øq-J©ØÒyúÖùüZ%çæÿãp
+·?¤
Ð[^FÎîã(w^´<äõ+endstream
+endobj
+20 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-27 -250 1122 750]
+/FontName/GCOOBE+CMR7
+/ItalicAngle 0
+/StemV 79
+/FontFile 19 0 R
+/Flags 4
+>>
+endobj
+19 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 845
+/Length2 2042
+/Length3 533
+/Length 2661
+>>
+stream
+xÚíy<ÔëÇDJv~YJd6Æ!ËB4¶&³hf,CÆÉ
+¡$Ò)-¶dÉ5¶ÂQJÊYêDö¥tFÝsÏûÏ}Ýÿîëþ~ï÷ó|÷ëó<Ú[ñ.údhóõP°rpF(¢mÅl
+nMdX+Èæ@ÀJ·°+[F²¥û2+íÊ üçÔeó7øSÂaRBO8G¢õÇ×gíd
+]ô$PF+ÀØ!?5B¤ýà_H+ýíK]$HV¶"e
÷ÎÍzÓG¦ð5V·¿ÀCjsË@Íä(=y4.Mv
ÀOuË.¼Ç¹0§³Åüä®ÉÙ3G WI×í`[8¤ÿT¾Üc%ìþGðL·g©^* ±®oìûÔUõî"ÿ!·®\à¡W×Ò]:pj°FKâï±OÕûÙ{¦x5ý±ÙC^
S EÄ.¹MZè»Evíõ?dÁDת[+Õ?\ÌÁM¸kÿSb×ú÷i4ì#³Ñå4#ìm¹ÄLTÄÖÆðÑ
ê%ökVF¨¦)&½IÙa1²éÙ'F©{lu%}'s~hKE"¡wÉvìüwZSWON&påwíÓ i([²Øúæc¯6í(×ÌZ-9§¡)8ù$ïy¹ÌñMî˪(íbÐhÿZõ*¶æ;(5ÜFÖ«ÏI!Ø¢$³KT5xõÂ#eñ×\V~ÑøpÆ;4hOq°Wn
}E7gÑt;vhðöAÎ:Ìpª<åtè«BýöÑ[<áÛÆ×¥!ZÒý!/Z\d;*eomËXÐ \pèÆé+òa%K5k&JnËè_ªS½~vMüÅîÂÎtD-EûëSâ¤ÝË+Üõ¢#Oêaߦ&×9ª§L*cMrÑP|qd!£ÝqÊt5ØcaGoÔÙñÔD¬bì(¡@½í©§æôB2ãRüRçN¹kfýeÊhµTððFVû«ÏÖ#Ù¹ÎÎ,LõÂl|
]¸ÉÄ
?ÓïÔ¸ö÷ÖÈ?F¾}µöIHÒ±WªÂè¥Þ!AëY:-y©Jó ¯ïÔ'ÿrÖDÀ#ãµG¾Ô*£§ô ÙÅpïv:Å#ËÊNd;ªü1åÎï
+iò¨Zë±Ùùò[fïo¡Wm8y¬q¬ ö¬¾¸`Ää¿ ¤f»éEÐ,êÐíÑSOòâq¤´Ìp|tFëªDðvJ6hã
?÷7mt¸ÜÆziìZ÷f*Ûyëg6i>ÅÈîeWÄÒåã
+öiÂül½&°ííè!6'[ßk_g-ußÁÍ-Æ ?¾âfß$}õT··_czµ-}óüp[ýñÖ&êÅk%¦åÁãñÎôT?ò%eEg°pÃ×çÇ#ïNýòäÜA¥Âó;éEè$¹æä;&å9¶ÒÅv_NÓÁÚ=W0jVÌn&ÜÝß3£! Y
+7Jè@ÄBZÒe´ÇúFË|',äo«*áüzß8ï]1wè£ÂÓÛ.ÄîæÿnSîc¥mà|>7«OA¶©r_d±Y¼xrÙã}Ýü)÷m8']kéêáùWµ¹iÕ7&ÕÃúíÔnÇÍÙ%ô¥'L;«©ß#rU0QKª9í+©W4Ej0Hó1Ô³uT×êT)ÏâE*Á7pGÅ?¤kÉÉWÌÉúÏT¥%Ö [zÉ
³Ù¸)l#«iìã$E\9Þ-t£¹I}qîÉ
+é%7=#0Æ^}£é)Õk
PGÛÝØ©stV³ÒÂâhï,ÈÑ-¾¸'ÓöØ·÷:ÛW¨f^ܤ~¿Wsbz×DÍ÷VÓïtÞRpÿìdƬ>åóZa?5ySo©å^éÙMûåfuëÒ^T±ç+Ìu5ü¸J{yÃôÌõ¸ðîÞ/õh¹:øaæðq¤¹ÊpEÞs{äR¯?]õåc~ât·ÕÍáû'$wmY¿·bó¦Vf¼£ðy¢LT²@ÝG_§Ø{ÉûÔ×y!ýÂOÁÏQL¹+ÐÏÜ6_
( ¶/8ZÑ#ò&ÏÃÿËòÿÿHTÈd3hDf+endstream
+endobj
+23 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-20 -250 1193 750]
+/FontName/DMIVYN+CMR6
+/ItalicAngle 0
+/StemV 83
+/FontFile 22 0 R
+/Flags 4
+>>
+endobj
+22 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 845
+/Length2 2056
+/Length3 533
+/Length 2671
+>>
+stream
+xÚí{8TûÇÝF5%9DB[æ\wî·&w3³5c1C(¹%P±©%j#¢Ø
+FIR¹ÄP[.gèì³÷iÎsþ;ÏYëõý¾ßõþ>Ïû{]4M_ÐÑ5Ñ´`nç|++æ*Fò ªæjë)]À4¤x°ÃÓÀ ^¸P$ÎB+iõØüó¸§÷äÛÜUìÁQÅÐÎÝ9ÉuÇ;§LOA·üI±nãðw6"Âb%úya[Èå%:l}N3@æOG¸<GV%
§þfò,ó3ÄÚS·øøälS}Ô³:ü9ÇÌ}Ú¦¹W¶[¤ö¿¯ìî;ü+F
¶©|³rÝÞn"¼úùiùûêþÅFÉjY>ÑäT¶â£é
D^É ïôi:rLÂ`pjÕÌ(¸ÊÂq¶U±cï{D"¤) Û(B½C 5¡ï>C*ðêì+ÒÍ;îÝ4(X[ÉMïc¸È\ÎËRw
$
ºUs°öS-¹ÐT¥OVË÷§:ÂE®[ J«<-<,w5M\iI*£km×hh@M<¡î6¶¿c¸äSëöÑZúZN©÷õÛZC#×cOÉ4n
dÀâ®I>Ì^N4nß
U?¼§µ}ôDªHvÖÝàÂh%z¨ÿòÿ¿ÁÿDÄÓè <-ÿ;ü|ví
+endstream
+endobj
+26 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-36 -250 1070 750]
+/FontName/MAMUVX+CMR8
+/ItalicAngle 0
+/StemV 76
+/FontFile 25 0 R
+/Flags 4
+>>
+endobj
+25 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1289
+/Length2 6434
+/Length3 533
+/Length 7245
+>>
+stream
+xÚíe\Tí×iAºka.º¥¡RR¥»»Qîn QZARD@)¥w|÷ÿèyÞóåüÎó;³¿Ìîµ×ö}¯½9Xuõòöî¶0ew7PP@P
++ 98 H
pwS¢`R+}+¡7èDûÀÿ@tSÇ?ý\?-áü¢-\þ@´ëoDk¸ýh
÷?áñ¢û"ÿ@t_¯?}¨?áý¢5|þ@´ïoB÷õÿÿçP+(¸û=
+B¢à_ÿ×DC7ú¼Õ¢`0X\òïóF¢Çõ×WýÂüè×óÙæäR!0ùøNmNzgï¿Ó%ìz¶)ú½zjôÛm\y§0gÝâ/º±gÜ,²õAZÒ½ßüýé«ÞSó:w¯JUÇ}¾¡h6ÃõÀbÛÍù
+EYá|8²/Dyt±Ò\Îu/ÚWñu-4nFou`HÙÍ*ùj$óåY¯ÃsØQÐú× Qº>»6<¿mRu4i(m.|-Àp²^ZøIUõôåfÀ'¡Tz@GYyd3 Ïâ!'"%Öy¿ZYm×ãf Q
+"Öîh(É+í=ÜxÑÅnù¶ýÀØd¤l|?éZ÷f4ÛhÚïãôúm\¡+ÍöF¸ç¥Éõ/
ºk3Pò§7°k[ª/§©ñ³Uó5\ò6AVþ»yç(Èå$MÕbzßéG¶±¢Øî½`6©ñðù
+ úOÚjV#z¸ÁÑqã÷Ûz+ç8bD8)2ïÀGê_W#P¼±9U¾£ÒjÂ}¹_
lÒÕbM¯À2!£lï7z0Q# oKQõ
+°HCæÇ+ûNô_=«Àm
Ò¥ø{ê!DÑÕóVª¯¶XÅ?Z`Ã-_(/]Í XØ1øe¶½cäÇ[<ZVñ1ýÖÑÐQrJaN©dòî(\J½¨Ç¾Ëàmqfë`9`ãUºÎX¾3S~ÝË+Éîì« ÎRf8'Cãò¨â'÷JýûkÎã*¡+X-ñn}»PÒS!¼)ÆëR:c,»ÐDw!³môP¿ã^[ᯬ¬p
+Õìq
/\Í4»m¡IÎîPG³Ô8gx}gQÜ1C¿jݪòò³A¤yÄ¥åüÔg*¯Û1Ù>³6¼XØ
&®fr+X Úò|uV%ø2KÚ®¼áÚeÄ9Ë2ëöÄpõû` ±æLb°´)ÉË%Ã>u!+#pcäupY¢ZjÛØR&Èq£Ì|Û&,/D¯{.®p[£¢7`
+sHõÇpúϽ¾ÚNâ&Ö¢;ío@o(¥¡¸û©ã><$óî/Î0ÎÕÔ/;GºoZxKÜ]a¦VUn¶)esÝ öy¸RJ"×õð|MuÞ×ÙeLÊ1ìDÖÙ$Xki> ?~ÍÚËTñåºò0
+¢er»Bg¯^±´ÑõÎ`æìë
+e-Û,KnNÖVC/ô<÷±*¦Bô&9Ú,uÑ=ÏkÃÒg¹iq!¤KDر]åý`ÍI°Á\l
³R^&Ï2føÎf4û"Äqg¦2ãû>z(J½»sØáÏ«±1¥pJ2ø-!]Üq
+ã^JfEx+oA?7ËÆRâÇQ1#1&Ax\
/äÍ¡(aeÛiH_HðýÑfÙÊå©í]Ú#NïdÅ&ÕõÞû¥Æ¢¾<ñµ@ímÝ48RÁO{ïÕÙË$
KîX|luâº!c(ó¹oa¦³©ªjY9wV\¼U_÷ó?aÙbÀ.+Øpta.4î [NöÄZÑyÆI¥ÈvFvtÎEGùðî*."ô"bc
£ÕJúuCÈg¿Ü=¨¶oBüÞ6~ÏcQzU%µÎ>ôË<
+«wÅö'¬gsIñ¹fWùD]·]x;çy<#O´Þlã>fábÖ×t«/öý¨oä)HÊ¡ÙH/
t öGâÝæG\>Æ.òªé;ú!|«Uîç/,-ô:ðõFÞàx½Ë*aè¼d6áò>ÆñE²¡BÎ)S;Áþp©#XvRlSÜB*è§ëÙ3å*j£º7~:É+TÚ=Çz©ðöÚ×ÕëÛ×/CP¬µç¢YÝfkªR,T<EëÒ¶×UváHFòàºû®Àl|ÃêT_ü°Fbu'ÝL±ä
+àmj-²ÓÕ)êÑõ¹ÃÄðá³Üoî{¾ð&VÝÞQÈ%}+¤áOòMé¢Jïu{'q¦cS®kGÙ<ý©®JGÑTkÔÚ'z`d{NÁ^
+D©^´N0<~¼þ¹ØrY°ìt}ºx3Aà§ßÉdÏtÉÉ|d¤1WÏÜÍ!Âs>G\NµÛÏ·xÍ:ÌþÃÛ4Ρr++@.·á1ÀPûµ¯ÃvÇÇÕés÷´I©cíº¸·V»Áç´-ß²m£V58ßÈÛJ
®ÉüùÍ7|JZf<¾D¦Y¦ëðorlCÏÁâ^·æµÔ|ÓÌÇØ,<î9Yc, "òH{,¡ßKµ/Cú3©ÿ<Q}b5×$Z5öB"z#¡VN¼¤mµôö]{Þä+Oktat¢ÍÑüÐ=°x§éÐÓûð¢Hgf,ëV5¬5ÛÈOÒ¾û¶þÌoUìE½³°(êFcV¾(!V§³QlºÊQ©Àã¿Sv»©W¨å»ý¸Wd!ÄL¥=YëâpUããÅÅÒrÐûj9.¦8Kôæ·|7¬ÚLåÇ7 JìpÙ(µR;A;ÒÅf6knb>R=hk·Zò%Èo)éÈíâñl¸¥éõ"ÆSëµ#æ:ÙR'RAxMmúS[Ùߦ)m¨°-æZóI`úÊXÊïî è
+Câo9ê/(RÁemÃN÷î??¦Íp|W%¹µ°yÐÐÓíÄÙÆ= ¥qXg6÷Áñü&_5j¢dDcS µ`)K¨ÍÞ}¹Z=J³g÷
[:i%r,z[e
®°5µQþnþJÑqì/iåØÙx95Mõ9OàÌÛWë$¼¯WÚý
«¾`¶$lñ<;6"'PUKio)åÓÕoµmç6üMÄ+i_aÑ,ã¡(¬3®ÃÊÏX3êy=9¾R*/HnfR
Âh×^¹bü½XñûÆVdw§¯Âíý^ÚXàZ_ïtMîÂ8t£*ð|$ÙÊ5F¹µèÅ,õ·ÕLBé
qÞF%«¼z@Aç[ísçIçÖ¹k÷-µâ7Îódôóø;ZÓô\¥ÍXàÄ6ÚêÔáj^|¶O»J8¡ðfM]BàøØýÃÑaPªôʽQyÉpP Q£%.ý²¬+ª½ä¼Ó._X½AR´.µÈR¦~ÍéìÏÂXáàyÞïO³ÍáËýøhÓ½ecÙêZaHÊÇUx·B01µôþSùÕ«|L§¸¤Ï[8ÎP÷Mj.ÎwSÀV°w½
1FQ¡|ò3£-vÖVÝ¥*º?î_çU¿ôÅR]f)¶#ÉÌÓNì³lç1?ùÚ~!ÆTûcððò(Y ÁQÿÊ`;ð
àë¨Ûî´FóJ2Ã4í¬þ8Çé:È
+AIUí:3"sýTU÷NCÀù>öÄ ?háy M
¥¡jj?AêHJÕt¹Ï<ç"·÷è7Ä NûbtÇâæZ3¡êÏ&y¦8}zúeÈUµ²,ô0æì%^Öa?;³ÞrêmѺëôx¡»4ieåÚÔgNáÜøS+ÿ½ñÍ{ÎotÖÁ\Å}ÇúÚOær¨.^_½SÊ dÛÔ"¡QbeÖÎöm:ÛªIÌ+P&Qã¢üøfX6LÑ ²Ê%'çÓèN¡
ÏG
xZF¿*/Þ3Zæ/o¹ôiMé6åã
ì Ô{ì/GÉå»·Ëæ0[m;pIQõá¾N³õpßÃÞf1¾âCò¥ß¨6¢{#¿['
+'éíá%(ª¿ _>ä'q3Ü6VéÃ.zàçmÊ´3»ØühIG%Ê/pÆçN?;**,4YÔ{¬KÔÖ]eÒãá:µ`ûÄ]-ñFÞ),£¿eàð
+ÚXùÞ2(u¬öédrä7,WÖ3ñQ¢¸)ñ3MÁRÌ»½~äw?µâôy¨lD¿ê"Oíú©qªÒc-y^É^GD³´/:
%ÌÐêcFecLÝUA<ãkG {×ÐÈ;i½·~8Vx@6Ôù!uñÀ0®²=òàµÚ:ÞØØJÔ2Û/ao¢FUôÔqn}Ü5ÞÓN
+Êy¥ùªÒPwôF¡5ÅÀ¸E¯âÕbGOÃÛ<\>êÛs&Ï|½z]6=¥0 õ¶e|GÙ)ý©Æ¦gÉ:s!#RivTÿ§àô[Úq$À3é$néWðEîôÃÊ7ô"ÐqÙ4Ú×äåkûÁ;.úÚÌÒà (çêÖµ^åç?ÌViá+®º¥40%0°,¼Sqöù¾g&EèÞCßúeÑÅ^ÚABR0ã4FH\ìÖ,Î[+N,G¬+å<_Uô®i1ý67§wdöq64ä×eÝ$9{yì|ÿ|åù`ïòË,®ÜTXAðËPw+*ÃÈ\¿ûúA¶µÄøÕbi E)Ø8êRºûT
Wj2%ÛZmâ;±yÞ
?ÂÔEÂà-rça_<µout¾Y¤XS\×N)Ûù½ø©3Â5UÂÁÈnjþÖ@=aÄÓwÓ¥E:/çY|çÔávy)uµQòúR1k&4W;KíØ&·hË
+c#jwñ1ûéÉwíòÓÎ`ÅïR}û@õ¥yÏß«I¯¯%ªJöxèuÁ2]SÇ7¨4^7>H#ÆÊ*ðÀÈÜom=¤tcÄ+<8Í:5[°õ .Åò&à}ÉP«øp&Òb_
Ëy
tMìè2s䳿ÅÛu²ìQ>LÌ[óe¹¿Ûã¤Ù_æ+uDl>.U|rÑò¤
*¹k[´"^M¼5×y6¾#«kÄC^~qUÚÅ4n½×ØÒ&XCr8_®~´ÉT:n^;>þH$éokcSêOÛL×l83×äó´AIaµ_þëÕÝpd_5Æq02ÙÙ$IK»v¤%B.A]Õ:LwLkQfc.më[s'ûuõÈïµ[fVi3-h
Q¯oþ$¨>NlÒÇ%H½9¼.)»ËdU¢ ¼wJMVÏ´ÅÂ.R¶öx8ºLØz04³|r
¸¼*áöQ¥ÓÐ1VçC>ÀRY0îÂz
Ý
¼¸[oÆ÷Çj+gRÅqK_êÒ5 dQW?¬¤¾lv[CTÉ«?8ÅtÎf9ñééZF%QÞÂtã'«âÚ©c%¦Äãoïx_éUUGÍ-ÚIùúQ|õYéЬqá°#+ã{ÏC-~È âKÝkn=!Ð5§ÿÑ=|{~¸æÛà¿`wÓòtùL9\
ÃÍÖbÏeÐôY*têáæ6qs5W8éfïJ§rc"Î\î!íÛ @®0Ýe¢!ÒTV.Zló}¿_/SOd°%Í%v¥:qÀËã^ñ3
+HMÆÉØs§`~é1e+îâÖyòûb¡Cqó'Ƚ î+[µôOÛÌù´r¨´ö½zï,E9V<!wãÕNÇôÞ ÕÉ)0wðÿåðÿø¢D¹»BÎÿ`9
+endstream
+endobj
+35 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-4 -235 731 800]
+/FontName/PQEIII+CMTT10
+/ItalicAngle 0
+/StemV 69
+/FontFile 34 0 R
+/Flags 4
+>>
+endobj
+34 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1874
+/Length2 12070
+/Length3 533
+/Length 13138
+>>
+stream
+xÚí·UXÜ͸»ChÜÝ îîîÖÆÝÝÝ%Xp÷àîÁ58 $¸3ýí}ÎNÎùÏÍ<s7Ï+æ@ýÿªü·ÐÜÊÕîÊØZزXþ²r´ò+ôþshÿB°Ë_¶rýÁVn!ØÊý²<þB°ç_Ö0ÿÇõÀÊÆù_á?÷Ãÿy1:xx3r+¨ü:¯OgûÍ@Ëj(F^âÊàð!P",5J<ÂçØ¥^Ó»?¯u+gyHÓ0.Ýßt>cö¸4¬
+qì0±:tclùµêW
þ ÙÕÎ1pzÑ×}:ãXYÿxÛJi¢l3p¼^øE7ì]RK"µám( ÍGè|<ä/çãÌÈöCºZFÜQ·û©a
£ÙËÿâ
<.w=µú:}ñ´ÇËkÅJEá£ôÞnA@ÎMNbeË`s)+&(.²Be¨Ô5ßéoC¯í~éÒU'+Ë l6¼íjëõ`»OÎbg*QÂQ0S.õJ¤*ïÆG|U{1kCzQ³Í2Ë9Æí¤è
æ;mÌê\>c
+8pMëò|aZìJY!hZ»ëýÑi¥I¶M÷kBÑNþ¤&_bøfÄÌE8Õ.)dÁÙ!C¹¡
MÂOvLZÝ¥öWÛP°Y¼ÜÆ¥éGª¬ÍËg7çG/ÍÒ+3ûìLáT|HG%ߺE
++w´TTþ>ÇÌ2Ù},:{g
ûà9Ö|\D5TdcÞË.à-,¹Ä%ITxîhÃ]®öàP³De?Ev2ÐÜ;Ù:7Èo¯N- Y64éwò²G§@HÖYpòÎx"íóq-Tõ7Ê iWëÄ:rµæîâÙ¿©¥º÷%ráè´ívfp;¯(Y¬ÈFXfi8Ü]ÑC±?¸×Ý|4µàÿ£þ ß9ÍdÔKùa)ÆYÏ\2b®"ÞÔKÒ=ÏÜ,ó8ETÔìGs¾I±¸ q¹.àéûGÒ9I°íöd±ÈÒnI
Ë#Þfdu>¹±¼g6nyºçóÄgqäÎ@ÜC¦ÆM4$l7¦Ä$Ð{⯤FîGcÖ3þÌ.èûtC$?Êñlùñ59ß>,Éà¨)l8÷©?-=4½$`<klÑqÆìÂZ8B_i4þ0ôJi¦àC§*k±5ÀfʲêÊÈE;õõN.÷"ûlcl]\'
â ÈPs*ÁÍæÁþý¬ÿádwE¬ÃáïÇtF¶
+dzWTÛQ^ç§ötÆOHCãC3ÍúÛ)ÉÓ5 ®°ÑuÅ?Gg\a>¾8G&ö´!ï->ÞçVãТG<úõ½GiÞh¡QÄ 9+t¥³ªc¨Îô>ÝìÆþæñqäAa»ßw¥øØÀå+êeÏôÒ%»m%VÞ_` _Rô{Ψ¸m-Úé x ¾"ß²79£¢ÔWpUôI@X_ü®ÓdsâKÒáè;È)òa÷÷>¥zñðw¾ªFÔÂä0(Ô{M7©,%:û¡Y¶*t<Â]LSÅô{D[$²
¸ ¹åxGÈbú1á³ÐÂÍ6Jï\u£zñÝ`Öóî¹Þ+ôàk]ü¸v+8ñmpÃ`°4}g+Öó¦yÏ'º²õC(
Ë'ª Úïõâ¦úWSYqsq.]m2Yܦs²ÉRU²G0ÿ¥!D¿ÀàEê@<ØÔù¤éÀÖ¡Ú:FêÐ#÷wÎã8ļn¦Úuízô2{½*~°°d>ïmBT,S
Ìfÿ}£^ò1QnËüµuGöv-×Jö(ʧ(dSfÂÏZh¿ÜÞ5ÖåHÒKD0æ
+.gÑ6ZÐTìÅîVf¿)@%åoQñZ_ù3;xÇDîûL°U*ÎÙ/$J¶Ûi!²iJ¶¢gXR+(»&×ç_d·v;%±GEìUÏû>³#íÞCíkîpÜOxNò>Ë;îÀ¤éá½_A{ö´£Üm_à`pJ·fÒ+!£é%9íAâ
ßÁ3ÉòÚÓ7-IÆð@Í#6f1UÐÈï8½ohm5¹ÂL%ó6:··Y¿þî2$O¤*µ"ÎÁé(tÈ%¯ÊA¨!*1ðõC¡ÚÈôñåpÂøÓ¯vn]FeÁg4Qÿ6,£ºn¦ò^+¦¢Àßò]ØÏã ËÏé1+1§ü µ79É4R½0K{ÏÆü[îGÌÅÏiZÚÍófÄìlÛò'æåB÷wÈ~î:<ýBÖý}ÙTY(t(PÔ$o¯\XÜåú8ãËÕíÐåP1'ÄÍòHI©Måì§g'f¼8í3mHoóéNHÁ_¦îts0ì~7;ï>V[;6f@~ÿåÊ4S!ã+ë¡l\#l zVH'Â
+É!]1í)Ëæ<²¨ÝÝÃ<>UÕÇÜ2¬A¹+z>@ÔÚêå"°QrõEZ˯?
´oêjÖÊ×.å·þ¹
+HPÐéß= ÈNºÃ°zsèuªÇw@>4"DÔ² vÀ´é%RW¤Ö
[®g|#Ú|S
WýN8ïªòÝ`oÂ/`CI/ÖÓßÛ@$Ð`
@ö.àH¾`uàÎàWO.EPÏðXÏBÚ0êÌSñ;ùãz}»Z
]¦5¼|q¶gl8¡WÎÅû¼%ÆÑ+jRÞÖÎi/"§íö¤Ä£!.×QØÅÓÂDr¦[AëHzÉ+¯jÏ\äL¾8{zjËb,,×ÆûïQ2RÕ0L<åtúvä].dÁ
+÷;åß"S¢¥6"]¾ö£ÊK~oÌÏ´~ÏS`ôd<2 ·áH¿¥¿Ðr]éð^V%Ùû4À8ÍòãgÔ%ÿ~øz¨¬#òMzzcè.+iJ|®lm[îD6l¯]e!Cømú
+ù^J=ªHµòw4mÐÞB÷?ýÇÚ[óóTP®õº)yÎëy Ô3¶[Üï[vGêk?B©Hü¹¢÷KÅz¼bdÖûW³¡rg1Wqf¬#Ä©¼¢÷V×v.<Ýù;÷·hãØFävµ P)'_
¾Eúb2JFD¡6Ì@¡à¶Ý$§@±J]WÍÊøXq7|b S²2ØgÒÒþó II·?ÜÁÙ^cQåÔ;Ê`ªk#áAY÷k&wáõ>ëA/=[EÃìmª¤>[+^+_¢Ï`òÄ(Ô¦ÂgÓ"ÂÆéæÕ*âñ]ddÜK³p¿ÏÁÓ8J%v(é®ÄW'í5¡«`¦ó;SO&ð¯¼:>âiÖ!#7Ó ÚZu´p=ß3`)öÄa?fVÙJa¤)6
>½ðÒ;
+ÝNíÕ+c
ÓÅWVÑÎäFzÉën|&!áëÜ>þñ%,ÕSÉðBdØavÁð);Æl{!
åkÃJWE470ýá'ä*ÃZ5õ¬
6;#{<ÞpÐ!ÞKÞ3Çvd*Ëþ1Ø&þ¦É2h´Å¨l;]W8Zí\Z¼ÅÙgsÞoDô,u´§õ¨joÖ{¨Í t¿wÐ]Ø {ZÙ]8ù4&àÃq×&aÛç¤Ãe¾w®ó¤ÊÀtÒHÄM³-¢3ÓDÍubºÆÍ<KDs¨[KNùÚR@ÙIJ|Þ;1N4pmLä,/ä*/°ª/þ)ÿ³^l7©à=,©W~õqÄ|+8êÃ_ÿËÚOýÔÜw0÷Ùdæç¶\<m"ö>ÜÉS8wI%öÅïßun¼FýÖÜ>z63ôÛ[~û¢Ê[Y
+ËûÃÖì0éÉõ^ÎCäL©ð§îY¶;bo«´Øþ"és®
?
©`±{³¹þÍR«¦Ü/ÖÂ1=<ï
_&ÐQ¢¥VTÇà?'O±<²ÚG¼Z¥¿;Ï51Tz+s´ºÁÖèn"Yèno×àâF¦Ò>ìPX»#\¼m÷Ív S.3È¥ÐúÉÍ3Èø»
+÷³¬Q¦qhÑ\?/üg6Ôâ+tE²CY<4ZÌ_Xн¤·é-`*{¤®Óü*ûîbëõ¸²ÄìwNHZ¶OìÑU>ðÂ5ÖÛWrM.8 þÒÆX(«üÀâ Ãìï5;JèµFdÁ»ÀàHÑQ,Q}\ìÖg1á¨ìßÖ|jqÎaòÓç:(v»QV¶õÝúÆåaM3 ïv_jáNqé!ÙpÒÜ7<ã´i~
+[#2Hã-¥é×/³MånX-±tìc¯[Úßá ±7åLë;ò©%SEé8_ôÏÕ¨µCQ½ <"U~rä«Ð1øýÆ ¯?,XºXÎ ÖGBÊXuH}¾=Õ=
O_`·mYÄXTåIÒj¢«y:FQÜ5{Ó
¢õ¤íÅñÊêEeÔÐ1J}ôE´lT¢ ;ôÍß^%g3?ãõ`À9måñ}=¼KpÄÅ>=س4ÒSTUºÐRçO%2¬+RÍýB1DËöÕ3ÛIôµ<Iâô¥ûï
vjxþÇ"6k¢/+,ÐJ®Å]ÇÂëá¡Ð×éäDÍnÐÚÞV8Zº}Ùl+¯u5V /ÜF]¬xÝË÷»,òs
Áï}§zP#4æÑÉùn¶Ñ#Îa¹ý?4(Q9Çnºgæi¸¥9WwBÀã=¬>wN ¨JEÄKæ§r1/.ãä¿~~y`².!ÚpBÓ?¨Ç|/N.ÓôCØ-+ôÛI»4ÖÓóøQ}è¨Yn?ËZ. pyìW½/ÿÌÞt'Ü]µn©ðXý
ÑGSb¥Ùnºud½Ì?r¸»´1¶)î@â@ë$¢f×Ö \¹ÍÞ(£ÈT6¤ÜÒjºÀ;w7~ÉBo »ZÏI¤Wìù¹ $(WpaüòY¡¦9ü¡ê¼JRùü>»¨®§ÝôãW,"JQl³òqÁå"k¤|9²3ùzÎHJGIF=ùTÑÌiöØæùü´+ìÉ2Í=<tñÕ4_ÑÔnù¿tÁg)ï!úmj£ÎÌI°î/^IÐHÝU(FÛ+·7ùßÒnÙÞ·xüÌjêW%Ò (üj Y
+à¤|ÎÐöÖKÄöÙBáõE©8|èxµ-ý°0¦9W³È*TÔFÔ!N¹ËÛgd »±b{¿Z2»i¢a²ù1ìÄPÅ~ñb¯Ó7:åDªë*éÐãtLóä=
+Vø¥zHë¶s!TMDè=tIpï7D¿s¢Ì*´¶¡~·SEÞUDȨÓX5ÓüêèQò`¶EX§ÄF.O5øPØPþpDúÄêè^ÿ#ÀêºÇSê7vúh}ÿvÿi©¡gͳ@±"n?_Ú<O½ûå§å{Z)ôðv¶´âONc+²ÐºlËήgn$?âZeó)
ÄBÅFGB'¦OV.^ÒàèvV$Z$
ÉÑA9K)ab¥©=Æ?2òút+#ûJ7ÇÞ+X¤Kô:À§ÉäÃON'ÜTYÿ&¢RUûsÝ=Ú[m3×,úzÄfJd
P¶ý}+·i/4EýÚéá¦ÍHPénáS
ÚL"RÌ1é>ÊÅrßíÂ>6lºJ±ìDÖùHfÐhdª'ÝÄX³ûüå2EEùt|jtä÷ãURJº8b÷ö)ß^?'´ú'Á
+÷
C(]>09âr69LÕdس
yâ)ìR5lÜY_QÜM¸âÄú )L¥ùÃ'ÔW}V@¤»úûòÓüæ æ{8ð¯þUNrta÷ñJõwVt(qëUS,aÖ.>-]¾GÞÜMmî_½W+ÞÃç$kO»-ú6#Å}ÎÇZ>)¹K Û[GîZ6©áwñC_ne%j Æô[oK)Wô³Tø¾Åx±U"Ãá
¥{2uÏQ¼ZÑ÷¶4ú!E]13ÂÓñ>/Á5SJ¡g«K[ýÇÂÂÄâHOZj&c9õ`uÜÔyÿB"GkHLòÁÓû
+³N3
ù(~;¦þ
+k$÷Çû¯åTMÌh6IºÈ1«ÈÕ¥5z\Ú;v0e!^hs"óÞ\OõCV¬¥c×ãh"Ñg¯ñÄ|Ë««7í&ò§9ÙÂÝw({>ÉÝtÁVM¬ì4¦ð
¹phähÍgÞÇÅâbÜäjò5Açü *@°9c»åOx?òR/Wà
+sG²<ä_/W-O¯
adOõü4ßÝX¤MFä½îy®xÇÖk}*qÒ¹ÆôJ ?E `4½He®a9pðícñ+*áw¬.fÅ$¸´ÆçïFí²½ÇßÖ$0(H#v=Ò$dêÞ <ùÛd-%ÿn½ËÁíÑ+êØÐ[ªï+¿áÌÉ9ch¡7Êx_´©¿ C@\õü!Ýs©ü18+©(cùà
Õèòýëy
I+N`» cz¹äûmêè~êð1×zEQ¹¼¯µÎõ=F÷$ÖàÔI}I 1ÙÖê{òýk<3yD;ÝñxaÛ
+ð*¿HTòMc7a»¨Pý*å*Hºã/¼Â{¸Æ:]¨Ü'~LçF1
Î EVùÌK¹ýByv¥ïh,zÔá}\Æñ¾ùçyòÛ1×LêûÅfæ¾_vQ2ai»R\§ßäkÀðW%JØïXÑd+7H¤©®½9
+c¯Ì82Qôÿá6+endstream
+endobj
+42 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-29 -960 1116 775]
+/FontName/NENDNC+CMSY10
+/ItalicAngle -14.035
+/StemV 85
+/FontFile 41 0 R
+/Flags 68
+>>
+endobj
+41 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 777
+/Length2 594
+/Length3 533
+/Length 1156
+>>
+stream
+xÚSUÖuLÉOJuËÏ+Ñ5Ô3´Rpö
44P0Ô3àRUu.JM,ÉÌÏsI,IµR0´´4Tp,MW04U00·22°25çâRUpÎ/¨,ÊLÏ(QÐpÖ©2WpÌM-ÊLNÌSðM,ÉHÍ£ZR©§ à£ÒR¬ZZT¢ÇÅeh¨\¢Çrg^Z¾9D8¥´+@W¦äçåT*¤¤¦qéûåmKº
dgaqºán¥99~¹ ãÁ
!S U[PZZ¤àZ®4<â8ßÔÌÒ\tYÏÄÌdÇôT]C=cSDf±[fEjJ@fIrBZbNq*X<5/Ý)Àà;DßÏÕÏÅÏYµÙÄÌÊTr0ßÁSQf
B´!P!ÂX±h¶¹æ%ç§dæ¥+)$%VrSgªPm¨Z¡Zt²¾^^~ P0ljÒò¸@ñjhn¡ ÜÔ¦Wò+ªu,t-Í&)Ö*ÍË,,MõtQ0500°0x-¹´¨(5¯Áã§e65µ"5köÑLÆxYí¯ _\U
+ßuâÜÍ
³Ô6JúoÄÚï7=öÜãÚ:vÌ<sOþ¦éÖ9ÇvØÜ÷WÞwüÌ5ÆFÉ'9î¼?»×
é-Ãâöd_ùW¢¾,î
+[â¨cÍþ×ëÂê÷æÿ®jIØï.±á¸M¸ 뵩EeÁ~¿?vâ[APE_:óî¸'SÛOÉZÝ0¸:}Ó9NÖÕmï}^ê5QËÅþ¬Ë÷ÃËþYmX%WáµîؽëxÊnÍå²Û¾³+ÉøæË?ðÉÖɽÈêsç³ûÿýþ¾ÇzM='ûxÍSózÕi|_Þ6oÍ£øó2ËsÃf§/hlÿÞzöÜÿ˦oüå[<»\¿7Ý/¬ÔäØÑÝÏ'íSØØRô}UÄÅÜ÷Ïpµm»aجÿN×GëëáùONEUýd[zÆïvåÕi}þo¼ë£æmýðä@ÇÙÆ3þ¯½Þt_!ýÉ8¡d3ãákûe»/[S·}ÎTeÿ¶¬×æw\¿ðÎ@ñ¸÷ïm\ê'Ïå[µ,úW$cïM¦ÉgÏ>6Vé|È)5]fkÚÓí"Y61|QÍÁ{BÁRÐÄçM¤}ÉmK®ñûk¾ØMW(úÄWÁ,æ-¢éYÅ¿Ï-VÉeÐ==kÌnÏ
:~§ö;]ê¸sææ÷Úν·WoÜÚ÷hetº7#[HÃ$ÆÒK±S¡ÆAßÇóZü
þOåÛô¢ò×
+g
¼<þi@!à5`XXTXÍÅ+endstream
+endobj
+1 0 obj
+<<
+/Creator( TeX output 2007.04.23:1418)
+/Producer(dvipdfm 0.13.2c, Copyright \251 1998, by Mark A. Wicks)
+/CreationDate(D:20070423141904+01'00')
+>>
+endobj
+5 0 obj
+<<
+/Type/Page
+/Resources 6 0 R
+/Contents[28 0 R 4 0 R 29 0 R 30 0 R]
+/Parent 70 0 R
+>>
+endobj
+32 0 obj
+<<
+/Type/Page
+/Resources 33 0 R
+/Contents[28 0 R 4 0 R 37 0 R 30 0 R]
+/Parent 70 0 R
+>>
+endobj
+70 0 obj
+<<
+/Type/Pages
+/Count 2
+/Kids[5 0 R 32 0 R]
+/Parent 3 0 R
+>>
+endobj
+39 0 obj
+<<
+/Type/Page
+/Resources 40 0 R
+/Contents[28 0 R 4 0 R 44 0 R 30 0 R]
+/Parent 71 0 R
+>>
+endobj
+46 0 obj
+<<
+/Type/Page
+/Resources 47 0 R
+/Contents[28 0 R 4 0 R 48 0 R 30 0 R]
+/Parent 71 0 R
+>>
+endobj
+71 0 obj
+<<
+/Type/Pages
+/Count 2
+/Kids[39 0 R 46 0 R]
+/Parent 3 0 R
+>>
+endobj
+50 0 obj
+<<
+/Type/Page
+/Resources 51 0 R
+/Contents[28 0 R 4 0 R 52 0 R 30 0 R]
+/Parent 72 0 R
+>>
+endobj
+54 0 obj
+<<
+/Type/Page
+/Resources 55 0 R
+/Contents[28 0 R 4 0 R 56 0 R 30 0 R]
+/Parent 72 0 R
+>>
+endobj
+72 0 obj
+<<
+/Type/Pages
+/Count 2
+/Kids[50 0 R 54 0 R]
+/Parent 3 0 R
+>>
+endobj
+58 0 obj
+<<
+/Type/Page
+/Resources 59 0 R
+/Contents[28 0 R 4 0 R 60 0 R 30 0 R]
+/Parent 73 0 R
+>>
+endobj
+62 0 obj
+<<
+/Type/Page
+/Resources 63 0 R
+/Contents[28 0 R 4 0 R 64 0 R 30 0 R]
+/Parent 73 0 R
+>>
+endobj
+66 0 obj
+<<
+/Type/Page
+/Resources 67 0 R
+/Contents[28 0 R 4 0 R 68 0 R 30 0 R]
+/Parent 73 0 R
+>>
+endobj
+73 0 obj
+<<
+/Type/Pages
+/Count 3
+/Kids[58 0 R 62 0 R 66 0 R]
+/Parent 3 0 R
+>>
+endobj
+3 0 obj
+<<
+/Type/Pages
+/Count 9
+/Kids[70 0 R 71 0 R 72 0 R 73 0 R]
+/MediaBox[0 0 595 842]
+>>
+endobj
+28 0 obj
+<<
+/Length 1
+>>
+stream
+
+endstream
+endobj
+30 0 obj
+<<
+/Length 1
+>>
+stream
+
+endstream
+endobj
+4 0 obj
+<<
+/Length 33
+>>
+stream
+1.00028 0 0 1.00028 72 769.82 cm
+endstream
+endobj
+74 0 obj
+<<
+>>
+endobj
+75 0 obj
+null
+endobj
+76 0 obj
+<<
+>>
+endobj
+2 0 obj
+<<
+/Type/Catalog
+/Pages 3 0 R
+/Outlines 74 0 R
+/Threads 75 0 R
+/Names 76 0 R
+>>
+endobj
+xref
+0 77
+0000000000 65535 f
+0000090280 00000 n
+0000092043 00000 n
+0000091696 00000 n
+0000091896 00000 n
+0000090444 00000 n
+0000008923 00000 n
+0000028122 00000 n
+0000027938 00000 n
+0000000009 00000 n
+0000033601 00000 n
+0000033415 00000 n
+0000000985 00000 n
+0000039334 00000 n
+0000039145 00000 n
+0000001886 00000 n
+0000046799 00000 n
+0000046611 00000 n
+0000002816 00000 n
+0000062083 00000 n
+0000061897 00000 n
+0000003717 00000 n
+0000065044 00000 n
+0000064858 00000 n
+0000004665 00000 n
+0000068015 00000 n
+0000067829 00000 n
+0000005649 00000 n
+0000091796 00000 n
+0000006633 00000 n
+0000091846 00000 n
+0000008825 00000 n
+0000090546 00000 n
+0000012202 00000 n
+0000075561 00000 n
+0000075375 00000 n
+0000008984 00000 n
+0000009711 00000 n
+0000012114 00000 n
+0000090727 00000 n
+0000015475 00000 n
+0000089011 00000 n
+0000088816 00000 n
+0000012264 00000 n
+0000013198 00000 n
+0000015376 00000 n
+0000090831 00000 n
+0000017841 00000 n
+0000015537 00000 n
+0000017797 00000 n
+0000091013 00000 n
+0000019992 00000 n
+0000017903 00000 n
+0000019926 00000 n
+0000091117 00000 n
+0000022074 00000 n
+0000020054 00000 n
+0000022008 00000 n
+0000091299 00000 n
+0000024172 00000 n
+0000022136 00000 n
+0000024117 00000 n
+0000091403 00000 n
+0000026107 00000 n
+0000024234 00000 n
+0000026052 00000 n
+0000091507 00000 n
+0000027876 00000 n
+0000026169 00000 n
+0000027810 00000 n
+0000090650 00000 n
+0000090935 00000 n
+0000091221 00000 n
+0000091611 00000 n
+0000091978 00000 n
+0000092000 00000 n
+0000092021 00000 n
+trailer
+<<
+/Size 77
+/Root 2 0 R
+/Info 1 0 R
+>>
+startxref
+92138
+%%EOF
Property changes on: puppetor/trunk/doc/howto.pdf
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/doc/howto.tex
===================================================================
--- puppetor/trunk/doc/howto.tex 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/doc/howto.tex 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,521 @@
+\documentclass{article}%
+\begin{document}%
+%
+\author{karsten.loesing@xxxxxxx}%
+\title{PuppeTor\\A Java-based Tor Simulator\\User's Guide}%
+\maketitle%
+%
+
+\section{Introduction}
+
+PuppeTor\footnote{The name signifies the metaphor of a puppeteer playing with
+puppets like this framework plays with Tor processes. This is a working title.
+Please feel free to suggest a better name for it.} is a Java framework that
+facilitates the
+configuration of a set of local Tor processes and the execution of automatic
+tests based on these processes. The intention is to make it easier for
+developers to
+analyze Tor's behavior in arbitrary network settings and to measure the
+effects of changes to the Tor source code. Due to the automation of
+configuration and execution, these experiments can be done in an unsupervised
+batch fashion.
+
+An application that makes use of this framework starts with setting up a set of
+pre-defined Tor processes: proxy, router, and directory. Though these
+configurations should work in most settings, they can be altered by adding or
+removing configuration entries.\footnote{This is not implemented yet, but it
+should not be too hard to do it.} After deciding whether the processes shall
+either create a private Tor network, or connect to the public Tor network,
+processes are started. Now the application can start clients and servers and
+perform requests using the local processes. In doing so, it can measure time
+intervals between events originating from Tor processes and it can synchronize
+with such events. Further, the application can re-configure processes during
+their execution using the Tor controller.\footnote{This is not implemented yet,
+but we already make heavy use of the controller, so that this addition is not a
+real problem.}
+
+There are two typical situations in which this framework can be useful:%
+\begin{enumerate}%
+\item Developers need to oversee the effects of their changes to the source code.
+Therefore, it is useful to have a clean setting of Tor nodes in a private
+network, so that all nodes are under full control of the developer.
+\item Developers might want to measure the real-world performance of certain Tor
+operations. Hence, they can set up nodes at the edge of the public Tor network
+and conduct performance measurements, maybe in a batch of some hundreds or
+thousands of runs.
+\end{enumerate}
+
+Of course, the applications described here are possible without this framework.
+But this framework has certain advantages over writing own configuration files
+and test scripts:%
+\begin{enumerate}
+\item It provides developers with pre-defined configurations of nodes.
+Especially the configuration of nodes in a private network with own directory
+nodes is not a trivial task.
+\item It takes away the need to implement synchronization of a test application
+with events created by Tor processes. This, too, is a non-trivial task and can,
+if not done properly, lead to deadlocks or inconsistent states (yes, this
+happened during development of the framework, too).
+\item It relieves the developer from the task to collect and merge log files.
+Typically, every Tor process produces its own log file, so that all files might
+need to be merged in chronological order to identify causal dependencies.
+\end{enumerate}
+
+Originally this framework has been designed to perform experiments on Tor hidden
+services. But it should be feasible for experiments on onion routing and other
+Tor services, too. If you have found an alternative usage for it, and maybe
+have changed or extended it to support your own development, please feel free to
+contribute your additions. And please report bugs.
+
+\section{Installation}
+
+PuppeTor requires a Tor executable and the Java Tor Controller API which can
+both be downloaded from the Subversion repository on the Tor
+homepage.\footnote{At the moment the Tor Controller API is included, because we
+had to make some changes to the Tor Controller code, so that the framework does
+not run with the current Tor Controller code. Maybe we should make these changes
+(bugs?) to the Tor Controller API before?}
+
+After downloading, you need to put the Tor executable to the base directory of
+this framework and make sure that it has the name \texttt{tor.exe} or
+\texttt{tor.sh}, respectively.\footnote{This is going to be configurable in the
+future.} Further, you need to have a Java Runtime Environment version 5 or
+higher installed on your machine.
+
+You can then start the examples with one of the following commands:
+
+\begin{verbatim}
+java -cp bin
+ de.uniba.wiai.lspi.puppetor.examples.AccessingPublicWebServerOverTor
+java -cp bin
+ de.uniba.wiai.lspi.puppetor.examples.AdvertisingHiddenServiceToPublicTorNetwork
+java -cp bin
+ de.uniba.wiai.lspi.puppetor.examples.AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork
+java -cp bin
+ de.uniba.wiai.lspi.puppetor.examples.AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork
+\end{verbatim}
+
+\section{Example 1: Accessing public Web server over Tor}
+
+The easiest way to use Tor is to anonymously access a public Web server. This is
+what we do in this first example.
+
+The network configuration consists only of one node and looks like this:
+\begin{itemize}
+\item Proxy with control port\footnote{Every node needs to open its control
+port, so that we can communicate with it.} 7001 and SOCKS port 7002.
+\end{itemize}
+
+%%%%% picture of network setting? %%%%%
+
+In the following, we will walk through the code that is necessary to configure
+this ``network'' and perform the request to a public Web server.
+
+We start with obtaining a \texttt{Network} instance which is the central place
+for each test run:
+
+\begin{verbatim}
+Network network = NetworkFactory.createNetwork("example1");
+\end{verbatim}
+
+Now we are ready to create the Tor proxy that we need to perform our request. As
+stated above it should listen on port 7001 for us as controller and on 7002 for
+us as SOCKS client. The name is given for logging purposes only:
+
+\begin{verbatim}
+network.createProxy("proxy", 7001, 7002);
+\end{verbatim}
+
+As we are fine with the pre-defined configuration (at least for the moment), we
+can write the configurations of all nodes (which is only our proxy in this case)
+to \texttt{torrc} files in the nodes' working directories:
+
+\begin{verbatim}
+network.writeConfigurations();
+\end{verbatim}
+
+Next we start the nodes of our network. (The separation of this call from the
+previous one is the possible investigation of the working directory content, if
+required. Starting nodes can fail for some reason and block our application
+forever. Therefore, we can provide a timeout in milliseconds after which we
+consider the operation as failed. Starting nodes is considered to be complete as
+soon as Tor opens its control port and we have connected to it:
+
+\begin{verbatim}
+network.startNodes(5000);
+\end{verbatim}
+
+From time to time, Tor cannot create a circuit without some kind of launching
+assistance. Especially in a private-network setting nodes need to reboot in
+order to refresh their directory information and be able to build circuits.
+Hence, we send the nodes a ``HUP'' signal in regular intervals until it tells us
+that it has opened a circuit. In the following operation we can configure how
+often we want to retry and how long we want to wait in between retries:
+
+\begin{verbatim}
+network.hupUntilUp(5, 10000);
+\end{verbatim}
+
+Now our Tor network is running.
+
+Next is the client that will perform requests using our proxy. PuppeTor contains
+a \texttt{ClientApplication} class which can start a thread to perform simple
+HTTP GET requests to a given address and port. We provide it with a name for
+logging purposes, the address and port to look up, and the SOCKS port of our
+proxy:
+
+\begin{verbatim}
+ClientApplication client = network.createClient("client",
+ "www.google.de", 80, 7002);
+\end{verbatim}
+
+Before starting the request we want to register for events coming from this
+client. This is necessary as requests are performed in a separate thread in the
+background, which allows more complex applications to perform multiple requests
+in parallel. Therefore, we implement the interface \texttt{EventListener}
+and its method \texttt{handleEvent(Event)}. In this method we are interested in
+the two events \texttt{Event.APPLICATION\_SENDING\_REQUEST} and
+\texttt{Event.APPLICATION\_REPLY\_RECEIVED}. What we do with these events is
+application-specific. In our case we record the time of the first event and
+subtract it from the time of the second event to obtain the round-trip time of
+our request:
+
+\begin{verbatim}
+EventListener clientEventListener =
+ new EventListener() { // ... };
+\end{verbatim}
+
+Next we obtain a reference on the \texttt{EventManager} which handles all
+asynchronous events coming from the Tor processes:
+
+\begin{verbatim}
+EventManager manager = network.getEventManager();
+\end{verbatim}
+
+We add our event handler to the event manager with the client as event subject:
+
+\begin{verbatim}
+manager.addEventListener(client, clientEventListener);
+\end{verbatim}
+
+Finally, we can perform the requests to the public server. Just in case that a
+request fails or times out, we state that we are willing to make three tries
+with a timeout of 20 seconds each, and that further requests should be aborted
+as soon as one request succeeds (last parameter \texttt{true}).
+
+\begin{verbatim}
+client.performRequest(3, 20000, true);
+\end{verbatim}
+
+Due to the asynchronous performing of requests, we need to explicitly wait for
+the requests to be completed. We can do this by invoking a method on the event
+manager that blocks the invoking thread until a certain event is received. In
+this case we want to be blocked until the event
+\texttt{Event.APPLICATION\_REQUESTS\_PERFORMED} is received from our client:
+
+\begin{verbatim}
+manager.waitForAnyOccurence(client,
+ Event.APPLICATION_REQUESTS_PERFORMED);
+\end{verbatim}
+
+At last we shut down the proxy:
+
+\begin{verbatim}
+network.shutdownNodes();
+\end{verbatim}
+
+\section{Example 2: Advertising hidden service to public Tor network}
+
+The second-easiest example is to advertise a hidden service to the public Tor
+network, but not perform any requests to it. In fact this can be a useful test
+to measure publication times of rendezvous service descriptors.
+
+The network configuration again consists only of one proxy node:
+\begin{itemize}
+\item Proxy with control port 7001 and SOCKS port 7002.
+\end{itemize}
+
+%%%%% picture of network setting? %%%%%
+
+We start again with creating a network and a proxy node. But this time we keep a
+reference on the proxy node:
+
+\begin{verbatim}
+Network network = NetworkFactory.createNetwork("example2");
+ProxyNode proxy = network.createProxy("proxy", 7001, 7002);
+\end{verbatim}
+
+Next we extend the configuration of the proxy by a hidden service entry. The
+hidden service gets the name \texttt{hidServ} which is used for its working directory
+and for logging purposes. It expects that the real service will run on
+\texttt{localhost:7001}, but announces to Tor that it runs on the virtual port
+80:
+
+\begin{verbatim}
+proxy.addHiddenService("hidServ", 7005, 80);
+\end{verbatim}
+
+The next step stays the same. We write the configuration of our proxy to its
+\texttt{torrc} file.
+
+\begin{verbatim}
+network.writeConfigurations();
+\end{verbatim}
+
+Before starting the proxy, we create and register an event listener that informs
+us about two events: when Tor opens a circuit
+(\texttt{Event.NODE\_CIRCUIT\_OPENED}) and when it has published a rendezvous
+service descriptor (\texttt{Event.NODE\_RSD\_PUBLISHED}).
+
+\begin{verbatim}
+EventListener proxyEventListener = new EventListener() { // ... }
+\end{verbatim}
+
+Again we register this event listener at the event manager, but this time with
+the proxy as event subject.
+
+\begin{verbatim}
+EventManager manager = network.getEventManager();
+manager.addEventListener(proxy, proxyEventListener);
+\end{verbatim}
+
+The next steps are similar to the first example. We need to start the proxy and
+maybe send ``HUP'' signals until it has built a circuit.
+
+\begin{verbatim}
+network.startNodes(5000);
+network.hupUntilUp(5, 10000));
+\end{verbatim}
+
+As there is no event we could wait for, we fall asleep for five minutes to
+observe publication of rendezvous service descriptors.
+
+\begin{verbatim}
+Thread.sleep(5L * 60L * 1000L);
+\end{verbatim}
+
+At the end we shut down the proxy.
+
+\begin{verbatim}
+network.shutdownNodes();
+\end{verbatim}
+
+\section{Example 3: Advertising and accessing hidden service over public Tor
+network}
+
+This third example is in fact the first for which this framework originally has
+been built. The setting consists of two proxies that are connected to the
+public Tor network, a server that is connected as hidden server to the first
+proxy, and a client that performs requests using the second proxy. This scenario
+can be used to measure real-world round-trip times for requests to hidden
+services.
+
+The network configuration consists of two proxy nodes and looks like this:
+\begin{itemize}
+\item Proxy \texttt{proxy1} with control port 7001 and SOCKS port 7002.
+\item Proxy \texttt{proxy2} with control port 7011 and SOCKS port 7012.
+\item Server that listens on port 7005 and is registered as hidden server at
+\texttt{proxy1}.
+\item Client that performs requests using SOCKS port 7012, i.e. over
+\texttt{proxy2}.
+\end{itemize}
+
+%%%%% picture of network setting? %%%%%
+
+The example starts similar to the previous example with creating and configuring
+two proxy nodes. A hidden service is configured at the first proxy. Then
+configurations are written and nodes started until all of them have built a
+circuit.
+
+\begin{verbatim}
+Network network = NetworkFactory.createNetwork("example3");
+ProxyNode proxy1 = network.createProxy("proxy1", 7001, 7002);
+network.createProxy("proxy2", 7011, 7012);
+proxy1.addHiddenService("hidServ", 7005, 80);
+network.writeConfigurations();
+network.startNodes(5000);
+network.hupUntilUp(5, 10000);
+\end{verbatim}
+
+In contrast to the previous example we want to wait until the first proxy has
+successfully uploaded a rendezvous service descriptor to the directory before
+performing a request. From experience we can say that hidden service
+initialization takes most part of the time. We want to wait for this event from
+the first proxy as source for at most 3 minutes:
+
+\begin{verbatim}
+EventManager manager = network.getEventManager();
+manager.waitForAnyOccurence(proxy1, Event.NODE_RSD_PUBLISHED,
+ 3L * 60L * 1000L);
+\end{verbatim}
+
+As soon as the first descriptor has been published, we can initialize both,
+server and client. In contrast to the first example the client will perform an
+empty HTTP GET on the hidden server's onion address instead of a public Web
+server. The server will answer with an empty HTTP OK:
+
+\begin{verbatim}
+ServerApplication server = network.createServer("server", 7005);
+String onionAddress = proxy1.getOnionAddress("hidServ");
+ClientApplication client = network.createClient("client", onionAddress,
+ 80, 7012);
+\end{verbatim}
+
+For convenience we create a single event listener for both, client and server.
+It can distinguish client and server events by their type. We are interested in
+the events \texttt{Event.APPLICATION\_SENDING\_REQUEST} which is fired from the
+client before sending a request, \texttt{Event.APPLICATION\_REQUEST\_RECEIVED}
+which is fired by the server when a request is received, and
+\texttt{Event.APPLICATION\_REPLY\_RECEIVED}
+which is again fired by the client when it has received a reply. However, we
+need to register this event listener for both event sources, client and server.
+
+\begin{verbatim}
+EventListener clientAndServerEventListener = new EventListener() { // ... }
+manager.addEventListener(client, clientAndServerEventListener);
+manager.addEventListener(server, clientAndServerEventListener);
+\end{verbatim}
+
+Now we instruct the server to listen for incoming requests and the client to
+perform such requests.
+
+\begin{verbatim}
+server.listen();
+client.performRequest(5, 45000, true);
+\end{verbatim}
+
+We wait for the client to complete all requests.
+
+\begin{verbatim}
+manager.waitForAnyOccurence(client,
+ Event.APPLICATION_REQUESTS_PERFORMED);
+\end{verbatim}
+
+Finally, we shut down all nodes.
+
+\begin{verbatim}
+network.shutdownNodes();
+\end{verbatim}
+
+\section{Example 4: Advertising and accessing hidden service over private Tor
+network}
+
+The last example is by far the most complex. It involves setting up a private
+Tor network including own directory nodes. The overall task stays the same as in
+the previous example: perform a request to a hidden service and see what
+happens. In this case performance does not mean too much, because there is
+no network latency on the local host. But in contrast to a public-network
+setting one could potentially observe what happens on all routers and directory
+nodes during the request.
+
+The network configuration now consists of three router nodes, which are used for
+performing onion routing and misused as proxies, two directory nodes, a client,
+and a server:
+\begin{itemize}
+\item Router \texttt{router1} with control port 7021, SOCKS port 7022, OR port
+7023, and dir port 7024.
+\item Router \texttt{router2} with control port 7031, SOCKS port 7032, OR port
+7033, and dir port 7034.
+\item Router \texttt{router3} with control port 7041, SOCKS port 7042, OR port
+7043, and dir port 7044.
+\item Directory \texttt{dir1} with control port 7051, SOCKS port 7052, OR port
+7053, and dir port 7054.
+\item Directory \texttt{dir2} with control port 7061, SOCKS port 7062, OR port
+7063, and dir port 7064.
+\item Server that listens on port 7025 and is registered as hidden server at
+\texttt{router1}.
+\item Client that performs requests using SOCKS port 7042, i.e. over
+\texttt{router3}.
+\end{itemize}
+
+%%%%% picture of network setting? %%%%%
+
+Creating and configuring these nodes is done similar to all previous examples
+and requires no further explanation:
+
+\begin{verbatim}
+Network network = NetworkFactory.createNetwork("example4");
+RouterNode router1 = network.createRouter("router1", 7021, 7022, 7023, 7024);
+network.createRouter("router2", 7031, 7032, 7033, 7034);
+network.createRouter("router3", 7041, 7042, 7043, 7044);
+network.createDirectory("dir1", 7051, 7052, 7053, 7054);
+network.createDirectory("dir2", 7061, 7062, 7063, 7064);
+router1.addHiddenService("hidServ", 7025, 80);
+\end{verbatim}
+
+However, the next step does require further explanation. The main difficulty
+lies in the fact that all nodes need to be configured so that they accept our
+own directory nodes instead of the pre-configured directory nodes from the
+public Tor network. This configuration requires the fingerprints of all
+directory nodes. These fingerprints are written to disk by the directory nodes
+as soon as they are started. But the directories need to be configured before
+being started, too, in order to prevent them from becoming part of the public
+Tor network. And now we have the chicken or the egg dilemma.
+
+The non-trivial solution is to configure the directory nodes with a fake
+directory configuration and start them using the \texttt{--list-fingerprint}
+option. Hence they write a \texttt{fingerprint} file to disk and shut down
+immediately. This fingerprint can be read, and all nodes can be
+configured to use the directory using this fingerprint.
+
+A second, non-trivial task is to authorize routers and directory nodes.
+Therefore, an authoritative directory needs to know all fingerprints of
+authorized nodes. They are stored in the \texttt{approved-routers} file in the
+working directory of the directory node.
+
+The complete task is encapsulated in the following method for convenience.
+However, all operations could also be performed directly on the nodes, if
+required.
+
+\begin{verbatim}
+network.configureAsPrivateNetwork();
+\end{verbatim}
+
+Afterwards, the process is more or less similar to the previous example:
+
+\begin{verbatim}
+network.writeConfigurations();
+network.startNodes(5000);
+network.hupUntilUp(8, 10000);
+EventManager manager = network.getEventManager();
+manager.waitForAnyOccurence(router1, Event.NODE_RSD_PUBLISHED,
+ 3L * 60L * 1000L);
+ServerApplication server = network.createServer("server", 7025);
+String onionAddress = router1.getOnionAddress("hidServ");
+ClientApplication client = network.createClient("client", onionAddress,
+ 80, 7042);
+EventListener clientAndServerEventListener = new EventListener() {
+manager.addEventListener(client, clientAndServerEventListener);
+manager.addEventListener(server, clientAndServerEventListener);
+server.listen();
+client.performRequest(5, 45000, true);
+manager.waitForAnyOccurence(client,
+ Event.APPLICATION_REQUESTS_PERFORMED);
+network.shutdownNodes();
+\end{verbatim}
+
+\section{Known issues}
+
+There is already a small list of problems and open questiosn that require more
+investigation:
+
+\begin{itemize}
+\item When \texttt{ProxyNode}s are used in a private-network setting, the
+application does not succeed. What configuration entry is missing in
+\texttt{ProxyNode} that is present in \texttt{RouterNode}? Or is it a Tor
+problem?
+%
+\item When logging to stdout is set to something lower than \texttt{notice}, the
+application does not succeed. This is not a big problem, because we can parse
+the output that we get from the controller. But maybe it's a problem of Tor tat
+we should fix?
+%
+\item From time to time there is a FileNotFoundException when starting the
+private-network example.
+
+\item Fight the TODOs\ldots
+
+\item Complete logging and documentation for the impl package.
+\end{itemize}
+
+\end{document}
\ No newline at end of file
Property changes on: puppetor/trunk/doc/howto.tex
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/doc/logging
===================================================================
--- puppetor/trunk/doc/logging 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/doc/logging 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,49 @@
+
+
+Notice: Logging has not been implemented consistently over all classes!
+
+
+
+=======================
+= Logging Conventions =
+=======================
+
+Logger names
+============
+
+Logger names consist of (1) the fully-qualified class name of the class that
+contains the logging statement and (2) the system component in the network that
+executes the given class.
+
+This leads to two possibilities for logger names:
+1. <fully-qualified class name>.<system component>
+2. <system component>.<fully-qualified class name>
+
+We decided to use the second schema to facilitate observing the operations of a
+single node or application in the network.
+
+
+Log Levels
+==========
+
+The following log levels are used for the given occurences:
+
+SEVERE
+- an error has occured that requires the test to be aborted immediately
+
+WARNING
+- an error has occured, but the test may continue
+
+INFO
+- an important change has occured in the test (e.g. all nodes started), mostly
+concerning Network
+
+FINE
+- some state, i.e. variable value, has changed
+
+FINER
+- entering and exiting a method, throwing an exception
+- some intermediate place in a method
+
+FINEST
+- redirected automatic output, e.g. from Tor
Property changes on: puppetor/trunk/doc/logging
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/license
===================================================================
--- puppetor/trunk/license 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/license 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,66 @@
+===============================================================================
+PuppeTor - A Java-based Tor Simulator - is distributed under this license:
+
+Copyright (c) 2007, Karsten Loesing
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+ * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+===============================================================================
+The Tor controller demonstration code is distributed under this license:
+
+Copyright (c) 2005, Nick Mathewson, Roger Dingledine
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+ * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Added: puppetor/trunk/logging.properties
===================================================================
--- puppetor/trunk/logging.properties 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/logging.properties 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,4 @@
+handlers= java.util.logging.ConsoleHandler
+java.util.logging.ConsoleHandler.level = FINEST
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+.level= FINEST
\ No newline at end of file
Property changes on: puppetor/trunk/logging.properties
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/readme
===================================================================
--- puppetor/trunk/readme 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/readme 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,8 @@
+This is a Java framework that facilitates the configuration of a set of local
+Tor processes and the execution of automatic tests based on these processes. The
+intention is to make it easier for developers to analyze Tor's behavior in
+arbitrary network settings and to measure the effects of changes to the Tor
+source code. Due to the automation of configuration and execution, these
+experiments can be done in an unsupervised batch fashion.
+
+For more information, read the how-to document in doc/howto.pdf .
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,55 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>ClientApplication</code> can be used to simulate simple
+ * <code>HTTP GET</code> requests by a virtual local client. Therefore, an
+ * address and a port are given wo which the client shall connect. Requests are
+ * performed by a background thread, so that multiple requests could be
+ * performed at the same time.
+ *
+ * @author kloesing
+ */
+public interface ClientApplication extends EventSource {
+
+ /**
+ * <p>
+ * Arranges for the requests being performed by a thread in the background
+ * and returns immediately. This thread will try for <code>retries</code>
+ * times to make the request with a timeout of
+ * <code>timeoutForEachRetry</code> millis each. If an attempt is not
+ * successful, the thread nevertheless waits for the timeout to expire. If
+ * <code>stopOnSuccess</code> is set to <code>true</code>, the thread
+ * will quit performing requests after the first successful request.
+ * </p>
+ *
+ * <p>
+ * For each sent request the application fires an
+ * <event>Event.APPLICATION_SENDING_REQUEST</code> event. On receiving a
+ * reply it fires an event of type <code>Event.APPLICATION_REPLY_RECEIVED</code>,
+ * if a request is not successful or times out, an <code>Event.APPLICATION_GAVE_UP_REQUEST</code>
+ * event is fired. After all requests have been performed (either
+ * successfully, or not) an event of type <code>Event.APPLICATION_REQUESTS_PERFORMED</code>
+ * is fired.
+ * </p>
+ *
+ * @param retries
+ * The number of retries that this client will perform. Must be
+ * <code>1</code> or greater.
+ * @param timeoutForEachRetry
+ * The timeout for each request. If a request is not successful,
+ * the thread nevertheless waits for the timeout to expire. Must
+ * not be negative.
+ * @param stopOnSuccess
+ * If set to <code>true</code>, the client quits performing
+ * requests after the first successful request, if <code>false</code>
+ * it continues until all <code>retries</code> have been
+ * processed.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public void performRequest(int retries, long timeoutForEachRetry,
+ boolean stopOnSuccess);
+
+ // TODO we need some way to interrupt performing requests
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,43 @@
+package de.uniba.wiai.lspi.puppetor;
+
+import java.util.Set;
+
+/**
+ * A DirectoryNode represents a Tor process that acts as RouterNode and is
+ * further a directory authoritative server for the (private) Tor network. It
+ * inherits most of the configuration and behavior from RouterNode and adds some
+ * directory-specific configurations and behavior.
+ *
+ * @author kloesing
+ */
+public interface DirectoryNode extends RouterNode {
+
+ /**
+ * Combines the node's fingerprint to a DirServer string that can be used to
+ * configure this or other nodes to use this node as directory server.
+ *
+ * @return DirServer string to configure a node to use this node as
+ * directory server.
+ * @throws TorProcessException
+ * Thrown if a problem occurs when determining the node's
+ * fingerprint.
+ */
+ public abstract String determineDirServerString()
+ throws TorProcessException;
+
+ /**
+ * Writes the given set of routers (either directories or routers) to the
+ * <code>approved-routers</code> file. This will confirm to directory
+ * clients, that the given routers can be trusted.
+ *
+ * @param approvedRouters
+ * The set of approved routers to be written.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed as parameter.
+ * @throws TorProcessException
+ * Thrown if the <code>approved-routers</code> file cannot be
+ * written to disk.
+ */
+ public void writeApprovedRouters(Set<String> approvedRouters)
+ throws TorProcessException;
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,87 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * An Event is created for every state change of an asynchronous system
+ * component, e.g. a Tor process or a client/server application running as
+ * thread in the background. In contrast to <code>NodeState</code> or
+ * <code>NetworkState</code> an <code>Event</code> cannot be a pre- or
+ * postconditions for a method invocation. There is no prescribed order in which
+ * events are fired by a certain process or application. Some events can be
+ * fired only once, others possibly multiple times. All management operations
+ * for events are contained in the <code>EventManager</code>.
+ *
+ * TODO change from enum to hierarchie of interfaces/classes, because enums do
+ * not support a type hierarchie which would be very useful!
+ *
+ * TODO add methods and attributes to events to include additional information
+ * (e.g. time of occurence)!
+ *
+ * @author kloesing
+ */
+public enum Event {
+
+ /**
+ * The node which fires this event has successfully opened a circuit. This
+ * event can only be fired once.
+ */
+ NODE_CIRCUIT_OPENED,
+
+ /**
+ * The node which fires this event has opened its control port. This event
+ * can only be fired once.
+ */
+ NODE_CONTROL_PORT_OPENED,
+
+ /**
+ * The node which fires this event has successfully published a rendezvous
+ * service descriptor. This event can be fired multiple times.
+ */
+ NODE_RSD_PUBLISHED,
+
+ /**
+ * The network fires this event when all nodes have opened their control
+ * ports. This event can only be fired once.
+ */
+ NETWORK_CONTROL_PORTS_OPENED,
+
+ /**
+ * The network fires this event when all nodes have successfully opened a
+ * circuit. This event can only be fired once.
+ */
+ NETWORK_CIRCUITS_ESTABLISHED,
+
+ /**
+ * The client application which fires this event has received a reply to a
+ * previously sent request. This event can be fired multiple times.
+ */
+ APPLICATION_REPLY_RECEIVED,
+
+ /**
+ * The client application which fires this event has sent a request. This
+ * event can be fired multiple times.
+ */
+ APPLICATION_SENDING_REQUEST,
+
+ /**
+ * The server application which fires this event has received a request and
+ * sent a reply to it. This event can be fired multiple times.
+ */
+ APPLICATION_REQUEST_RECEIVED,
+
+ /**
+ * The client application which fires this event has given up waiting for
+ * the reply to a previously sent request. This event can be fired multiple
+ * times.
+ */
+ APPLICATION_GAVE_UP_REQUEST,
+
+ /**
+ * The client application which fires this event has completed a series of
+ * requests, whether they were successful or not. This event can be fired
+ * multiple times. TODO Can it really be fired multiple times? Is it
+ * possible to send multiple request series from the same client? Should it
+ * be possible?
+ */
+ APPLICATION_REQUESTS_PERFORMED
+
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventListener.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventListener.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventListener.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,20 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * This interface must be implemented by any object in a test application that
+ * shall be registered as event listener.
+ *
+ * @author kloesing
+ */
+public interface EventListener {
+
+ /**
+ * Is invoked when an asynchronous event is fired by the source (or one of
+ * the sources) for which this listener was registered.
+ *
+ * @param event
+ * The event that was fired.
+ */
+ public void handleEvent(Event event);
+
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventListener.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,190 @@
+package de.uniba.wiai.lspi.puppetor;
+
+import java.util.List;
+
+/**
+ * The EventManager is the central place for a test run to manage asynchronous
+ * events by Tor processes and client/server applications running as threads in
+ * the background. A test application can either register event listeners to be
+ * notified asynchronously about events when they occur, or synchronize with an
+ * event by being blocked until the certain event occurs.
+ *
+ * @author kloesing
+ */
+
+public interface EventManager {
+
+ /**
+ * Registers the given <code>listener</code> as event listener for events
+ * originating from the given <code>source</code>. This method returns a
+ * list of all previously fired events by this source, so that each event
+ * fired by this source is either included in the returned list or
+ * signalized in a later invocation on the event listener, but not in both.
+ * This prevents race conditions by eliminating the gap between registration
+ * of an event handler and asking if an event has been fired before
+ * registering. This method can be invoked in any node or network state.
+ *
+ * @param source
+ * The source of events that the listener is interested in. May
+ * not be <code>null</code>.
+ * @param listener
+ * The listener that wants to be notified about events from the
+ * given <code>source</code>. If the <code>listener</code>
+ * is already registered for the same <code>source</code>,
+ * nothing happens, i.e. the <code>listener</code> will not
+ * receive multiple invocations for the same event. May not be
+ * <code>null</code>.
+ * @return A list of all previously fired events for the given
+ * <code>source</code>. If no event has been fired before, an
+ * empty list is returned instead of <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed for either of the
+ * parameters.
+ */
+ public abstract List<Event> addEventListener(EventSource source,
+ EventListener listener);
+
+ /**
+ * Returns the list of all previously observed events from the given
+ * <code>source</code>.
+ *
+ * @param source
+ * The source of the events that the invoking thread is
+ * interested in. May not be <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed as parameter.
+ * @return List of all previously observed events from the given
+ * <code>source</code>.
+ */
+ public abstract List<Event> getEventHistory(EventSource source);
+
+ /**
+ * Returns whether the given <code>event</code> has been observed from the
+ * given <code>source</code> before, or not.
+ *
+ * @param source
+ * The source of the event that the invoking thread is interested
+ * in. May not be <code>null</code>.
+ * @param event
+ * The event that the invoking thread is interested int. May not
+ * be <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed for either of the
+ * parameters.
+ * @return <code>true</code> if the event has been observed from the
+ * source before, <code>false</code> otherwise.
+ */
+ public abstract boolean hasEventOccured(EventSource source, Event event);
+
+ /**
+ * Removes the given <code>listener</code> as event listener from all
+ * previously registered sources. If this listener is not registered for any
+ * source, nothing happens. This method can be invoked in any node or
+ * network state.
+ *
+ * TODO should we include the source as parameter, too, to have even more
+ * control over removing listeners from sources? Or should we overload the
+ * method?
+ *
+ * @param listener
+ * The listener that shall be removed from the list of registered
+ * listeners. May not be <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed as parameter.
+ */
+ public abstract void removeEventListener(EventListener listener);
+
+ /**
+ * Checks if the given <code>event</code> has been observed from the given
+ * <code>source</code> before; if not, blocks the invoking thread until
+ * the next event is fired from that source. Note that this method does not
+ * restrict waiting to a timeout, so that it could potentially block
+ * forever! This method can be invoked in any node or network state.
+ *
+ * @param source
+ * The source of the event that the invoking thread is willing to
+ * wait for. May not be <code>null</code>.
+ * @param event
+ * The event that the invoking thread is willing to wait for from
+ * the given <code>source</code>. May not be <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed for either of the
+ * parameters.
+ */
+ public abstract void waitForAnyOccurence(EventSource source, Event event);
+
+ /**
+ * Checks if the given <code>event</code> has been observed from the given
+ * <code>source</code> before; if not, blocks the invoking thread until
+ * the next event is fired from that source or the given timeout of
+ * <code>maximumTimeToWaitInMillis</code> millis has expired. This method
+ * can be invoked in any node or network state.
+ *
+ * @param source
+ * The source of the event that the invoking thread is willing to
+ * wait for. May not be <code>null</code>.
+ * @param event
+ * The event that the invoking thread is willing to wait for from
+ * the given <code>source</code>. May not be <code>null</code>.
+ * @param maximumTimeToWaitInMillis
+ * The maximum time to wait in milliseconds. A positive value or
+ * zero restricts waiting to this time. If this value is
+ * negative, we will wait potentially forever.
+ * @return <code>true</code> if an event of the given type has been fired
+ * by the source within the given timeout, <code>false</code>
+ * otherwise.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is passed for either of the
+ * parameters.
+ */
+ public abstract boolean waitForAnyOccurence(EventSource source,
+ Event event, long maximumTimeToWaitInMillis);
+
+ /**
+ * Blocks the invoking thread until the next <code>event</code> is fired
+ * from the given <code>source</code>. This method only waits for the
+ * next occurence of an event, regardless of previous occurrences. Note that
+ * this method does not restrict waiting to a timeout, so that it could
+ * potentially block forever! This method can be invoked in any node or
+ * network state.
+ *
+ * @param source
+ * The source of the event that the invoking thread is willing to
+ * wait for. May not be <code>null</code>.
+ * @param event
+ * The event that the invoking thread is willing to wait for from
+ * the given <code>source</code>. May not be <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed for either of the
+ * parameters.
+ */
+ public abstract void waitForNextOccurence(EventSource source, Event event);
+
+ /**
+ * Blocks the invoking thread until the next <code>event</code> is fired
+ * from the given <code>source</code> or the given timeout of
+ * <code>maximumTimeToWaitInMillis</code> millis has expired. This method
+ * only waits for the next occurence of an event, regardless of previous
+ * occurrences. This method can be invoked in any node or network state.
+ *
+ * @param source
+ * The source of the event that the invoking thread is willing to
+ * wait for. May not be <code>null</code>.
+ * @param event
+ * The event that the invoking thread is willing to wait for from
+ * the given <code>source</code>. May not be <code>null</code>.
+ * @param maximumTimeToWaitInMillis
+ * The maximum time to wait in milliseconds. A positive value or
+ * zero restricts waiting to this time. If this value is
+ * negative, we will wait potentially forever.
+ * @return <code>true</code> if an event of the given type has been fired
+ * by the source within the given timeout, <code>false</code>
+ * otherwise.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is passed for either of the
+ * parameters.
+ */
+ public abstract boolean waitForNextOccurence(EventSource source,
+ Event event, long maximumTimeToWaitInMillis);
+
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventSource.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventSource.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventSource.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,11 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * This marker interface is the super-interface of those interfaces that can be
+ * the source for events.
+ *
+ * @author kloesing
+ */
+public interface EventSource {
+ // this interface contains no methods, because it is a marker interface.
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventSource.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,324 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * A Network instance constitutes the central object of any test run and is
+ * aware of the node configuration. It creates all nodes for this configuration
+ * and is able to perform common operations on these nodes. Apart from the
+ * factory methods, all other operations could also be performed manually by an
+ * application using the appropriate interfaces.
+ *
+ * @author kloesing
+ */
+public interface Network extends EventSource {
+
+ /**
+ * <p>
+ * Configures the nodes in this network so that they can run in a private
+ * network and don't require public directory servers or onion routers from
+ * the Internet. This configuration should be done after configuring the
+ * nodes and before writing configurations to disk.
+ * </p>
+ *
+ * <p>
+ * The main requirement for this method lies in the fact that all nodes need
+ * to be configured so that they accept our own directory nodes instead of
+ * the pre-configured directory nodes from the public Tor network. This
+ * configuration requires the fingerprints of all directory nodes. These
+ * fingerprints are written to disk by the directory nodes as soon as they
+ * are started. But the directories need to be configured before being
+ * started, too, in order to prevent them from becoming part of the public
+ * Tor network. And now we have the chicken or the egg dilemma.
+ * </p>
+ *
+ * <p>
+ * The non-trivial solution is to configure the directory nodes with a fake
+ * directory configuration and start them using the
+ * <code>--list-fingerprint</code> option. Hence they write a
+ * <code>fingerprint</code> file to disk and shut down immediately. This
+ * fingerprint can be read, and all nodes can be configured to use the
+ * directory using this fingerprint.
+ * </p>
+ *
+ * <p>
+ * A second, non-trivial task is to authorize routers and directory nodes.
+ * Therefore, an authoritative directory needs to know all fingerprints of
+ * authorized nodes. They are stored in the \texttt{approved-routers} file
+ * in the working directory of the directory node.
+ * </p>
+ *
+ * <p>
+ * The complete task is encapsulated in this method for convenience.
+ * However, all operations could also be performed directly on the nodes, if
+ * required.
+ * </p>
+ *
+ * TODO check if we have enough directory and router nodes to build a
+ * private network? How many are required? 2 dirs and 3 routers?
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @throws TorProcessException
+ * Thrown if an I/O problem occurs while starting nodes with the
+ * <code>--list-fingerprint</code> option, reading files from
+ * the nodes' working directories, or writing the
+ * <code>approved-routers</code> files.
+ */
+ public abstract void configureAsPrivateNetwork() throws TorProcessException;
+
+ /**
+ * Creates a new client application, but does not yet perform a request.
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @param clientApplicationName
+ * The name for this client application, which is used for
+ * logging purposes only. May neither be null or a zero-length
+ * string.
+ * @param targetAddress
+ * The target for requests sent by this client application. Can
+ * be an IP address, a domain name, or an onion address. May
+ * neither be null or a zero-length string.
+ * @param targetPort
+ * The TCP port for requests sent by this client application. If
+ * the target address is an onion address, this port is the
+ * virtual port that the hidden service has announced. May not be
+ * negative or greater than 65535.
+ * @param socksPort
+ * The TCP port on which a local Tor process is waiting for
+ * incoming SOCKS requests. May not be negative or greater than
+ * 65535.
+ * @return Reference to the created client application.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract ClientApplication createClient(
+ String clientApplicationName, String targetAddress, int targetPort,
+ int socksPort);
+
+ /**
+ * Creates a new directory node and adds it to the network, but does not yet
+ * write its configuration to disk or start the corresponding Tor process.
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, and as node nickname. May
+ * neither be <code>null</code> or have zero or more than 19
+ * alpha-numeric characters.
+ * @param controlPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for a controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming SOCKS requests. May not be negative or greater
+ * than 65535.
+ * @param orPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming requests from other onion routers. May not be
+ * negative or greater than 65535.
+ * @param dirPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming directory requests. May not be negative or
+ * greater than 65535.
+ * @return Reference to the created directory node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract DirectoryNode createDirectory(String nodeName,
+ int controlPort, int socksPort, int orPort, int dirPort);
+
+ /**
+ * Creates a new proxy node and adds it to the network, but does not yet
+ * write its configuration to disk or start the corresponding Tor process.
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @param nodeName
+ * The name for this node, which is only used as name for the
+ * working directory and for logging purposes. May neither be
+ * <code>null</code> or have zero or more than 19 alpha-numeric
+ * characters.
+ * @param controlPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for a controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming SOCKS requests. May not be negative or greater
+ * than 65535.
+ * @return Reference to the created proxy node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract ProxyNode createProxy(String nodeName, int controlPort,
+ int socksPort);
+
+ /**
+ * Creates a new router node and adds it to the network, but does not yet
+ * write its configuration to disk or start the corresponding Tor process.
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @param nodeName
+ * The name for this node, which is used as name for the working
+ * directory, for logging purposes, and as node nickname. May
+ * neither be <code>null</code> or have zero or more than 19
+ * alpha-numeric characters.
+ * @param controlPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for a controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming SOCKS requests. May not be negative or greater
+ * than 65535.
+ * @param orPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming requests from other onion routers. May not be
+ * negative or greater than 65535.
+ * @param dirPort
+ * The TCP port on which the corresponding Tor process will wait
+ * for incoming directory requests which in fact are requests for
+ * the mirrored directory. May not be negative or greater than
+ * 65535.
+ * @return Reference to the created router node.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract RouterNode createRouter(String nodeName, int controlPort,
+ int socksPort, int orPort, int dirPort);
+
+ /**
+ * Creates a new server application, but does not start listening for
+ * incoming requests.
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @param serverApplicationName
+ * The name for this server application, which is used for
+ * logging purposes only. May neither be null or a zero-length
+ * string.
+ * @param serverPort
+ * The TCP port on which the server will wait for incoming
+ * requests. May not be negative or greater than 65535.
+ * @return Reference to the created server application.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract ServerApplication createServer(
+ String serverApplicationName, int serverPort);
+
+ /**
+ * Returns a reference on the (single) event manager for this network.
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @return Reference on the (single) event manager for this network.
+ */
+ public abstract EventManager getEventManager();
+
+ /**
+ * Returns the current network state.
+ *
+ * @return Current network state.
+ */
+ public abstract NetworkState getNetworkState();
+
+ /**
+ * <p>
+ * Sends a HUP signal to all nodes in the network in regular intervals and
+ * blocks the invoking thread until all nodes have reported to have
+ * successfully opened a circuit.
+ * </p>
+ *
+ * <p>
+ * First, the method waits for <code>hupInterval</code> millis for the
+ * nodes to have successfully opened a circuit. If they do not succeed
+ * within this time, a HUP signal is sent to all nodes and the method waits
+ * for another <code>hupInterval</code> millis. In total, the method sends
+ * at most <code>tries</code> HUP signals before giving up and returning
+ * with <code>false</code>. Thus, the maximum waiting time is
+ * <code>(tries + 1)</code> times <code>hupInterval</code>. As soon as
+ * all nodes have successfully opened circuits, the method returns with
+ * <code>true</code>.
+ * </p>
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @param tries
+ * The maximum number of HUP signals that are sent to the Tor
+ * processes. Negative values are not allowed. A value of zero
+ * means to wait only for the given time of
+ * <code>hupInterval</code> millis without sending a HUP
+ * signal. Typical values depend on the network being a public or
+ * private Tor network and range about 3 to 5 tries.
+ * @param hupInterval
+ * The time in millis that the method will wait between sending
+ * HUP signals. Negative values are not allowed. Typically,
+ * values should not be smaller than 10 seconds to permit Tor to
+ * stabilize.
+ * @throws IllegalArgumentException
+ * Thrown if a negative value is given for either
+ * <code>tries</code> or <code>hupInterval</code>.
+ * @throws TorProcessException
+ * Thrown if an I/O problem occurs while sending HUP signals.
+ * @return <code>true</code> if all nodes have reported to have
+ * successfully opened a circuit, <code>false</code> otherwise.
+ */
+ public abstract boolean hupUntilUp(int tries, long hupInterval)
+ throws TorProcessException;
+
+ /**
+ * Attempts to shut down all nodes. The method blocks until all shutdown
+ * requests have been sent and either returns, or throws the first exception
+ * that has been observed when shutting down nodes. The method can be
+ * assumed to return very quickly.
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @throws TorProcessException
+ * Thrown if an I/O problem occurs while shutting down the
+ * nodes.
+ */
+ public abstract void shutdownNodes() throws TorProcessException;
+
+ /**
+ * Attempts to start all nodes within a given timeout of
+ * <code>maximumTimeToWaitInMillis</code> millis. The method returns as
+ * soon as all nodes have started and opened their control port so that we
+ * can connect to them. It returns a boolean that states whether the
+ * operation was either successful or has timed out.
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @param maximumTimeToWaitInMillis
+ * The maximum time to wait in milliseconds. A positive value or
+ * zero restricts waiting to this time. Negative values are not
+ * allowed. Typical values are in the range of a few seconds.
+ * @return <code>true</code> if all nodes could be started successfully,
+ * <code>false</code> if a timeout has occured.
+ * @throws IllegalArgumentException
+ * Thrown if a negative value is given for
+ * <code>maximumTimeToWaitInMillis</code>.
+ * @throws TorProcessException
+ * Thrown if an I/O problem occurs while startint the nodes.
+ */
+ public abstract boolean startNodes(long maximumTimeToWaitInMillis)
+ throws TorProcessException;
+
+ /**
+ * Writes the configurations of all nodes in this network to disk. This
+ * method is assumed to return very quickly.
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @throws TorProcessException
+ * Thrown if an I/O problem occurs while writing to the nodes'
+ * working directories.
+ */
+ public abstract void writeConfigurations() throws TorProcessException;
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,32 @@
+package de.uniba.wiai.lspi.puppetor;
+
+import de.uniba.wiai.lspi.puppetor.impl.NetworkImpl;
+
+/**
+ * The <code>NetworkFactory</code> is an abstract factory that can create
+ * <code>Network</code> instances.
+ *
+ * TODO At the moment, this class uses the concrete class NetworkImpl to
+ * implement its only factory method. If we want to make this a real abstract
+ * factory, we need to replace the concrete constructor by reading the class
+ * name of the class implementing Network from a property file and invoking its
+ * constructor using reflection. Currently, this is the only place where we
+ * reference a class from the impl package.
+ *
+ * @author karsten
+ */
+public abstract class NetworkFactory {
+
+ /**
+ * Creates a new network that is required for a test run. The new network is
+ * initially unpopulated and creates its own working directory at
+ * test-env/randomTestID/.
+ *
+ * @param networkName
+ * Name of this network configuration.
+ * @return A new network instance.
+ */
+ public static Network createNetwork(String networkName) {
+ return new NetworkImpl(networkName);
+ }
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,41 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>NetworkState</code> constitutes the single state of a network
+ * that is the result of the <code>NodeState</code>s of all nodes in the
+ * network. In contrast to <code>Event</code> the network (and node) states
+ * depend only on the methods that have been invoked on these objects, and not
+ * on asynchronous state changes. Most operations of <code>Network</code>
+ * require a certain <code>NetworkState</code> as precondition and may ensure
+ * another <code>NetworkState</code> as postcondition. There is a prescribed
+ * order of states.
+ *
+ * @author kloesing
+ */
+public enum NetworkState {
+ /**
+ * The configurations of the nodes in the network have not been written to
+ * disk and can be changed. This is the initial state of a
+ * <code>Network</code>.
+ */
+ CONFIGURING_NODES,
+
+ /**
+ * The configurations of all nodes in the network have been written to disk
+ * and cannot be changed anymore, but the Tor processes have not been
+ * started, yet. This state could be useful to review the configurations
+ * that have been written to disk.
+ */
+ CONFIGURATIONS_WRITTEN,
+
+ /**
+ * The nodes in the network have been started and are running.
+ */
+ NODES_STARTED,
+
+ /**
+ * The nodes in the network had been started and shut down.
+ */
+ NODES_SHUT_DOWN
+
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,39 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>NodeState</code> constitutes the state of a single Tor node. In
+ * contrast to <code>Event</code> the node (and network) states depend only on
+ * the methods that have been invoked on these objects, and not on asynchronous
+ * state changes. Most operations of <code>ProxyNode</code> and its subclasses
+ * require a certain <code>NodeState</code> as precondition and may ensure
+ * another <code>NodeState</code> as postcondition. There is a prescribed
+ * order of states.
+ *
+ * @author kloesing
+ */
+public enum NodeState {
+
+ /**
+ * The node's configuration has not been written to disk and can be changed.
+ * This is the initial state of a <code>ProxyNode</code> or one of its
+ * subclasses.
+ */
+ CONFIGURING,
+
+ /**
+ * The node's configuration has been written to disk and cannot be changed
+ * anymore, but the Tor process has not been started, yet. This state could
+ * be useful to review the configuration that has been written to disk.
+ */
+ CONFIGURATION_WRITTEN,
+
+ /**
+ * The node has been started and is running.
+ */
+ RUNNING,
+
+ /**
+ * The node had been started and shut down.
+ */
+ SHUT_DOWN
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,164 @@
+package de.uniba.wiai.lspi.puppetor;
+
+import java.util.Set;
+
+/**
+ * A ProxyNode represents a Tor process that is configured as onion proxy, i.e.
+ * to relay traffic from a local application to the Tor network and vice versa,
+ * and does not route traffic on behalf of remote applications. It is the
+ * superclass for other node types that extend the configuration of a ProxyNode.
+ *
+ * @author kloesing
+ */
+public interface ProxyNode extends EventSource {
+
+ /**
+ * Adds hidden service entries to this node's configuration. This method can
+ * only be invoked while the node is in state
+ * <code>NodeState.CONFIGURING</code>. TODO Should this operation also be
+ * possible while the process is running? We could easily change the
+ * configuration via the controller.
+ *
+ * @param serviceName
+ * Name of the hidden service that will be used as name for the
+ * hidden service directory. May neither be <code>null</code>
+ * or a zero-length string.
+ * @param servicePort
+ * The TCP port on which the service will be available for
+ * requests. This can be different for the virtual port that is
+ * announced to clients. May not be negative or greater than
+ * 65535.
+ * @param virtualPort
+ * The virtual TCP port that this hidden service runs on as it is
+ * announced to clients. May not be negative or greater than
+ * 65535.
+ * @throws IllegalStateException
+ * Thrown if node is not in state
+ * <code>NodeState.CONFIGURING</code>.
+ * @throws IllegalArgumentException
+ * Thrown if an invalid value is given for either of the
+ * parameters.
+ */
+ public abstract void addHiddenService(String serviceName, int servicePort,
+ int virtualPort);
+
+ /**
+ * Adds the given set of DirServer configuration entries to this node's
+ * configuration. Note that as soon as one DirServer is configured, the node
+ * does not connect to an outside directory server of the public network any
+ * more!
+ *
+ * TODO allow invocation of this method only in correct state
+ *
+ * @param authorizedDirServerStrings
+ * A set of DirServer configuration entries that each contain one
+ * directory server that this node shall connect to. May not be
+ * <code>null</code>.
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> is passed as parameter.
+ */
+ public abstract void configureDirServers(
+ Set<String> authorizedDirServerStrings);
+
+ /**
+ * Returns the node's name.
+ *
+ * @return The node's name.
+ */
+ public abstract String getNodeName();
+
+ /**
+ * Returns the node's state.
+ *
+ * @return The node's state.
+ */
+ public abstract NodeState getNodeState();
+
+ /**
+ * Determines the onion address for the previously added hidden service with
+ * name <code>serviceName</code>. Requires that the node has been
+ * started, i.e. is in state <code>NodeState.RUNNING</code>.
+ *
+ * @param serviceName
+ * Name of the hidden service that has been used before to add
+ * the hidden service. May neither be <code>null</code> or a
+ * zero-length string.
+ * @return The onion address string consisting of 16 base32 chars plus
+ * ".onion".
+ * @throws IllegalArgumentException
+ * Thrown if <code>null</code> or a zero-length string is
+ * passed as parameter.
+ * @throws TorProcessException
+ * Thrown if either there does not exist a hidden service with
+ * the given <code>serviceName</code> as directory or if the
+ * <code>hostname</code> file could not be read.
+ */
+ public abstract String getOnionAddress(String serviceName)
+ throws TorProcessException;
+
+ /**
+ * Sends a HUP command to the process via its control port to restart it;
+ * can only be done if node has already been started!
+ *
+ * @throws TorProcessException
+ * Thrown if an I/O problem occurs while sending the HUP signal.
+ * @throws IllegalStateException
+ * Thrown if node is not in state <code>NodeState.RUNNING</code>.
+ */
+ public abstract void hup() throws TorProcessException;
+
+ /**
+ * Shuts down Tor process corresponding to this node immediately. This is
+ * done by sending the <code>SHUTDOWN</code> signal twice, so that nodes
+ * extending ProxyNode and have opened their OR port shutdown immediately,
+ * too.
+ *
+ * @throws IllegalStateException
+ * Thrown if node is not in state <code>NodeState.RUNNING</code>.
+ * @throws TorProcessException
+ * Thrown if an I/O problem occurs while sending the
+ * <code>SHUTDOWN</code> signal.
+ */
+ public abstract void shutdown() throws TorProcessException;
+
+ /**
+ * Starts the Tor process for this node and connects to the control port as
+ * soon as it is opened. <b>In order for this method to succeed it is
+ * absolutely necessary, that logging on the console is not changed in the
+ * node's configuration to a higher level than NOTICE, because the output is
+ * parsed to see when the control port is opened.</b>
+ *
+ * @param maximumTimeToWaitInMillis
+ * Maximum time in millis we will wait for the Tor process to be
+ * started and the control port being opened. If this value is
+ * negative or zero, we will wait potentially forever. TODO
+ * should we normalize behavior for negative timeouts to either
+ * throw an exception or wait forever consistently for the whole
+ * framework?
+ * @return <code>true</code> if the node could be started successfully,
+ * <code>false</code> otherwise.
+ * @throws IllegalStateException
+ * Thrown if node is not in state
+ * <code>NodeState.CONFIGURATION_WRITTEN</code>, i.e. if
+ * either configuration has not been written or the process has
+ * already been started.
+ * @throws TorProcessException
+ * Thrown if either the process could not be started, or the
+ * connection to the control port could not be established.
+ */
+ public abstract boolean startNode(long maximumTimeToWaitInMillis)
+ throws TorProcessException;
+
+ /**
+ * Writes the node's configuration to the <code>torrc</code> file in the
+ * node's working directory and changes the state to
+ * <code>NodeState.CONFIGURATION_WRITTEN</code>.
+ *
+ * @throws IllegalStateException
+ * Thrown if not invoked in state
+ * <code>NodeState.CONFIGURING</code>.
+ * @throws TorProcessException
+ * Thrown if the configuration file cannot be written to disk.
+ */
+ public abstract void writeConfiguration() throws TorProcessException;
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,55 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * A RouterNode represents a Tor process that is configured to both, relay
+ * traffic from a local application to the Tor network and to route traffic on
+ * behalf of remote applications. It inherits most of its configuration and
+ * behavior from its superclass ProxyNode and adds some router-specific
+ * configurations and behavior.
+ *
+ * @author kloesing
+ */
+public interface RouterNode extends ProxyNode {
+
+ /**
+ * <p>
+ * Determines the directory node's fingerprint. If the Tor process has not
+ * been started before, it is started to determine the node's fingerprint.
+ * This is done using a temporary configuration file and with the
+ * command-line option <code>--list-fingerprint</code>. Tor then
+ * generates a new onion key and writes its fingerprint to the
+ * <code>fingerprint</code> file in its working directory, but does not
+ * start routing traffic.
+ * </p>
+ *
+ * <p>
+ * The temporary <code>torrc</code> file contains a fake entry as
+ * DirServer, so that the node thinks that it is in a private network. The
+ * reason for this is that some configuration entries might only work in a
+ * private network, but the node cannot be configured with valid DirServer
+ * entries that would make it believe to be in a private network, because
+ * they can only be generated after invoking this method. Therefore, a
+ * single DirServer entry is configured with this node as directory server
+ * having a fingerprint of
+ * <code>0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
+ * </p>
+ *
+ * <p>
+ * The fingerprint is returned and locally stored so that further
+ * invocations of this method can be answered immediately. This method can
+ * only be invoked in state <code>NodeState.CONFIGURING</code> and does
+ * not change this state. It should be invoked after most of the other
+ * configurations for this node have been completed, but can be invoked
+ * before specifying directory servers for this node.
+ * </p>
+ *
+ * @return The content of the node's fingerprint file.
+ * @throws TorProcessException
+ * Thrown if either the temporary <code>torrc</code>
+ * configuration file cannot be written, the Tor process cannot
+ * be started temporarily, or the fingerprint file cannot be
+ * read.
+ */
+ public abstract String determineFingerprint() throws TorProcessException;
+
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,21 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>ServerApplication</code> can be used as simple HTTP server that
+ * answers all <code>HTTP GET</code> requests by empty <code>HTTP OK</code>
+ * replies. Therefore, a thread will be started to listen for incoming requests
+ * in the background.
+ *
+ * @author kloesing
+ */
+public interface ServerApplication extends EventSource {
+
+ /**
+ * Starts listening for incoming <code>HTTP GET</code> requests by
+ * clients. Any incoming request is answered by an empty
+ * <code>HTTP OK</code> reply.
+ */
+ public abstract void listen();
+
+ // TODO we need some way to interrupt listening for requests
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,59 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>TorProcessException</code> comprises all kinds of checked
+ * exceptions that occur when interacting with the JVM-external Tor processes or
+ * with the local file system. Any occurence of this exception denotes either a
+ * configuration problem that can only be solved outside of the JVM, or an
+ * unexpected problem. In contrast to this, all kinds of programming errors of a
+ * test application (invoking a method with wrong parameter values, in wrong
+ * state, etc.) will instead cause runtime exceptions from the Java API.
+ *
+ * @author kloesing
+ */
+@SuppressWarnings("serial")
+public class TorProcessException extends Exception {
+
+ /**
+ * Creates a TorProcessException without a detail message or cause.
+ */
+ public TorProcessException() {
+ super();
+ }
+
+ /**
+ * Creates a TorProcessException with the given detail <code>message</code>
+ * and <code>cause</code>.
+ *
+ * @param message
+ * The detail message of this exception.
+ * @param cause
+ * The cause for this exception.
+ */
+ public TorProcessException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Creates a TorProcessException with the given detail <code>message</code>,
+ * but without a cause.
+ *
+ * @param message
+ * The detail message of this exception.
+ */
+ public TorProcessException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a TorProcessException with the given <code>cause</code>, but
+ * without a detail message.
+ *
+ * @param cause
+ * The cause for this exception.
+ */
+ public TorProcessException(Throwable cause) {
+ super(cause);
+ }
+
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,100 @@
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+/**
+ * Example for accessing a public Web server (www.google.com) over Tor to
+ * measure access times.
+ *
+ * @author kloesing
+ */
+public class AccessingPublicWebServerOverTor {
+
+ /**
+ * Sets up and runs the test.
+ *
+ * @param args
+ * Command-line arguments are ignored.
+ *
+ * @throws TorProcessException
+ * Thrown if there is a problem with the JVM-external Tor
+ * processes that we cannot handle.
+ */
+ public static void main(String[] args) throws TorProcessException {
+
+ // though we only need one proxy, we always need to create a network
+ // to initialize a test case
+ Network network = NetworkFactory.createNetwork("example1");
+
+ // create a single proxy node with name "proxy", control port 7001,
+ // and SOCKS port 7002
+ network.createProxy("proxy", 7001, 7002);
+
+ // write configuration of proxy node
+ network.writeConfigurations();
+
+ // start proxy node and wait until it has opened a circuit with a
+ // timeout of 5 seconds
+ if (!network.startNodes(5000)) {
+
+ // failed to start the proxy
+ return;
+ }
+
+ // hup until proxy has built circuits (5 retries, 10 seconds timeout
+ // each)
+ if (!network.hupUntilUp(5, 10000)) {
+
+ // failed to build circuits
+ return;
+ }
+
+ // create client application
+ ClientApplication client = network.createClient("client",
+ "www.google.de", 80, 7002);
+
+ // create event listener to listen for client application events
+ EventListener clientEventListener = new EventListener() {
+
+ // remember time when request was sent
+ private long before;
+
+ public void handleEvent(Event event) {
+ switch (event) {
+ case APPLICATION_SENDING_REQUEST:
+ before = System.currentTimeMillis();
+ break;
+ case APPLICATION_REPLY_RECEIVED:
+ System.out
+ .println("Request took "
+ + (System.currentTimeMillis() - before)
+ + " millis");
+ }
+ }
+ };
+
+ // obtain reference to event manager to be able to respond to events
+ EventManager manager = network.getEventManager();
+
+ // register event handler for client application events
+ manager.addEventListener(client, clientEventListener);
+
+ // perform at most three request with a timeout of 20 seconds each
+ client.performRequest(3, 20000, true);
+
+ // block this thread as long as client requests are running
+ manager.waitForAnyOccurence(client,
+ Event.APPLICATION_REQUESTS_PERFORMED);
+
+ // shut down proxy
+ network.shutdownNodes();
+
+ System.out.println("Exiting...");
+ }
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,154 @@
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.RouterNode;
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+/**
+ * Example for advertising and accessing a hidden service over a private Tor
+ * network.
+ *
+ * @author kloesing
+ */
+public class AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork {
+
+ /**
+ * Sets up and runs the test.
+ *
+ * @param args
+ * Command-line arguments are ignored.
+ *
+ * @throws TorProcessException
+ * Thrown if there is a problem with the JVM-external Tor
+ * processes that we cannot handle.
+ */
+ public static void main(String[] args) throws TorProcessException {
+
+ // create a network to initialize a test case
+ Network network = NetworkFactory.createNetwork("example4");
+
+ // create two proxy nodes with names "proxy1"/"proxy2", control ports
+ // 7001/7011, and SOCKS ports 7002/7012
+ // TODO for some reason proxies do not work in a private-network
+ // setting...
+ // ProxyNode proxy1 = network.createProxy("proxy1", 7001, 7002);
+ // network.createProxy("proxy2", 7011, 7012);
+
+ // create three router nodes with parameters (router name, control port,
+ // SOCKS port, OR port, dir mirror port)
+ RouterNode router1 = network.createRouter("router1", 7021, 7022, 7023,
+ 7024);
+ network.createRouter("router2", 7031, 7032, 7033, 7034);
+ network.createRouter("router3", 7041, 7042, 7043, 7044);
+
+ // create two directory nodes with parameters (router name, control
+ // port, SOCKS port, OR port, dir port)
+ network.createDirectory("dir1", 7051, 7052, 7053, 7054);
+ network.createDirectory("dir2", 7061, 7062, 7063, 7064);
+
+ // add hidden service to the configuration of proxy1
+ router1.addHiddenService("hidServ", 7025, 80);
+
+ // configure nodes of this network to be part of a private network
+ network.configureAsPrivateNetwork();
+
+ // write configuration of proxy node
+ network.writeConfigurations();
+
+ // start proxy node and wait until it has opened a circuit with a
+ // timeout of 5 seconds
+ if (!network.startNodes(5000)) {
+
+ // failed to start the proxy
+ System.out.println("Failed to start nodes!");
+ return;
+ }
+
+ // hup until proxy has built circuits (8 retries, 10 seconds timeout
+ // each)
+ if (!network.hupUntilUp(8, 10000)) {
+
+ // failed to build circuits
+ System.out.println("Failed to build circuits!");
+ return;
+ }
+
+ // obtain reference to event manager to be able to respond to events
+ EventManager manager = network.getEventManager();
+
+ // wait for 3 minutes that the proxy has published its first RSD
+ if (!manager.waitForAnyOccurence(router1, Event.NODE_RSD_PUBLISHED,
+ 3L * 60L * 1000L)) {
+
+ // failed to publish an RSD
+ System.out.println("Failed to publish an RSD!");
+ return;
+ }
+
+ // determine onion address for hidden service
+ String onionAddress = router1.getOnionAddress("hidServ");
+
+ // create server application
+ ServerApplication server = network.createServer("server", 7025);
+
+ // create client application
+ ClientApplication client = network.createClient("client", onionAddress,
+ 80, 7042);
+
+ // create event listener to listen for client and server application
+ // events
+ EventListener clientAndServerEventListener = new EventListener() {
+
+ private long requestReceivedAtServer;
+
+ // remember time when request was sent and when it was received
+ private long requestSentFromClient;
+
+ public void handleEvent(Event event) {
+ switch (event) {
+ case APPLICATION_SENDING_REQUEST:
+ requestSentFromClient = System.currentTimeMillis();
+ break;
+ case APPLICATION_REQUEST_RECEIVED:
+ requestReceivedAtServer = System.currentTimeMillis();
+ System.out.println("Request took "
+ + (requestReceivedAtServer - requestSentFromClient)
+ + " millis from client to server!");
+ break;
+ case APPLICATION_REPLY_RECEIVED:
+ System.out
+ .println("Request took "
+ + (System.currentTimeMillis() - requestSentFromClient)
+ + " millis for the rount-trip and "
+ + (System.currentTimeMillis() - requestReceivedAtServer)
+ + " millis from server to client!");
+ }
+ }
+ };
+
+ // register event handler for client and server application events
+ manager.addEventListener(client, clientAndServerEventListener);
+ manager.addEventListener(server, clientAndServerEventListener);
+
+ // start server
+ server.listen();
+
+ // perform at most five request with a timeout of 45 seconds each
+ client.performRequest(5, 45000, true);
+
+ // block this thread as long as client requests are running
+ manager.waitForAnyOccurence(client,
+ Event.APPLICATION_REQUESTS_PERFORMED);
+
+ // shut down proxy
+ network.shutdownNodes();
+
+ System.out.println("Exiting...");
+ }
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,137 @@
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+/**
+ * Example for advertising and accessing a hidden service over the public Tor
+ * network.
+ *
+ * @author kloesing
+ */
+public class AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork {
+
+ /**
+ * Sets up and runs the test.
+ *
+ * @param args
+ * Command-line arguments are ignored.
+ *
+ * @throws TorProcessException
+ * Thrown if there is a problem with the JVM-external Tor
+ * processes that we cannot handle.
+ */
+ public static void main(String[] args) throws TorProcessException {
+
+ // create a network to initialize a test case
+ Network network = NetworkFactory.createNetwork("example3");
+
+ // create two proxy nodes with names "proxy1"/"proxy2", control ports
+ // 7001/7011, and SOCKS ports 7002/7012
+ ProxyNode proxy1 = network.createProxy("proxy1", 7001, 7002);
+ network.createProxy("proxy2", 7011, 7012);
+
+ // add hidden service to the configuration of proxy1
+ proxy1.addHiddenService("hidServ", 7005, 80);
+
+ // write configuration of proxy node
+ network.writeConfigurations();
+
+ // start proxy node and wait until it has opened a circuit with a
+ // timeout of 5 seconds
+ if (!network.startNodes(5000)) {
+
+ // failed to start the proxy
+ System.out.println("Failed to start the proxy!");
+ return;
+ }
+
+ // hup until proxy has built circuits (5 retries, 10 seconds timeout
+ // each)
+ if (!network.hupUntilUp(5, 10000)) {
+
+ // failed to build circuits
+ System.out.println("Failed to build circuits!");
+ return;
+ }
+
+ // obtain reference to event manager to be able to respond to events
+ EventManager manager = network.getEventManager();
+
+ // wait for 3 minutes that the proxy has published its first RSD
+ if (!manager.waitForAnyOccurence(proxy1, Event.NODE_RSD_PUBLISHED,
+ 3L * 60L * 1000L)) {
+
+ // failed to publish an RSD
+ System.out.println("Failed to publish an RSD!");
+ return;
+ }
+
+ // create server application
+ ServerApplication server = network.createServer("server", 7005);
+
+ // determine onion address for hidden service
+ String onionAddress = proxy1.getOnionAddress("hidServ");
+
+ // create client application
+ ClientApplication client = network.createClient("client", onionAddress,
+ 80, 7012);
+
+ // create event listener to listen for client and server application
+ // events
+ EventListener clientAndServerEventListener = new EventListener() {
+
+ private long requestReceivedAtServer;
+
+ // remember time when request was sent and when it was received
+ private long requestSentFromClient;
+
+ public void handleEvent(Event event) {
+ switch (event) {
+ case APPLICATION_SENDING_REQUEST:
+ requestSentFromClient = System.currentTimeMillis();
+ break;
+ case APPLICATION_REQUEST_RECEIVED:
+ requestReceivedAtServer = System.currentTimeMillis();
+ System.out.println("Request took "
+ + (requestReceivedAtServer - requestSentFromClient)
+ + " millis from client to server!");
+ break;
+ case APPLICATION_REPLY_RECEIVED:
+ System.out
+ .println("Request took "
+ + (System.currentTimeMillis() - requestSentFromClient)
+ + " millis for the rount-trip and "
+ + (System.currentTimeMillis() - requestReceivedAtServer)
+ + " millis from server to client!");
+ }
+ }
+ };
+
+ // register event handler for client and server application events
+ manager.addEventListener(client, clientAndServerEventListener);
+ manager.addEventListener(server, clientAndServerEventListener);
+
+ // start server
+ server.listen();
+
+ // perform at most five request with a timeout of 45 seconds each
+ client.performRequest(5, 45000, true);
+
+ // block this thread as long as client requests are running
+ manager.waitForAnyOccurence(client,
+ Event.APPLICATION_REQUESTS_PERFORMED);
+
+ // shut down proxy
+ network.shutdownNodes();
+
+ System.out.println("Exiting...");
+ }
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,105 @@
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+/**
+ * Example for advertising a hidden service to the public Tor network and
+ * observe publication of rendezvous service descriptors.
+ *
+ * @author kloesing
+ */
+public class AdvertisingHiddenServiceToPublicTorNetwork {
+
+ /**
+ * Sets up and runs the test.
+ *
+ * @param args
+ * Command-line arguments are ignored.
+ *
+ * @throws TorProcessException
+ * Thrown if there is a problem with the JVM-external Tor
+ * processes that we cannot handle.
+ */
+ public static void main(String[] args) throws TorProcessException {
+
+ // though we only need one proxy, we always need to create a network
+ // to initialize a test case
+ Network network = NetworkFactory.createNetwork("example2");
+
+ // create a single proxy node with name "proxy", control port 7001,
+ // and SOCKS port 7002
+ ProxyNode proxy = network.createProxy("proxy", 7001, 7002);
+
+ // add hidden service to the configuration
+ proxy.addHiddenService("hidServ", 7005, 80);
+
+ // write configuration of proxy node
+ network.writeConfigurations();
+
+ // create event listener to listen for events from our proxy
+ EventListener proxyEventListener = new EventListener() {
+
+ // remember time when request was sent
+ private long circuitOpened = -1;
+
+ public void handleEvent(Event event) {
+ switch (event) {
+ case NODE_CIRCUIT_OPENED:
+ if (circuitOpened == -1) {
+ circuitOpened = System.currentTimeMillis();
+ }
+ break;
+ case NODE_RSD_PUBLISHED:
+ System.out.println("RSD published "
+ + (System.currentTimeMillis() - circuitOpened)
+ + " millis after first circuit was opened");
+ }
+ }
+ };
+
+ // obtain reference to event manager to be able to respond to events
+ EventManager manager = network.getEventManager();
+
+ // register event handler for proxy events
+ manager.addEventListener(proxy, proxyEventListener);
+
+ // start proxy node and wait until it has opened a circuit with a
+ // timeout of 5 seconds
+ if (!network.startNodes(5000)) {
+
+ // failed to start the proxy
+ System.out.println("Failed to start the proxy!");
+ return;
+ }
+
+ // hup until proxy has built circuits (5 retries, 10 seconds timeout
+ // each)
+ if (!network.hupUntilUp(5, 10000)) {
+
+ // failed to build circuits
+ System.out.println("Failed to build circuits!");
+ return;
+ }
+
+ // let it run for 5 minutes and observe when RSDs are published...
+ System.out
+ .println("Waiting for 5 minutes and observing RSD publications...");
+
+ try {
+ Thread.sleep(5L * 60L * 1000L);
+ } catch (InterruptedException e) {
+ // do nothing
+ }
+
+ // shut down proxy
+ network.shutdownNodes();
+
+ System.out.println("Exiting...");
+ }
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,390 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.net.Proxy.Type;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.Event;
+
+/**
+ * Implementation of ClientApplication.
+ *
+ * @author kloesing
+ */
+public class ClientApplicationImpl implements ClientApplication {
+
+ /**
+ * Thread that performs the requests in the background
+ */
+ private RequestThread clientThread;
+
+ /**
+ * Logger for this client
+ */
+ private Logger logger;
+
+ /**
+ * Target address for requests; can be either a server address or an onion
+ * address.
+ */
+ private String targetAddress;
+
+ /**
+ * Target port for requests; can be either a server port or a virtual port
+ * of a hidden service.
+ */
+ private int targetPort;
+
+ /**
+ * SOCKS port of the local Tor node.
+ */
+ private int socksPort;
+
+ /**
+ *
+ */
+ private EventManagerImpl eventManager;
+
+ /**
+ *
+ */
+ private String clientApplicationName;
+
+ /**
+ * (logging finished)
+ *
+ * erzeugt neue client app innerhalb der jvm, started aber noch keine
+ * requests.
+ *
+ * @param network
+ *
+ * @param clientApplicationName
+ * fÃr logging
+ * @param targetAddress
+ * Target address for requests; can be either a server address or
+ * an onion address.
+ * @param targetPort
+ * Target port for requests; can be either a server port or a
+ * virtual port of a hidden service.
+ * @param socksPort
+ * SOCKS port of the local Tor node.
+ */
+ ClientApplicationImpl(NetworkImpl network, String clientApplicationName,
+ String targetAddress, int targetPort, int socksPort) {
+
+ // TODO make sure that name is loggable!!
+
+ // create logger
+ this.logger = Logger.getLogger("application." + clientApplicationName);
+
+ // log entering
+ this.logger.entering(this.getClass().getName(),
+ "ClientApplicationImpl", new Object[] { clientApplicationName,
+ targetAddress, targetPort, socksPort });
+
+ // check parameters
+ if (clientApplicationName == null
+ || clientApplicationName.length() == 0 || targetAddress == null
+ || targetAddress.length() == 0 || targetPort < 0
+ || targetPort > 65535 || socksPort < 0 || socksPort > 65535) {
+
+ IllegalArgumentException e = new IllegalArgumentException("bla");
+ this.logger.throwing(this.getClass().getName(),
+ "ClientApplicationImpl", e);
+ throw e;
+ }
+
+ // remember parameters
+ this.clientApplicationName = clientApplicationName;
+ this.targetAddress = targetAddress;
+ this.targetPort = targetPort;
+ this.socksPort = socksPort;
+
+ // obtain reference on event manager
+ this.eventManager = network.getEventManagerImpl();
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "ClientApplicationImpl");
+ }
+
+ /**
+ * bricht alle laufenden requests ab
+ *
+ */
+ public void stopRequest() {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "stopRequest");
+
+ // check if a request is running
+ if (this.clientThread == null) {
+ throw new IllegalStateException("No request has been started!");
+ }
+
+ // log this event
+ this.logger.log(Level.FINE, "Shutting down client");
+
+ // interrupt thread
+ this.clientThread.stopRequest();
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "stopRequest");
+ }
+
+ /**
+ * (logging done)
+ *
+ * startet einen oder mehrere aufeinander folgende aufrufe an die im
+ * konstruktor Ãbergebene adresse und port.
+ */
+ public void performRequest(int retries, long timeoutForEachRetry,
+ boolean stopOnSuccess) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "performRequest",
+ new Object[] { retries, timeoutForEachRetry, stopOnSuccess });
+
+ // check parameters
+ if (retries <= 0 || timeoutForEachRetry < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ // check if we already have started a request (TODO change this to allow
+ // multiple requests in parallel? would be possible)
+ if (this.clientThread != null) {
+ throw new IllegalStateException(
+ "Another request has already been started!");
+ }
+
+ // create a thread that performs requests in the background
+ this.clientThread = new RequestThread(retries, timeoutForEachRetry,
+ stopOnSuccess);
+ this.clientThread.setName("Request Thread");
+ this.clientThread.setDaemon(true);
+ this.clientThread.start();
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "performRequest");
+ }
+
+ /**
+ * (logging done)
+ *
+ * @author kloesing
+ *
+ */
+ private class RequestThread extends Thread {
+
+ private int retries;
+
+ private long timeoutForEachRetry;
+
+ private boolean connected;
+
+ private boolean stopOnSuccess;
+
+ RequestThread(int retries, long timeoutForEachRetry,
+ boolean stopOnSuccess) {
+
+ // log entering
+ logger
+ .entering(this.getClass().getName(), "RequestThread",
+ new Object[] { retries, timeoutForEachRetry,
+ stopOnSuccess });
+
+ // remember parameters
+ this.retries = retries;
+ this.timeoutForEachRetry = timeoutForEachRetry;
+ this.stopOnSuccess = stopOnSuccess;
+
+ // start connected
+ this.connected = true;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "RequestThread");
+ }
+
+ /**
+ * stoppt diesen request egal wo er gerade steht
+ *
+ */
+ public void stopRequest() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "stopRequest");
+
+ // change connected state to false and interrupt thread
+ this.connected = false;
+ this.interrupt();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "stopRequest");
+ }
+
+ /**
+ * (logging done)
+ */
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ try {
+
+ // set Tor as proxy
+ InetSocketAddress isa = new InetSocketAddress("127.0.0.1",
+ socksPort);
+ Proxy p = new Proxy(Type.SOCKS, isa);
+
+ // create target address for socket
+ InetSocketAddress hs = new InetSocketAddress(targetAddress,
+ targetPort);
+
+ // start loop
+ for (int i = 0; connected && i < retries; i++) {
+
+ // log this try
+ logger.log(Level.FINE, "Trying to perform request");
+
+ // remember when we started
+ long timeBeforeConnectionAttempt = System
+ .currentTimeMillis();
+
+ // send event to event manager
+ eventManager.observeEvent(ClientApplicationImpl.this,
+ Event.APPLICATION_SENDING_REQUEST);
+
+ Socket s = null;
+ try {
+
+ // create new socket using Tor as proxy
+ s = new Socket(p);
+
+ // try to connect to remote server
+ s.connect(hs, (int) timeoutForEachRetry);
+
+ // open output stream to write request
+ PrintStream out = new PrintStream(s.getOutputStream());
+ out.print("GET / HTTP/1.0\r\n\r\n");
+
+ // open input stream to read reply
+ BufferedReader in = new BufferedReader(
+ new InputStreamReader(s.getInputStream()));
+
+ // only read the first char in the response; this method
+ // blocks until there is a response
+ in.read();
+
+ // send event to event manager
+ eventManager.observeEvent(ClientApplicationImpl.this,
+ Event.APPLICATION_REPLY_RECEIVED);
+
+ // if we should stop on success, stop further connection
+ // attempts
+ if (this.stopOnSuccess) {
+ this.connected = false;
+ }
+
+ // clean up socket
+ in.close();
+ out.close();
+ s.close();
+
+ } catch (SocketTimeoutException e) {
+
+ // log warning
+ logger.log(Level.WARNING,
+ "Connection to remote server timed out!", e);
+
+ // send event to event manager
+ eventManager.observeEvent(ClientApplicationImpl.this,
+ Event.APPLICATION_GAVE_UP_REQUEST);
+
+ // try again immediately, if there are retries left
+
+ } catch (IOException e) {
+
+ // log warning
+ logger.log(Level.WARNING,
+ "Connection to remote server could not be "
+ + "established!", e);
+
+ // send event to event manager
+ eventManager.observeEvent(ClientApplicationImpl.this,
+ Event.APPLICATION_GAVE_UP_REQUEST);
+
+ // wait for the rest of the timeout
+ long timeOfTimeoutLeft = timeBeforeConnectionAttempt
+ + timeoutForEachRetry
+ - System.currentTimeMillis();
+ if (timeOfTimeoutLeft > 0) {
+ try {
+ Thread.sleep(timeOfTimeoutLeft);
+ } catch (InterruptedException ex) {
+ // do nothing
+ }
+ }
+
+ } finally {
+
+ // close connection
+ try {
+
+ // try to close socket
+ logger.log(Level.FINER, "Trying to close socket.");
+ s.close();
+ logger.log(Level.FINE, "Socket closed.");
+
+ } catch (Exception e1) {
+
+ // log warning
+ logger.log(Level.WARNING,
+ "Exception when trying to close socket!",
+ e1);
+ }
+ }
+ }
+
+ } catch (Exception e) {
+
+ // log that we have been interrupted
+ logger.log(Level.WARNING, "Client has been interrupted!", e);
+
+ } finally {
+
+ // we are done here
+ logger.log(Level.FINE, "Requests performed!");
+
+ // send event to event manager
+ eventManager.observeEvent(ClientApplicationImpl.this,
+ Event.APPLICATION_REQUESTS_PERFORMED);
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ": clientApplicationName=\""
+ + this.clientApplicationName + "\", targetAddress=\""
+ + this.targetAddress + "\", targetPort=" + this.targetPort
+ + ", socksPort=" + this.socksPort;
+ }
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,84 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import de.uniba.wiai.lspi.puppetor.DirectoryNode;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+public class DirectoryNodeImpl extends RouterNodeImpl implements DirectoryNode {
+ /**
+ * Initializes this Tor node, but does not start it, yet.
+ */
+ public DirectoryNodeImpl(NetworkImpl network, String nodeName,
+ int controlPort, int socksPort, int orPort, int dirPort) {
+ super(network, nodeName, controlPort, socksPort, orPort, dirPort);
+
+ // configure this node as an authoritative directory
+ this.configuration.add("AuthoritativeDirectory 1");
+
+ // TODO make this a little bit more configurable---same as to
+ // location of tor.exe?
+ this.configuration
+ .add("RecommendedVersions 0.1.2.12-rc,0.1.2.7-alpha-dev");
+
+ this.configuration.add("VersioningAuthoritativeDirectory 1");
+
+ this.configuration.add("DirAllowPrivateAddresses 1");
+
+ this.configuration.add("NamingAuthoritativeDirectory 1");
+
+ this.configuration.add("V1AuthoritativeDirectory 1");
+
+ // TODO this only works since Tor 0.1.2.x!!!
+ this.configuration.add("HSAuthoritativeDir 1");
+
+ }
+
+ public void writeApprovedRouters(Set<String> approvedRouters)
+ throws TorProcessException {
+
+ // check param!
+
+ try {
+ File approvedRoutersFile = new File(this.workingDir
+ .getAbsolutePath()
+ + File.separator + "approved-routers");
+ BufferedWriter bw = new BufferedWriter(new FileWriter(
+ approvedRoutersFile));
+ SortedSet<String> sortedApprovedRouters = new TreeSet<String>(
+ approvedRouters);
+ for (String approvedRouter : sortedApprovedRouters) {
+ bw.write(approvedRouter + "\n");
+ }
+ bw.close();
+ } catch (IOException e) {
+ throw new TorProcessException(e);
+ }
+ }
+
+ public synchronized String determineDirServerString()
+ throws TorProcessException {
+
+ // determine fingerprint
+ String fingerprint = determineFingerprint();
+
+ // cut off router nickname
+ fingerprint = fingerprint.substring(fingerprint.indexOf(" ") + 1);
+
+ // put together everything
+ String dirServerString = "DirServer " + this.nodeName
+ + " hs 127.0.0.1:" + this.dirPort + " " + fingerprint;
+ return dirServerString;
+ }
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,348 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.EventSource;
+
+public class EventManagerImpl implements EventManager {
+
+ private Logger logger;
+
+ EventManagerImpl(String networkName) {
+
+// TODO make sure that name is loggable!!
+
+ this.logger = Logger.getLogger("event." + networkName);
+ this.observedEvents = new HashMap<EventSource, List<Event>>();
+ this.eventHandlers = new HashMap<EventSource, Set<EventListener>>();
+ }
+
+ private Map<EventSource, List<Event>> observedEvents;
+
+ private Map<EventSource, Set<EventListener>> eventHandlers;
+
+ public synchronized List<Event> addEventListener(EventSource source,
+ EventListener listener) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "addEventListener",
+ new Object[] { source, listener });
+
+ // check parameters
+ if (source == null || listener == null) {
+ throw new IllegalArgumentException();
+ }
+
+ // if necessary, create new event listener set for source
+ if (!this.eventHandlers.containsKey(source)) {
+ this.eventHandlers.put(source, new HashSet<EventListener>());
+ }
+
+ // add listener
+ this.eventHandlers.get(source).add(listener);
+
+ // log change
+ this.logger
+ .log(Level.FINE, "Added event listener for source " + source);
+
+ // log exiting and return
+ List<Event> result = getEventHistory(source);
+ this.logger.exiting(this.getClass().getName(), "addEventListener",
+ result);
+ return result;
+ }
+
+ public synchronized List<Event> getEventHistory(EventSource source) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "getNodeEventHistory",
+ source);
+
+ // check parameter
+ if (source == null) {
+ throw new IllegalArgumentException();
+ }
+
+ // prepare result
+ List<Event> result = new ArrayList<Event>();
+
+ // did we already observe events for this source?
+ if (this.observedEvents.containsKey(source)) {
+ // yes, add all events to result list
+ result.addAll(this.observedEvents.get(source));
+ }
+
+ // log exiting and return result
+ this.logger.exiting(this.getClass().getName(), "getNodeEventHistory",
+ result);
+ return result;
+
+ }
+
+ public synchronized boolean hasEventOccured(EventSource source, Event event) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "hasEventOccured",
+ new Object[] { source, event });
+
+ // check parameters
+ if (source == null || event == null) {
+ throw new IllegalArgumentException();
+ }
+
+ // determine result
+ boolean result = this.observedEvents.containsKey(source)
+ && this.observedEvents.get(source).contains(event);
+
+ // log exiting and return result
+ this.logger.exiting(this.getClass().getName(), "hasEventOccured",
+ result);
+ return result;
+ }
+
+ public synchronized void removeEventListener(EventListener eventListener) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "removeEventListener",
+ eventListener);
+
+ // check parameters
+ if (eventListener == null) {
+ throw new IllegalArgumentException();
+ }
+
+ // don't know to which source this listener has been added (may to more
+ // than one), so remove it from all possible sets
+ for (Set<EventListener> set : eventHandlers.values()) {
+ if (set.remove(eventListener)) {
+ logger.log(Level.FINE, "Removed event listener!");
+ }
+ }
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "removeEventListener");
+ }
+
+ public synchronized void waitForAnyOccurence(EventSource source, Event event) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "waitForAnyOccurence",
+ new Object[] { source, event });
+
+ // check parameters
+ if (source == null || event == null) {
+ throw new IllegalArgumentException();
+ }
+
+ // invoke overloaded method with maximumTimeToWaitInMillis of -1L which
+ // means to wait forever
+ waitForAnyOccurence(source, event, -1L);
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "waitForAnyOccurence");
+
+ }
+
+ public synchronized boolean waitForAnyOccurence(EventSource source,
+ Event event, long maximumTimeToWaitInMillis) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "waitForAnyOccurence",
+ new Object[] { source, event, maximumTimeToWaitInMillis });
+
+ // check parameters
+ if (source == null || event == null) {
+ throw new IllegalArgumentException();
+ }
+
+ // check if we have already observed the event
+ if (this.hasEventOccured(source, event)) {
+
+ this.logger.log(Level.FINE, "Waiting for any occurence of event "
+ + event + " returned immediately!");
+
+ this.logger.exiting(this.getClass().getName(),
+ "waitForAnyOccurence", true);
+ return true;
+ }
+
+ // invoke method that waits for next occurence of the event
+ boolean result = waitForNextOccurence(source, event,
+ maximumTimeToWaitInMillis);
+
+ // log exiting and return result
+ this.logger.exiting(this.getClass().getName(), "waitForAnyOccurence",
+ result);
+ return result;
+
+ }
+
+ public synchronized void waitForNextOccurence(EventSource source,
+ Event event) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "waitForNextOccurence",
+ new Object[] { source, event });
+
+ // check parameters
+ if (source == null || event == null) {
+ throw new IllegalArgumentException();
+ }
+
+ // invoke overloaded method with maximumTimeToWaitInMillis of -1L which
+ // means to wait forever
+ waitForNextOccurence(source, event, -1L);
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "waitForNextOccurence");
+
+ }
+
+ public synchronized boolean waitForNextOccurence(EventSource source,
+ Event event, long maximumTimeToWaitInMillis) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "waitForNextOccurence",
+ new Object[] { source, event, maximumTimeToWaitInMillis });
+
+ // check parameters
+ if (source == null || event == null) {
+ throw new IllegalArgumentException();
+ }
+
+ // distinguish between negative waiting time (wait forever) and zero or
+ // positive waiting time
+ if (maximumTimeToWaitInMillis < 0) {
+
+ // wait forever
+ while (!this.hasEventOccured(source, event)) {
+
+ this.logger.log(Level.FINEST,
+ "We will wait infinetely for the next occurence of "
+ + "event " + event + " from source " + source
+ + "...");
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ // don't handle
+ }
+
+ this.logger.log(Level.FINEST,
+ "We have been notified about an observed event while "
+ + "waiting for events of type " + event
+ + " from source " + source
+ + "; need to check whether the observed event "
+ + "is what we are looking for...");
+ }
+
+ this.logger.log(Level.FINE, "Waiting for occurence of event "
+ + event + " succeeded!");
+
+ // log exiting and return result
+ this.logger.exiting(this.getClass().getName(),
+ "waitForNextOccurence", true);
+ return true;
+
+ } else {
+
+ // wait for the given time at most
+ long endOfTime = System.currentTimeMillis()
+ + maximumTimeToWaitInMillis;
+ long timeLeft = 0;
+ while (!this.hasEventOccured(source, event)
+ && (timeLeft = endOfTime - System.currentTimeMillis()) > 0) {
+
+ this.logger.log(Level.FINEST, "We will wait for " + timeLeft
+ + " millis for the next occurence of event " + event
+ + " from source " + source + "...");
+
+ try {
+ wait(timeLeft);
+ } catch (InterruptedException e) {
+ // don't handle
+ }
+
+ this.logger.log(Level.FINEST,
+ "We have been notified about an observed event while "
+ + "waiting for events of type " + event
+ + " from source " + source
+ + "; need to check whether the observed event "
+ + "is what we are looking for...");
+ }
+
+ // determine result
+ boolean result = this.hasEventOccured(source, event);
+
+ this.logger.log(Level.FINE, "Waiting for next occurence of event "
+ + event + " from source " + source
+ + (result ? " succeeded!" : " did not succeed!"));
+
+ // log exiting and return result
+ this.logger.exiting(this.getClass().getName(),
+ "waitForNextOccurence", result);
+ return result;
+ }
+ }
+
+ /**
+ * wird lokal aufgerufen; speichert event in history und meldet es an alle
+ * registrierten event handler weiter
+ *
+ */
+ synchronized void observeEvent(EventSource source, Event event) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "observeEvent",
+ new Object[] { source, event });
+
+ this.logger.log(Level.FINE, "Observed event " + event + " from source "
+ + source + "!");
+
+ // remember observed event
+ if (!this.observedEvents.containsKey(source)) {
+ this.observedEvents.put(source, new ArrayList<Event>());
+ }
+ this.observedEvents.get(source).add(event);
+
+ // notify waiting threads
+ notifyAll();
+
+ // inform event listeners
+ if (this.eventHandlers.containsKey(source)) {
+
+ // make a copy of the event handler set, because some event handlers
+ // might want to remove itself from this set while handling the
+ // event
+
+ Set<EventListener> copyOfEventHandlers = new HashSet<EventListener>(
+ this.eventHandlers.get(source));
+
+ for (EventListener eventHandler : copyOfEventHandlers) {
+
+ this.logger.log(Level.FINE, "Informing event listener "
+ + eventHandler + " about recently observed event "
+ + event + " from source " + source + "!");
+ eventHandler.handleEvent(event);
+ }
+ }
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "observeEvent");
+ }
+
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,461 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.DirectoryNode;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkState;
+import de.uniba.wiai.lspi.puppetor.NodeState;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.RouterNode;
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+public class NetworkImpl implements Network {
+
+ private EventManagerImpl eventManager;
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ": networkName=\""
+ + this.networkName + "\", networkState="
+ + this.networkState.toString() + ", nodes.size()="
+ + this.nodes.size();
+ }
+
+ private Logger logger;
+
+ /**
+ * Contains the name of this node configuration which is the String
+ * conversion of System.currentTimeMillis().
+ */
+ protected String networkName;
+
+ /**
+ * Creates an initially unpopulated Tor network and creates a new working
+ * directory for it at test-env/randomTestID/.
+ *
+ * @param networkName
+ * Name of this network configuration.
+ * @param logToConsole
+ * Whether logging statements shall be written to the console or
+ * to a file.
+ */
+ public NetworkImpl(String networkName) {
+
+// TODO make sure that name is loggable!!
+
+ this.logger = Logger.getLogger("network." + networkName);
+ this.logger.setLevel(Level.ALL);
+ this.networkName = networkName;
+ this.workingDir = new File("test-env/" + System.currentTimeMillis());
+ this.workingDir.mkdirs();
+
+ // TODO if we want to log to file, set this... somehow...
+ // this.logFile = new File(this.workingDir.getAbsolutePath()
+ // + "\\events.log");
+
+ this.eventManager = new EventManagerImpl(this.networkName);
+ }
+
+ public NetworkState getNetworkState() {
+ // TODO Auto-generated method stub
+ throw new UnsupportedOperationException(
+ "Auto-generated method stub in NetworkImpl.getNetworkState");
+ }
+
+ private boolean allNodesUp() {
+
+ // check if all nodes are up
+ for (ProxyNode node : this.nodes) {
+ if (!eventManager.hasEventOccured(node, Event.NODE_CIRCUIT_OPENED)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * check, (wait, check, hup)*
+ *
+ * if all nodes get up during sleeping, we will wake up and return
+ * immediately
+ *
+ * @param retries
+ * @param hupInterval
+ * @throws Exception
+ */
+ public boolean hupUntilUp(int retries, long hupInterval)
+ throws TorProcessException {
+
+ if (allNodesUp()) {
+ return true;
+ }
+
+ // register event handlers
+ final Thread sleepingThread = Thread.currentThread();
+ for (ProxyNode node : this.nodes) {
+ eventManager.addEventListener(node, new EventListener() {
+
+ public void handleEvent(Event event) {
+ if (event == Event.NODE_CIRCUIT_OPENED) {
+ sleepingThread.interrupt();
+ eventManager.removeEventListener(this);
+ }
+ }
+ });
+ }
+
+ for (int i = 0; i < retries; i++) {
+
+ // determine how long to sleep
+ long endOfSleeping = System.currentTimeMillis() + hupInterval;
+ long now;
+
+ while ((now = System.currentTimeMillis()) < endOfSleeping) {
+
+ // sleep
+ try {
+ Thread.sleep(endOfSleeping - now);
+ } catch (InterruptedException e) {
+ }
+
+ // check
+ if (allNodesUp()) {
+ return true;
+ }
+ }
+
+ // send a HUP signal to all nodes
+ for (ProxyNode node : this.nodes) {
+ node.hup();
+ }
+
+ // continue in loop
+ }
+
+ // no retries left and not all nodes are up; return failure
+ return false;
+
+ }
+
+ public void shutdownNodes() throws TorProcessException {
+ if (this.networkState != NetworkState.NODES_STARTED) {
+ throw new IllegalStateException();
+ }
+ TorProcessException firstCaughtException = null;
+ for (ProxyNode node : this.nodes) {
+ try {
+ node.shutdown();
+ } catch (TorProcessException e) {
+ if (firstCaughtException != null) {
+ firstCaughtException = e;
+ }
+ }
+ }
+ this.networkState = NetworkState.NODES_SHUT_DOWN;
+ if (firstCaughtException != null) {
+ throw firstCaughtException;
+ }
+ }
+
+ private static class NodeStarter extends Thread {
+
+ TorProcessException caughtException;
+
+ private long maximumTimeToWaitInMillis;
+
+ private ProxyNode node;
+
+ boolean success = false;
+
+ NodeStarter(ProxyNode node, long maximumTimeToWaitInMillis) {
+ this.node = node;
+ this.maximumTimeToWaitInMillis = maximumTimeToWaitInMillis;
+ }
+
+ @Override
+ public void run() {
+ try {
+ this.success = this.node
+ .startNode(this.maximumTimeToWaitInMillis);
+ } catch (TorProcessException e) {
+ this.caughtException = e;
+ }
+ }
+ }
+
+ public boolean startNodes(long maximumTimeToWaitInMillis)
+ throws TorProcessException {
+ if (this.networkState != NetworkState.CONFIGURATIONS_WRITTEN) {
+ throw new IllegalStateException();
+ }
+
+ if (maximumTimeToWaitInMillis < 0) {
+ throw new IllegalArgumentException();
+ }
+
+ // remember time when we begin starting the nodes
+ long before = System.currentTimeMillis();
+
+ // start nodes in parallel
+ Set<NodeStarter> allNodeStarters = new HashSet<NodeStarter>();
+ for (ProxyNode node : this.nodes) {
+ NodeStarter nodeStarter = new NodeStarter(node,
+ maximumTimeToWaitInMillis);
+ allNodeStarters.add(nodeStarter);
+ nodeStarter.start();
+ }
+
+ for (NodeStarter nodeStarter : allNodeStarters) {
+ try {
+ nodeStarter.join();
+ } catch (InterruptedException e) {
+ // we have some kind of problem here!
+ return false;
+ }
+ if (nodeStarter.caughtException != null) {
+ this.networkState = NetworkState.NODES_SHUT_DOWN;
+ throw nodeStarter.caughtException;
+ }
+ if (!nodeStarter.success) {
+ this.logger.log(Level.WARNING,
+ "Starting nodes was not successful in "
+ + (maximumTimeToWaitInMillis / 1000)
+ + " seconds.", this.networkName);
+ return false;
+ }
+ }
+
+ // check how long we took to start all nodes
+ long after = System.currentTimeMillis();
+ this.logger.log(Level.FINE, "Starting nodes was successful and took "
+ + ((after - before) / 1000) + " seconds.", this.networkName);
+
+ // change state
+ this.networkState = NetworkState.NODES_STARTED;
+
+ return true;
+ }
+
+ public ClientApplication createClient(String clientApplicationName,
+ String targetAddress, int targetPort, int socksPort) {
+ return new ClientApplicationImpl(this, clientApplicationName,
+ targetAddress, targetPort, socksPort);
+ }
+
+ public ProxyNode createProxy(String nodeName, int controlPort, int socksPort) {
+
+ // TODO check parms
+
+ ProxyNodeImpl proxy = new ProxyNodeImpl(this, nodeName, controlPort,
+ socksPort);
+
+ // add to nodes
+ this.nodes.add(proxy);
+
+ return proxy;
+ }
+
+ public DirectoryNode createDirectory(String nodeName, int controlPort,
+ int socksPort, int orPort, int dirPort) {
+
+ DirectoryNode dir = new DirectoryNodeImpl(this, nodeName, controlPort,
+ socksPort, orPort, dirPort);
+
+ this.nodes.add(dir);
+
+ return dir;
+ }
+
+ public RouterNode createRouter(String nodeName, int controlPort,
+ int socksPort, int orPort, int dirPort) {
+ RouterNode router = new RouterNodeImpl(this, nodeName, controlPort,
+ socksPort, orPort, dirPort);
+
+ this.nodes.add(router);
+
+ return router;
+ }
+
+ public ServerApplication createServer(String serverApplicationName,
+ int serverPort) {
+ return new ServerApplicationImpl(this, serverApplicationName,
+ serverPort);
+ }
+
+ public EventManager getEventManager() {
+ return this.eventManager;
+ }
+
+ public EventManagerImpl getEventManagerImpl() {
+ return this.eventManager;
+ }
+
+ private NetworkState networkState = NetworkState.CONFIGURING_NODES;
+
+ /**
+ * Writes configurations for all nodes in the network, including torrc and
+ * approved-routers files. Directory nodes are configured first in order to
+ * obtain their fingerprints for all torrc files. Next are routers to obtain
+ * their fingerprints for the directories' approved-routers files. Proxies
+ * are configured at the end.
+ *
+ * This operation can only be invoked, if network status is CONFIGURABLE.
+ *
+ * @throws IllegalStateException
+ * If method is invoked in network status other than
+ * CONFIGURABLE.
+ */
+ public void writeConfigurations() throws TorProcessException {
+
+ // check state
+ if (this.networkState != NetworkState.CONFIGURING_NODES) {
+ throw new IllegalStateException();
+ }
+
+ // TODO don't we need to configure the nodes as private nodes, if we
+ // have a directory node here?!
+
+ for (ProxyNode node : this.nodes) {
+ if (node.getNodeState() == NodeState.CONFIGURING) {
+ node.writeConfiguration();
+ }
+ }
+
+ // change state
+ this.networkState = NetworkState.CONFIGURATIONS_WRITTEN;
+ }
+
+ /**
+ * Directory that contains status information of all nodes contained in this
+ * network, plus the common log file, if one is created.
+ */
+ protected File workingDir;
+
+ /**
+ * Returns the working directory of this network configuration which is in
+ * test-env/networkName/.
+ *
+ * @return Working directory of this network.
+ */
+ File getWorkingDir() {
+ return workingDir;
+ }
+
+ /**
+ * All nodes contained in this network. It is important that we store only
+ * interface types to assure that all operations could also be performed by
+ * the application itself.
+ */
+ protected Set<ProxyNode> nodes = new HashSet<ProxyNode>();
+
+ /**
+ * Internal class that is used to determine fingerprints in parallel.
+ *
+ *
+ */
+ private static class FingerprintThread extends Thread {
+
+ private TorProcessException caughtException = null;
+
+ private RouterNode node = null;
+
+ FingerprintThread(RouterNode node) {
+ this.node = node;
+ }
+
+ public void run() {
+ try {
+ node.determineFingerprint();
+ } catch (TorProcessException e) {
+ this.caughtException = e;
+ }
+ }
+ }
+
+ private HashSet<String> approvedRoutersFingerprints;
+
+ Set<String> authorizedDirectoriesFingerprints = new HashSet<String>();
+
+ public void configureAsPrivateNetwork() throws TorProcessException {
+
+ // determine fingerprints for all directories and routers (can be done
+ // in parallel)
+ Set<FingerprintThread> fingerprintThreads = new HashSet<FingerprintThread>();
+ for (ProxyNode node : nodes) {
+ if (node instanceof RouterNode) {
+ RouterNode dirOrRouterNode = (RouterNode) node;
+ FingerprintThread fingerprintThread = new FingerprintThread(
+ dirOrRouterNode);
+ fingerprintThread.setName(node.getNodeName()
+ + " Fingerprint Resolver");
+ fingerprintThreads.add(fingerprintThread);
+ fingerprintThread.start();
+ }
+ }
+ for (FingerprintThread fingerprintThread : fingerprintThreads) {
+ try {
+ fingerprintThread.join();
+ } catch (InterruptedException e) {
+ // ignore
+ logger.log(Level.WARNING, "fingerprint thread was interrupted.");
+ }
+ if (fingerprintThread.caughtException != null) {
+ throw fingerprintThread.caughtException;
+ }
+ }
+
+ // read DirServer strings for all directories from memory; they should
+ // have been read from disk before, so that this will perform really
+ // fast
+ for (ProxyNode node : this.nodes) {
+ if (node instanceof DirectoryNode) {
+ DirectoryNode dirNode = (DirectoryNode) node;
+ this.authorizedDirectoriesFingerprints.add(dirNode
+ .determineDirServerString());
+ }
+
+ }
+
+ // configure nodes
+ for (ProxyNode node : this.nodes) {
+ if (node.getNodeState() == NodeState.CONFIGURING) {
+ node
+ .configureDirServers(this.authorizedDirectoriesFingerprints);
+ }
+ }
+
+ // read fingerprints for all directories and routers from memory and
+ // write them to approved-routers file; they should have been read from
+ // disk before, so that this will perform really fast
+ this.approvedRoutersFingerprints = new HashSet<String>();
+ for (ProxyNode node : this.nodes) {
+ if (node instanceof RouterNode) {
+ RouterNode routerOrDirNode = (RouterNode) node;
+ this.approvedRoutersFingerprints.add(routerOrDirNode
+ .determineFingerprint());
+ }
+
+ }
+ for (ProxyNode node : this.nodes) {
+ if (node instanceof DirectoryNode) {
+ DirectoryNode dirNode = (DirectoryNode) node;
+ dirNode.writeApprovedRouters(this.approvedRoutersFingerprints);
+ }
+ }
+ }
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,730 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English or needs some work
+ * to refine it. This which will be the first thing to change in the next
+ * version. Sorry for any inconvenience!
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.freehaven.tor.control.EventHandler;
+import net.freehaven.tor.control.TorControlConnection;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.NodeState;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+/**
+ * Abstract Java proxy that represents one Tor node (i.e. Tor process) in the
+ * testbed network. Can be a Tor proxy, a Tor router, or a Tor directory.
+ *
+ * @author kloesing
+ */
+public class ProxyNodeImpl implements ProxyNode {
+
+ // TODO make this configurable
+ protected static final File torExecutable = new File("tor");
+
+ /**
+ * Logger for this Node instance which is called "node." plus the node's
+ * name.
+ */
+ protected Logger logger;
+
+ /**
+ * Collects all configuration strings for this node during configuration
+ * phase in the order they are added.
+ */
+ protected List<String> configuration;
+
+ public synchronized NodeState getNodeState() {
+ return this.nodeState;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ": nodeName=\""
+ + this.nodeName + "\", controlPort=" + this.controlPort
+ + ", socksPort=" + this.socksPort;
+ }
+
+ /**
+ * Port on which node will be listening for SOCKS connections.
+ */
+ protected int socksPort;
+
+ private EventManagerImpl eventManager;
+
+ /**
+ * LOGGING OK
+ *
+ * Creates a new node and adds it to the given network. Does not yet create
+ * a new Tor process.
+ *
+ * @param network
+ * Network configuration to which this node belongs.
+ * @param nodeName
+ * The node's name which may only consist of between 1 and 19
+ * alpha-numeric characters.
+ * @param controlPort
+ * Port on which the Tor process will be listening for us as its
+ * controller.
+ *
+ * @throws IllegalArgumentException
+ * If at least one of the parameters is null or has an invalid
+ * value.
+ */
+ public ProxyNodeImpl(NetworkImpl network, String nodeName, int controlPort,
+ int socksPort) {
+
+ // make sure that name is a valid logger name
+ if (nodeName == null || nodeName.length() < 1 || nodeName.length() > 19
+ || !nodeName.matches("[a-zA-Z0-9]*")) {
+
+ // prepare and throw exception
+ String reason = "\"" + nodeName + "\" is not a valid node name!";
+ IllegalArgumentException e = new IllegalArgumentException(reason);
+ throw e;
+ }
+
+ // create logger
+ this.logger = Logger.getLogger(nodeName + "."
+ + this.getClass().getName());
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "ProxyNodeImpl",
+ new Object[] { network, nodeName, controlPort, socksPort });
+
+ // check remaining parameters
+ if (network == null || controlPort < 0 || controlPort > 65535
+ || socksPort < 0 || socksPort > 65535) {
+
+ // prepare and throw exception
+ String reason = "Invalid parameter values: network=" + network
+ + ", controlPort=" + controlPort + ", socksPort="
+ + socksPort;
+ IllegalArgumentException e = new IllegalArgumentException(reason);
+ this.logger.log(Level.SEVERE, reason, e);
+ this.logger.throwing(this.getClass().getName(), "ProxyNodeImpl", e);
+ throw e;
+ }
+
+ // store parameter values
+ this.network = network;
+ this.nodeName = nodeName;
+ this.controlPort = controlPort;
+ this.socksPort = socksPort;
+
+ // obtain reference on event manager from network
+ this.eventManager = network.getEventManagerImpl();
+
+ // determine working directory
+ this.workingDir = new File(this.network.getWorkingDir()
+ .getAbsolutePath()
+ + File.separator + nodeName + File.separator);
+
+ // create working directory
+ this.logger.log(Level.FINER, "Creating working directory \""
+ + this.workingDir + "\"...");
+ this.workingDir.mkdirs();
+ this.logger.log(Level.FINE, "Created working directory \""
+ + this.workingDir + "\"!");
+
+ // create reference on config file
+ this.configFile = new File(this.workingDir.getAbsolutePath()
+ + File.separator + "torrc");
+
+ // initialize configuration with general-purpose configurations
+ this.configuration = new ArrayList<String>();
+ this.configuration.add("DataDirectory .");
+ this.configuration.add("SafeLogging 0");
+ this.configuration.add("UseEntryGuards 0");
+ this.configuration.add("ControlPort " + controlPort);
+ this.configuration.add("SocksPort " + socksPort);
+
+ // TODO why is this not the standard????
+ this.configuration.add("RunAsDaemon 0");
+
+ // TODO setting log level to lower level than NOTICE leads to error when
+ // connecting via control port! why?
+ this.configuration.add("Log notice stdout");
+
+ // allow two nodes on the same circuit to be in the same /16 net
+ // TODO this depends in private or public network setting!!!
+ // TODO even more important: this only works since Tor 0.1.2.x!!!
+ this.configuration.add("EnforceDistinctSubnets 0");
+
+ // don't rely on node verification, yet... TODO change?
+ this.configuration
+ .add("AllowInvalidNodes middle,rendezvous,exit,entry,introduction");
+
+ // initialize state
+ this.nodeState = NodeState.CONFIGURING;
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "ProxyNodeImpl");
+
+ }
+
+ /**
+ * LOGGING OK
+ *
+ * @see de.uniba.wiai.lspi.puppetor.ProxyNode#configureDirServers(java.util.Set)
+ */
+ public synchronized void configureDirServers(
+ Set<String> authorizedDirServerStrings) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "configureDirServers",
+ authorizedDirServerStrings);
+
+ // check parameter
+ if (authorizedDirServerStrings == null) {
+
+ // prepare and throw exception
+ String reason = "Parameter may not be null!";
+ IllegalArgumentException e = new IllegalArgumentException(reason);
+ this.logger.log(Level.SEVERE, reason, e);
+ this.logger.throwing(this.getClass().getName(),
+ "configureDirServers", e);
+ throw e;
+ }
+
+ // add to configuration
+ this.configuration.addAll(authorizedDirServerStrings);
+ this.logger.log(Level.FINE, "Added "
+ + authorizedDirServerStrings.size()
+ + " DirServer entries to configuration!");
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "configureDirServers");
+
+ }
+
+ /**
+ * Network to which this node belongs.
+ */
+ protected NetworkImpl network;
+
+ /**
+ * Name of this node that is used as part of the node's working directory
+ * and logger.
+ */
+ protected String nodeName;
+
+ /**
+ * Port on which the process will be listening for us as its controller.
+ */
+ protected int controlPort;
+
+ /**
+ * Directory in which all information concerning this node is stored.
+ */
+ protected File workingDir;
+
+ /**
+ * LOGGING OK
+ *
+ * @param maximumTimeToWaitInMillis <
+ * 0 bedeutet unendlich lange
+ *
+ */
+ public synchronized boolean startNode(long maximumTimeToWaitInMillis)
+ throws TorProcessException {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "startNode",
+ maximumTimeToWaitInMillis);
+
+ // check state
+ if (this.nodeState != NodeState.CONFIGURATION_WRITTEN) {
+
+ // prepare and throw exception
+ String reason = "Node is not in state "
+ + "NodeState.CONFIGURATION_WRITTEN!";
+ IllegalStateException e = new IllegalStateException(reason);
+ this.logger.log(Level.SEVERE, reason, e);
+ this.logger.throwing(this.getClass().getName(), "startNode", e);
+ throw e;
+ }
+
+ // start process
+ ProcessBuilder processBuilder = new ProcessBuilder(torExecutable
+ .getPath(), "-f", "torrc");
+ processBuilder.directory(this.workingDir);
+ processBuilder.redirectErrorStream(true);
+ try {
+ this.torProcess = processBuilder.start();
+ this.logger.log(Level.FINE, "Started Tor process successfully!");
+ } catch (IOException e) {
+
+ // prepare and throw exception
+ String reason = "Could not start Tor process!";
+ TorProcessException e1 = new TorProcessException(reason, e);
+ this.logger.log(Level.SEVERE, reason, e1);
+ this.logger.throwing(this.getClass().getName(), "startNode", e1);
+ throw e1;
+ }
+
+ // start thread to parse output
+ final BufferedReader br = new BufferedReader(new InputStreamReader(
+ this.torProcess.getInputStream()));
+ Thread outputThread = new Thread() {
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ String line = null;
+ boolean waitingForControlPort = true;
+
+ // read output from Tor
+ try {
+
+ while ((line = br.readLine()) != null) {
+ logger.log(Level.FINEST, line);
+
+ // if control port has not been opened, check log
+ // statement for Tor to say this
+ if (waitingForControlPort
+ && line.contains("Opening Control listener on")) {
+ logger.log(Level.FINE, "Observed event of type "
+ + "Event.NODE_CONTROL_PORT_OPENED");
+ eventManager.observeEvent(ProxyNodeImpl.this,
+ Event.NODE_CONTROL_PORT_OPENED);
+ waitingForControlPort = false;
+ }
+ }
+ } catch (IOException e) {
+
+ // don't throw this exception, nobody will catch it!
+ String reason = "IOException when reading output from Tor "
+ + "process!";
+ logger.log(Level.WARNING, reason, e);
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+ };
+ outputThread.setDaemon(true);
+ outputThread.setName(this.nodeName + " Output Parser");
+ outputThread.start();
+ this.logger.log(Level.FINE, "Started thread to parse output!");
+
+ // add shutdown hook that kills the process on JVM exit
+ final Process p = this.torProcess;
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ // destroy Tor process
+ p.destroy();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+ });
+ this.logger.log(Level.FINER,
+ "Started shutdown hook that will destroy the Tor process on "
+ + "JVM exit!");
+
+ // wait for Tor to open the control port
+ this.logger.log(Level.FINER,
+ "Waiting for Tor to open its control port...");
+ if (!this.eventManager.waitForAnyOccurence(this,
+ Event.NODE_CONTROL_PORT_OPENED, maximumTimeToWaitInMillis)) {
+
+ // Tor did not open its control port
+ this.logger.log(Level.WARNING,
+ "Tor did not manage to open its control port within "
+ + maximumTimeToWaitInMillis + " millis!");
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "startNode", false);
+ return false;
+ }
+ this.logger.log(Level.FINE,
+ "Tor has successfully opened its control port and told us "
+ + "about that!");
+
+ // connect to the controller
+ this.logger.log(Level.FINER, "Connecting to control port...");
+ try {
+ Socket controlSocket = new java.net.Socket("127.0.0.1", controlPort);
+ this.conn = TorControlConnection.getConnection(controlSocket);
+ this.conn.authenticate(new byte[0]);
+ } catch (IOException e) {
+
+ // prepare and throw exception
+ String reason = "Could not connect to control port!";
+ TorProcessException e1 = new TorProcessException(reason, e);
+ this.logger.log(Level.SEVERE, reason, e1);
+ this.logger.throwing(this.getClass().getName(), "startNode", e1);
+ throw e1;
+ }
+ this.logger.log(Level.FINE, "Connected to control port successfully!");
+
+ // register event handler at Tor process
+ EventHandler handler = new EventHandler() {
+
+ public void bandwidthUsed(long read, long written) {
+ logger.log(Level.FINEST, "bandwidthUsed(read, written) = "
+ + read + ", " + written);
+ }
+
+ public void circuitStatus(String status, String circID, String path) {
+ logger.log(Level.FINEST,
+ "circuitStatus(status, circID, path) = " + status
+ + ", " + circID + ", " + path);
+ }
+
+ public void message(String severity, String msg) {
+ logger.log(Level.FINEST, "message(severity, msg) = " + severity
+ + ", " + msg);
+ if (msg.contains("Uploading rendezvous descriptor")) {
+ logger.log(Level.FINER, "Observed event of type "
+ + "NODE_RSD_PUBLISHED");
+ eventManager.observeEvent(ProxyNodeImpl.this,
+ Event.NODE_RSD_PUBLISHED);
+ } else if (msg
+ .contains("Tor has successfully opened a circuit.")) {
+ logger.log(Level.FINER, "Observed event of type "
+ + "NODE_CIRCUIT_OPENED");
+ eventManager.observeEvent(ProxyNodeImpl.this,
+ Event.NODE_CIRCUIT_OPENED);
+ } else {
+ // log statement did not contain anything of interest for
+ // us...
+ }
+ }
+
+ public void newDescriptors(List orList) {
+ StringBuilder sb = new StringBuilder(
+ "newDescriptors(orList) =\n");
+ for (Object o : orList) {
+ sb.append(o + "\n");
+ }
+ logger.log(Level.FINEST, sb.toString());
+ }
+
+ public void orConnStatus(String status, String orName) {
+ logger.log(Level.FINEST, "orConnStatus(status, orName) = "
+ + status + ", " + orName);
+ }
+
+ public void streamStatus(String status, String streamID,
+ String target) {
+ logger.log(Level.FINEST,
+ "streamStatus(status, streamID, target) = " + status
+ + ", " + streamID + ", " + target);
+ }
+
+ public void unrecognized(String type, String msg) {
+ logger.log(Level.FINEST, "unrecognized(type, msg) = " + type
+ + ", " + msg);
+ }
+ };
+ conn.setEventHandler(handler);
+
+ // register for logging events of level INFO and higher
+ List<String> events = new ArrayList<String>();
+ events.add("INFO");
+ events.add("NOTICE");
+ events.add("WARN");
+ events.add("ERR");
+ try {
+
+ // register for events using controller
+ this.conn.setEvents(events);
+ } catch (IOException e) {
+
+ // prepare and throw exception
+ String reason = "Could not register event handler at Tor process!";
+ TorProcessException e1 = new TorProcessException(reason, e);
+ this.logger.log(Level.SEVERE, reason, e1);
+ this.logger.throwing(this.getClass().getName(), "startNode", e1);
+ throw e1;
+ }
+ this.logger.log(Level.FINE, "Registered event handler at Tor process!");
+
+ // set state to RUNNING
+ this.nodeState = NodeState.RUNNING;
+
+ // log exiting and return with success
+ this.logger.exiting(this.getClass().getName(), "startNode", true);
+ return true;
+ }
+
+ /**
+ * Handle on running Tor process.
+ */
+ protected Process torProcess;
+
+ public synchronized void addHiddenService(String serviceName,
+ int servicePort, int virtualPort) {
+
+ // check state
+ if (this.nodeState != NodeState.CONFIGURING) {
+ this.logger.log(Level.SEVERE,
+ "Hidden service can only be added when node is in state "
+ + "CONFIGURING!");
+ throw new IllegalStateException(
+ "Hidden service can only be added when node is in state "
+ + "CONFIGURING!");
+ }
+
+ // check params
+ if (serviceName == null || serviceName.length() == 0 || servicePort < 0
+ || servicePort > 65535 || virtualPort < 0
+ || virtualPort > 65535) {
+ this.logger.log(Level.SEVERE,
+ "Illegal argument when adding hidden service!");
+ throw new IllegalArgumentException();
+ }
+
+ // add hidden service using Tor controller
+ this.configuration.add("HiddenServiceDir "
+ + workingDir.getAbsolutePath() + "/" + serviceName);
+ this.configuration.add("HiddenServicePort " + virtualPort
+ + " 127.0.0.1:" + servicePort);
+
+ }
+
+ public synchronized String getOnionAddress(String serviceName)
+ throws TorProcessException {
+
+ // check parameter
+ if (serviceName == null || serviceName.length() == 0) {
+ this.logger.log(Level.SEVERE, "Illegal argument!");
+ throw new IllegalArgumentException();
+ }
+
+ // check if hidden service directory exists
+ File hiddenServiceFile = new File(workingDir.getAbsolutePath()
+ + File.separator + serviceName + File.separator + "hostname");
+ if (!hiddenServiceFile.exists()) {
+ this.logger.log(Level.SEVERE,
+ "Hidden service directory does not exist: "
+ + hiddenServiceFile.getAbsolutePath());
+ throw new TorProcessException(
+ "Hidden service directory does not exist: "
+ + hiddenServiceFile.getAbsolutePath());
+ }
+
+ // read hostname from file
+ String address = null;
+ try {
+ // TODO use FileReader
+ FileInputStream fis = new FileInputStream(hiddenServiceFile);
+ BufferedInputStream bis = new BufferedInputStream(fis);
+ byte[] bytes = new byte[16];
+ bis.read(bytes);
+ address = new String(bytes) + ".onion";
+ bis.close();
+ } catch (IOException e) {
+ throw new TorProcessException("Could not read hostname file!", e);
+ }
+
+ return address;
+ }
+
+ /**
+ * Connection via Tor controller.
+ */
+ protected TorControlConnection conn;
+
+ protected NodeState nodeState = NodeState.CONFIGURING;
+
+ protected File configFile;
+
+ public synchronized void writeConfiguration() throws TorProcessException {
+
+ // check state
+ if (this.nodeState != NodeState.CONFIGURING) {
+ throw new IllegalStateException();
+ }
+
+ // write config file
+ try {
+ BufferedWriter bw = new BufferedWriter(new FileWriter(configFile));
+ for (String c : this.configuration) {
+ bw.write(c + "\n");
+ }
+ bw.close();
+ } catch (IOException e) {
+ throw new TorProcessException("Could not write configuration!", e);
+ }
+
+ // change state
+ this.nodeState = NodeState.CONFIGURATION_WRITTEN;
+ }
+
+ /**
+ * @see de.uniba.wiai.lspi.puppetor.ProxyNode#shutdown()
+ */
+ public synchronized void shutdown() throws TorProcessException {
+
+ // check state
+ if (this.nodeState != NodeState.RUNNING) {
+ throw new IllegalStateException();
+ }
+
+ // we cannot simply kill the tor.exe process, because we have
+ // established a controller connection to it which would interpret a
+ // closed socket as failure and throw a RuntimeException
+ // TODO who cares?!
+ try {
+ this.conn.stopThread();
+ this.conn.shutdownTor("SHUTDOWN");
+ this.conn.shutdownTor("SHUTDOWN");
+ } catch (IOException e) {
+ this.logger.log(Level.SEVERE,
+ "Could not send shutdown command to Tor process!", e);
+ throw new TorProcessException(
+ "Could not send shutdown command to Tor process!", e);
+ }
+
+ // change state
+ this.nodeState = NodeState.SHUT_DOWN;
+ }
+
+ // /**
+ // * Flags that denotes whether certain events have been observed in the
+ // log.
+ // * In general, this requires logging to be done at least on notice level!
+ // */
+ // protected Set<Event> observedEvents;
+
+ // public synchronized boolean hasEventOccured(Event event) {
+ // return observedEvents.contains(event);
+ // }
+
+ public synchronized void hup() throws TorProcessException {
+
+ // check state
+ if (this.nodeState != NodeState.RUNNING) {
+ this.logger.log(Level.SEVERE,
+ "Cannot hup a process when it's not running!");
+ throw new IllegalStateException(
+ "Cannot hup a process when it's not running!");
+ }
+
+ // should we check whether conn is null?
+ if (this.conn == null) {
+ System.out.println("WARNING! conn is null!");
+ }
+ try {
+ // TODO sometimes, this throws a NullPointerException...
+ this.conn.signal("HUP");
+ } catch (IOException e) {
+ this.logger.log(Level.SEVERE,
+ "Could not send the command HUP to the Tor process!", e);
+ throw new TorProcessException(
+ "Could not send the command HUP to the Tor process!", e);
+ }
+ }
+
+ // public synchronized void addConfig(String newConfiguration) {
+ // if (this.nodeStatus != Status.CONFIGURING) {
+ // throw new IllegalStateException();
+ // }
+ // // TODO should we check the new config?!
+ // this.configuration.add(newConfiguration);
+ // }
+
+ // public String getConfiguration() {
+ // StringBuilder result = new StringBuilder("Node configuration: ");
+ // for (String next : this.configuration) {
+ // result.append(next + "\n");
+ // }
+ // return result.toString();
+ // }
+
+ /**
+ * Adds a hidden service configuration to this node while the node is
+ * running.
+ *
+ * TODO this should be possible both, during configuration and when running
+ * a network...
+ */
+ public synchronized String addHiddenServiceUsingController(
+ String serviceName, int servicePort, int virtualPort) {
+ if (1 != 2)
+ throw new UnsupportedOperationException();
+ // TODO check state
+
+ // check params
+ if (serviceName == null || serviceName.length() == 0 || servicePort < 0
+ || servicePort > 65535 || virtualPort < 0
+ || virtualPort > 65535) {
+ throw new IllegalArgumentException();
+ }
+
+ // String serverName = server.getServerName();
+ // int serverPort = server.getServerPort();
+
+ // add hidden service using Tor controller
+ this.logger.log(Level.FINE,
+ "Adding hidden service using Tor controller.");
+
+ List<String> configs = new ArrayList<String>();
+ configs.add("HiddenServiceDir " + workingDir.getAbsolutePath() + "/"
+ + serviceName);
+ configs.add("HiddenServicePort " + virtualPort + " 127.0.0.1:"
+ + servicePort);
+ try {
+ conn.setConf(configs);
+ } catch (IOException e) {
+ throw new RuntimeException(
+ "IOException when trying to register a new "
+ + "hidden service", e);
+ }
+ this.logger.log(Level.FINE,
+ "Hidden service successfully registered at Tor proxy.");
+
+ //
+ File hiddenServiceFile = new File(workingDir.getAbsolutePath() + "\\"
+ + serviceName + "\\hostname");
+ if (!hiddenServiceFile.exists()) {
+ throw new RuntimeException();
+ }
+ // read hostname from file
+ String address = null;
+ try {
+ // TODO use FileReader
+ FileInputStream fis = new FileInputStream(hiddenServiceFile);
+ BufferedInputStream bis = new BufferedInputStream(fis);
+ byte[] bytes = new byte[16];
+ bis.read(bytes);
+ address = new String(bytes) + ".onion";
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return address;
+ }
+
+ public synchronized String getNodeName() {
+ return this.nodeName;
+ }
+
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,189 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+
+import de.uniba.wiai.lspi.puppetor.RouterNode;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+public class RouterNodeImpl extends ProxyNodeImpl implements RouterNode {
+
+ /**
+ * OR port of this router.
+ */
+ protected int orPort;
+
+ /**
+ * Directory port of this router.
+ */
+ protected int dirPort;
+
+ /**
+ * This node's fingerprint that is determined as hash value of this node's
+ * onion routing key.
+ */
+ protected String fingerprint;
+
+ @Override
+ public String toString() {
+ return super.toString() + ", orPort=" + this.orPort + ", dirPort="
+ + this.dirPort;
+ }
+
+ public RouterNodeImpl(NetworkImpl network, String nodeName,
+ int controlPort, int socksPort, int orPort, int dirPort) {
+ super(network, nodeName, controlPort, socksPort);
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "RouterNodeImpl",
+ new Object[] { network, nodeName, controlPort, socksPort,
+ orPort, dirPort });
+
+ // check parameters
+ if (orPort < 0 || orPort > 65535 || dirPort < 0 || dirPort > 65535) {
+ throw new IllegalArgumentException();
+ }
+
+ // remember parameters
+ this.orPort = orPort;
+ this.dirPort = dirPort;
+
+ // add further configuration to make this node a router node
+ this.configuration.add("ORPort " + orPort);
+ this.configuration.add("Nickname " + nodeName);
+ this.configuration.add("ContactInfo wont@xxxxxxxxx");
+
+ // all routers mirror the directory
+ this.configuration.add("DirPort " + dirPort);
+
+ // this node's address should be localhost and not guessed by Tor
+ this.configuration.add("Address 127.0.0.1");
+
+ // the OR port may only be contacted locally
+ this.configuration.add("ORListenAddress 127.0.0.1");
+
+ // offer directory only locally (either by being an authority, or by
+ // mirroring it)
+ this.configuration.add("DirListenAddress 127.0.0.1");
+
+ // allow exit to private network and everything else (node will only
+ // be used by other nodes in the private network, so no worry)
+ this.configuration.add("ExitPolicyRejectPrivate 0");
+ this.configuration.add("ExitPolicy accept *:*");
+
+ // bypass testing if we are reachable
+ this.configuration.add("AssumeReachable 1");
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "RouterNodeImpl");
+
+ }
+
+ /**
+ * @see de.uniba.wiai.lspi.puppetor.RouterNode#determineFingerprint()
+ */
+ public synchronized String determineFingerprint()
+ throws TorProcessException {
+ if (this.fingerprint != null) {
+
+ // we can answer the request immediately
+ return this.fingerprint;
+ }
+
+
+ // write modified config file, including a DirServer option with
+ // false fingerprint; this is necessary, because otherwise Tor
+ // would not accept that this router node has a private IP
+ // address, but connects to the public directory servers; just a
+ // workaround...
+ Set<String> copyOfConfig = new HashSet<String>(this.configuration);
+ // TODO when working dir exists, but Tor did not manage to write
+ // a fingerprint file, we get a NullPointerException...
+ String fakeDirServerString = "DirServer " + this.nodeName
+ + " 127.0.0.1:" + this.dirPort
+ + " 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000";
+ copyOfConfig.add(fakeDirServerString);
+ try {
+ BufferedWriter bw = new BufferedWriter(new FileWriter(configFile));
+ for (String c : copyOfConfig) {
+ bw.write(c + "\n");
+ }
+ bw.close();
+ } catch (IOException e) {
+ throw new TorProcessException(
+ "Could not write temporary config file!", e);
+ }
+
+ // start process with option --list-fingerprint
+ ProcessBuilder processBuilder = new ProcessBuilder(torExecutable
+ .getPath(), "--list-fingerprint", "-f", "torrc");
+ processBuilder.directory(this.workingDir);
+ processBuilder.redirectErrorStream(true);
+ Process tmpProcess = null;
+
+ try {
+ tmpProcess = processBuilder.start();
+ } catch (IOException e) {
+ throw new TorProcessException(
+ "Could not start Tor process temporarily with "
+ + "--list-fingerprint option!", e);
+ }
+
+ // start thread to parse output
+ final BufferedReader br = new BufferedReader(new InputStreamReader(
+ tmpProcess.getInputStream()));
+ Thread outputThread = new Thread(new Runnable() {
+ public void run() {
+ try {
+ // Read output from tor
+ String line = null;
+ while ((line = br.readLine()) != null) {
+ // TODO discard output?
+ logger.log(Level.FINER, line);
+ }
+ } catch (IOException e) {
+ // TODO handle?
+ }
+ }
+ });
+ outputThread.setDaemon(true);
+ outputThread.start();
+
+ // wait for process to terminate
+ // TODO should limit waiting to a certain time?!
+ try {
+ tmpProcess.waitFor();
+ } catch (InterruptedException e) {
+ // TODO handle?
+ }
+
+ // read fingerprint from file
+
+ File fingerprintFile = new File(this.workingDir.getAbsolutePath()
+ + File.separator + "fingerprint");
+ try {
+ BufferedReader br2 = new BufferedReader(new FileReader(
+ fingerprintFile));
+ this.fingerprint = br2.readLine();
+ br2.close();
+ } catch (IOException e) {
+ throw new TorProcessException(
+ "Could not read fingerprint from file!", e);
+ }
+
+ return this.fingerprint;
+ }
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,222 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+
+public class ServerApplicationImpl implements ServerApplication {
+
+ /**
+ * logger fÃr diesen server
+ */
+ private Logger logger;
+
+ private EventManagerImpl eventManager;
+
+ private int serverPort;
+
+ /**
+ * erzeugt neue server app innerhalb der jvm, fÃngt aber noch nicht an mit
+ * listening.
+ *
+ */
+ ServerApplicationImpl(NetworkImpl network, String serverApplicationName,
+ int serverPort) {
+
+ // TODO make sure that name is loggable!!
+
+ // create logger
+ this.logger = Logger.getLogger("application." + serverApplicationName);
+
+ // log entering
+ this.logger.entering(this.getClass().getName(),
+ "ServerApplicationImpl", new Object[] { serverApplicationName,
+ serverPort });
+
+ // check parameters
+ if (serverApplicationName == null
+ || serverApplicationName.length() == 0 || serverPort < 0
+ || serverPort > 65535) {
+ throw new IllegalArgumentException();
+ }
+
+ // remember parameters
+ this.serverPort = serverPort;
+
+ // obtain reference on event manager
+ this.eventManager = network.getEventManagerImpl();
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "ClientApplicationImpl");
+ }
+
+ private Thread serverThread;
+
+ public void stopListening() {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "stopListening");
+
+ // check if a request is running
+ if (this.serverThread == null) {
+ throw new IllegalStateException(
+ "We did not start listening before!");
+ }
+
+ // log this event
+ this.logger.log(Level.FINE, "Shutting down server");
+
+ // interrupt thread
+ this.serverThread.interrupt();
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "stopListening");
+
+ }
+
+ public void listen() {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "listen");
+
+ // check if we are already listening
+ if (this.serverThread != null) {
+ throw new IllegalStateException("We are already listening!");
+ }
+
+ // create a thread that listens in the background
+ this.serverThread = new ListenThread();
+ this.serverThread.setName("Reply Thread");
+ this.serverThread.setDaemon(true);
+ this.serverThread.start();
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "listen");
+ }
+
+ private class ListenThread extends Thread {
+
+ private boolean connected;
+
+ ListenThread() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "ListenThread");
+
+ // start connected
+ this.connected = true;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "ListenThread");
+ }
+
+ /**
+ * stoppt listening
+ *
+ */
+ public void stopListening() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "stopListening");
+
+ // change connected state to false and interrupt thread
+ this.connected = false;
+ this.interrupt();
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "stopListening");
+ }
+
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ try {
+
+ ServerSocket serverSocket = null;
+
+ try {
+ serverSocket = new ServerSocket(serverPort);
+ } catch (IOException ioe) {
+ System.out.println("Can't open server socket on port "
+ + serverPort + "!");
+ }
+ System.out.println("Listening on port " + serverPort + "...");
+ while (connected) {
+ try {
+ Socket incomingConnection = serverSocket.accept();
+ HandlerThread handler = new HandlerThread(
+ incomingConnection);
+ new Thread(handler).start();
+ } catch (Exception e) {
+ }
+ }
+
+ } catch (Exception e) {
+
+ // log that we have been interrupted
+ logger.log(Level.WARNING, "Server has been interrupted!", e);
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+
+ }
+
+ }
+
+ private class HandlerThread implements Runnable {
+
+ private Socket handleSocket = null;
+
+ public HandlerThread(Socket requestSocket) {
+ handleSocket = requestSocket;
+ }
+
+ public void run() {
+
+ try {
+
+ // wait for request (don't mind content)
+ BufferedReader in = new BufferedReader(new InputStreamReader(
+ handleSocket.getInputStream()));
+ in.read();
+
+ // send event to event manager
+ eventManager.observeEvent(ServerApplicationImpl.this,
+ Event.APPLICATION_REQUEST_RECEIVED);
+
+ // write response
+ PrintStream out = new PrintStream(handleSocket
+ .getOutputStream());
+ out.print("HTTP/1.0 200 OK\r\n");
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ try {
+ // handleSocket.getInputStream().close();
+ // handleSocket.getOutputStream().flush();
+ // handleSocket.getOutputStream().close();
+ handleSocket.close();
+ } catch (Exception e) {
+ }
+ }
+ }
+}
Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/.cvsignore
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/.cvsignore 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/.cvsignore 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1 @@
+*.class
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/.cvsignore
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/Bytes.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/Bytes.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/Bytes.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,115 @@
+// $Id: Bytes.java 8419 2006-09-19 14:57:31Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Static class to do bytewise structure manipulation in Java.
+ */
+/* XXXX There must be a better way to do most of this.
+ * XXXX The string logic here uses default encoding, which is stupid.
+ */
+final class Bytes {
+
+ /** Write the two-byte value in 's' into the byte array 'ba', starting at
+ * the index 'pos'. */
+ public static void setU16(byte[] ba, int pos, short s) {
+ ba[pos] = (byte)((s >> 8) & 0xff);
+ ba[pos+1] = (byte)((s ) & 0xff);
+ }
+
+ /** Write the four-byte value in 'i' into the byte array 'ba', starting at
+ * the index 'pos'. */
+ public static void setU32(byte[] ba, int pos, int i) {
+ ba[pos] = (byte)((i >> 24) & 0xff);
+ ba[pos+1] = (byte)((i >> 16) & 0xff);
+ ba[pos+2] = (byte)((i >> 8) & 0xff);
+ ba[pos+3] = (byte)((i ) & 0xff);
+ }
+
+ /** Return the four-byte value starting at index 'pos' within 'ba' */
+ public static int getU32(byte[] ba, int pos) {
+ return
+ ((ba[pos ]&0xff)<<24) |
+ ((ba[pos+1]&0xff)<<16) |
+ ((ba[pos+2]&0xff)<< 8) |
+ ((ba[pos+3]&0xff));
+ }
+
+ public static String getU32S(byte[] ba, int pos) {
+ return String.valueOf( ((long)getU32(ba,pos))&0xffffffffL );
+ }
+
+ /** Return the two-byte value starting at index 'pos' within 'ba' */
+ public static int getU16(byte[] ba, int pos) {
+ return
+ ((ba[pos ]&0xff)<<8) |
+ ((ba[pos+1]&0xff));
+ }
+
+ /** Return the string starting at position 'pos' of ba and extending
+ * until a zero byte or the end of the string. */
+ public static String getNulTerminatedStr(byte[] ba, int pos) {
+ int len, maxlen = ba.length-pos;
+ for (len=0; len<maxlen; ++len) {
+ if (ba[pos+len] == 0)
+ break;
+ }
+ return new String(ba, pos, len);
+ }
+
+ /**
+ * Read bytes from 'ba' starting at 'pos', dividing them into strings
+ * along the character in 'split' and writing them into 'lst'
+ */
+ public static void splitStr(List lst, byte[] ba, int pos, byte split) {
+ while (pos < ba.length && ba[pos] != 0) {
+ int len;
+ for (len=0; pos+len < ba.length; ++len) {
+ if (ba[pos+len] == 0 || ba[pos+len] == split)
+ break;
+ }
+ if (len>0)
+ lst.add(new String(ba, pos, len));
+ pos += len;
+ if (ba[pos] == split)
+ ++pos;
+ }
+ }
+
+ /**
+ * Read bytes from 'ba' starting at 'pos', dividing them into strings
+ * along the character in 'split' and writing them into 'lst'
+ */
+ public static List splitStr(List lst, String str) {
+ // split string on spaces, include trailing/leading
+ String[] tokenArray = str.split(" ", -1);
+ if (lst == null) {
+ lst = Arrays.asList( tokenArray );
+ } else {
+ lst.addAll( Arrays.asList( tokenArray ) );
+ }
+ return lst;
+ }
+
+ private static final char[] NYBBLES = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+ public static final String hex(byte[] ba) {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < ba.length; ++i) {
+ int b = ((int)ba[i]) & 0xff;
+ buf.append(NYBBLES[b >> 4]);
+ buf.append(NYBBLES[b&0x0f]);
+ }
+ return buf.toString();
+ }
+
+ private Bytes() {};
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/Bytes.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/ConfigEntry.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/ConfigEntry.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/ConfigEntry.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,14 @@
+// $Id: ConfigEntry.java 6843 2005-07-14 20:26:11Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+/** A single key-value pair from Tor's configuration. */
+public class ConfigEntry {
+ public ConfigEntry(String k, String v) {
+ key = k;
+ value = v;
+ }
+ public final String key;
+ public final String value;
+}
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/ConfigEntry.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/EventHandler.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/EventHandler.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/EventHandler.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,76 @@
+// $Id: EventHandler.java 6853 2005-07-23 01:17:27Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+/**
+ * Abstract interface whose methods are invoked when Tor sends us an event.
+ *
+ * @see TorControlConnection#setEventHandler
+ * @see TorControlConnection#setEvents
+ */
+public interface EventHandler {
+ /**
+ * Invoked when a circuit's status has changed.
+ * Possible values for <b>status</b> are:
+ * <ul>
+ * <li>"LAUNCHED" : circuit ID assigned to new circuit</li>
+ * <li>"BUILT" : all hops finished, can now accept streams</li>
+ * <li>"EXTENDED" : one more hop has been completed</li>
+ * <li>"FAILED" : circuit closed (was not built)</li>
+ * <li>"CLOSED" : circuit closed (was built)</li>
+ * </ul>
+ *
+ * <b>circID</b> is the alphanumeric identifier of the affected circuit,
+ * and <b>path</b> is a comma-separated list of alphanumeric ServerIDs.
+ */
+ public void circuitStatus(String status, String circID, String path);
+ /**
+ * Invoked when a stream's status has changed.
+ * Possible values for <b>status</b> are:
+ * <ul>
+ * <li>"NEW" : New request to connect</li>
+ * <li>"NEWRESOLVE" : New request to resolve an address</li>
+ * <li>"SENTCONNECT" : Sent a connect cell along a circuit</li>
+ * <li>"SENTRESOLVE" : Sent a resolve cell along a circuit</li>
+ * <li>"SUCCEEDED" : Received a reply; stream established</li>
+ * <li>"FAILED" : Stream failed and not retriable.</li>
+ * <li>"CLOSED" : Stream closed</li>
+ * <li>"DETACHED" : Detached from circuit; still retriable.</li>
+ * </ul>
+ *
+ * <b>streamID</b> is the alphanumeric identifier of the affected stream,
+ * and its <b>target</b> is specified as address:port.
+ */
+ public void streamStatus(String status, String streamID, String target);
+ /**
+ * Invoked when the status of a connection to an OR has changed.
+ * Possible values for <b>status</b> are ["LAUNCHED" | "CONNECTED" | "FAILED" | "CLOSED"].
+ * <b>orName</b> is the alphanumeric identifier of the OR affected.
+ */
+ public void orConnStatus(String status, String orName);
+ /**
+ * Invoked once per second. <b>read</b> and <b>written</b> are
+ * the number of bytes read and written, respectively, in
+ * the last second.
+ */
+ public void bandwidthUsed(long read, long written);
+ /**
+ * Invoked whenever Tor learns about new ORs. The <b>orList</b> object
+ * contains the alphanumeric ServerIDs associated with the new ORs.
+ */
+ public void newDescriptors(java.util.List orList);
+ /**
+ * Invoked when Tor logs a message.
+ * <b>severity</b> is one of ["DEBUG" | "INFO" | "NOTICE" | "WARN" | "ERR"],
+ * and <b>msg</b> is the message string.
+ */
+ public void message(String severity, String msg);
+ /**
+ * Invoked when an unspecified message is received.
+ * <type> is the message type, and <msg> is the message string.
+ */
+ public void unrecognized(String type, String msg);
+
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/EventHandler.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/NullEventHandler.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/NullEventHandler.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/NullEventHandler.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,19 @@
+// $Id: NullEventHandler.java 6833 2005-07-11 19:14:18Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+/**
+ * Implementation of EventHandler that ignores all events. Useful
+ * when you only want to override one method.
+ */
+public class NullEventHandler implements EventHandler {
+ public void circuitStatus(String status, String circID, String path) {}
+ public void streamStatus(String status, String streamID, String target) {}
+ public void orConnStatus(String status, String orName) {}
+ public void bandwidthUsed(long read, long written) {}
+ public void newDescriptors(java.util.List orList) {}
+ public void message(String severity, String msg) {}
+ public void unrecognized(String type, String msg) {}
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/NullEventHandler.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/PasswordDigest.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/PasswordDigest.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/PasswordDigest.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,103 @@
+// $Id: PasswordDigest.java 6831 2005-06-24 18:03:27Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+import java.security.SecureRandom;
+import java.security.MessageDigest;
+
+/**
+ * A hashed digest of a secret password (used to set control connection
+ * security.)
+ *
+ * For the actual hashing algorithm, see RFC2440's secret-to-key conversion.
+ */
+public class PasswordDigest {
+
+ byte[] secret;
+ String hashedKey;
+
+ /** Return a new password digest with a random secret and salt. */
+ public static PasswordDigest generateDigest() {
+ byte[] secret = new byte[20];
+ SecureRandom rng = new SecureRandom();
+ rng.nextBytes(secret);
+ return new PasswordDigest(secret);
+ }
+
+ /** Construct a new password digest with a given secret and random salt */
+ public PasswordDigest(byte[] secret) {
+ this(secret, null);
+ }
+
+ /** Construct a new password digest with a given secret and random salt.
+ * Note that the 9th byte of the specifier determines the number of hash
+ * iterations as in RFC2440.
+ */
+ public PasswordDigest(byte[] secret, byte[] specifier) {
+ this.secret = secret;
+ if (specifier == null) {
+ specifier = new byte[9];
+ SecureRandom rng = new SecureRandom();
+ rng.nextBytes(specifier);
+ specifier[8] = 96;
+ }
+ hashedKey = "16:"+encodeBytes(secretToKey(secret, specifier));
+ }
+
+ /** Return the secret used to generate this password hash.
+ */
+ public byte[] getSecret() {
+ return secret;
+ }
+
+ /** Return the hashed password in the format used by Tor. */
+ public String getHashedPassword() {
+ return hashedKey;
+ }
+
+ /** Parameter used by RFC2440's s2k algorithm. */
+ private static final int EXPBIAS = 6;
+
+ /** Implement rfc2440 s2k */
+ public static byte[] secretToKey(byte[] secret, byte[] specifier) {
+ MessageDigest d;
+ try {
+ d = MessageDigest.getInstance("SHA-1");
+ } catch (java.security.NoSuchAlgorithmException ex) {
+ throw new RuntimeException("Can't run without sha-1.");
+ }
+ int c = ((int)specifier[8])&0xff;
+ int count = (16 + (c&15)) << ((c>>4) + EXPBIAS);
+
+ byte[] tmp = new byte[8+secret.length];
+ System.arraycopy(specifier, 0, tmp, 0, 8);
+ System.arraycopy(secret, 0, tmp, 8, secret.length);
+ while (count > 0) {
+ if (count >= tmp.length) {
+ d.update(tmp);
+ count -= tmp.length;
+ } else {
+ d.update(tmp, 0, count);
+ count = 0;
+ }
+ }
+ byte[] key = new byte[20+9];
+ System.arraycopy(d.digest(), 0, key, 9, 20);
+ System.arraycopy(specifier, 0, key, 0, 9);
+ return key;
+ }
+
+ private static final char[] NYBBLES = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+ /** Return a hexadecimal encoding of a byte array. */
+ // XXX There must be a better way to do this in Java.
+ private static final String encodeBytes(byte[] ba) {
+ return Bytes.hex(ba);
+ }
+
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/PasswordDigest.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlCommands.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlCommands.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlCommands.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,149 @@
+// $Id: TorControlCommands.java 6849 2005-07-20 22:48:35Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+/** Interface defining constants used by the Tor controller protocol.
+ */
+// XXXX Take documentation for these from control-spec.txt
+public interface TorControlCommands {
+
+ public static final short CMD_ERROR = 0x0000;
+ public static final short CMD_DONE = 0x0001;
+ public static final short CMD_SETCONF = 0x0002;
+ public static final short CMD_GETCONF = 0x0003;
+ public static final short CMD_CONFVALUE = 0x0004;
+ public static final short CMD_SETEVENTS = 0x0005;
+ public static final short CMD_EVENT = 0x0006;
+ public static final short CMD_AUTH = 0x0007;
+ public static final short CMD_SAVECONF = 0x0008;
+ public static final short CMD_SIGNAL = 0x0009;
+ public static final short CMD_MAPADDRESS = 0x000A;
+ public static final short CMD_GETINFO = 0x000B;
+ public static final short CMD_INFOVALUE = 0x000C;
+ public static final short CMD_EXTENDCIRCUIT = 0x000D;
+ public static final short CMD_ATTACHSTREAM = 0x000E;
+ public static final short CMD_POSTDESCRIPTOR = 0x000F;
+ public static final short CMD_FRAGMENTHEADER = 0x0010;
+ public static final short CMD_FRAGMENT = 0x0011;
+ public static final short CMD_REDIRECTSTREAM = 0x0012;
+ public static final short CMD_CLOSESTREAM = 0x0013;
+ public static final short CMD_CLOSECIRCUIT = 0x0014;
+
+ public static final String[] CMD_NAMES = {
+ "ERROR",
+ "DONE",
+ "SETCONF",
+ "GETCONF",
+ "CONFVALUE",
+ "SETEVENTS",
+ "EVENT",
+ "AUTH",
+ "SAVECONF",
+ "SIGNAL",
+ "MAPADDRESS",
+ "GETINFO",
+ "INFOVALUE",
+ "EXTENDCIRCUIT",
+ "ATTACHSTREAM",
+ "POSTDESCRIPTOR",
+ "FRAGMENTHEADER",
+ "FRAGMENT",
+ "REDIRECTSTREAM",
+ "CLOSESTREAM",
+ "CLOSECIRCUIT",
+ };
+
+ public static final short EVENT_CIRCSTATUS = 0x0001;
+ public static final short EVENT_STREAMSTATUS = 0x0002;
+ public static final short EVENT_ORCONNSTATUS = 0x0003;
+ public static final short EVENT_BANDWIDTH = 0x0004;
+ public static final short EVENT_NEWDESCRIPTOR = 0x0006;
+ public static final short EVENT_MSG_DEBUG = 0x0007;
+ public static final short EVENT_MSG_INFO = 0x0008;
+ public static final short EVENT_MSG_NOTICE = 0x0009;
+ public static final short EVENT_MSG_WARN = 0x000A;
+ public static final short EVENT_MSG_ERROR = 0x000B;
+
+ public static final String[] EVENT_NAMES = {
+ "(0)",
+ "CIRC",
+ "STREAM",
+ "ORCONN",
+ "BW",
+ "OLDLOG",
+ "NEWDESC",
+ "DEBUG",
+ "INFO",
+ "NOTICE",
+ "WARN",
+ "ERR",
+ };
+
+ public static final byte CIRC_STATUS_LAUNCHED = 0x01;
+ public static final byte CIRC_STATUS_BUILT = 0x02;
+ public static final byte CIRC_STATUS_EXTENDED = 0x03;
+ public static final byte CIRC_STATUS_FAILED = 0x04;
+ public static final byte CIRC_STATUS_CLOSED = 0x05;
+
+ public static final String[] CIRC_STATUS_NAMES = {
+ "LAUNCHED",
+ "BUILT",
+ "EXTENDED",
+ "FAILED",
+ "CLOSED",
+ };
+
+ public static final byte STREAM_STATUS_SENT_CONNECT = 0x00;
+ public static final byte STREAM_STATUS_SENT_RESOLVE = 0x01;
+ public static final byte STREAM_STATUS_SUCCEEDED = 0x02;
+ public static final byte STREAM_STATUS_FAILED = 0x03;
+ public static final byte STREAM_STATUS_CLOSED = 0x04;
+ public static final byte STREAM_STATUS_NEW_CONNECT = 0x05;
+ public static final byte STREAM_STATUS_NEW_RESOLVE = 0x06;
+ public static final byte STREAM_STATUS_DETACHED = 0x07;
+
+ public static final String[] STREAM_STATUS_NAMES = {
+ "SENT_CONNECT",
+ "SENT_RESOLVE",
+ "SUCCEEDED",
+ "FAILED",
+ "CLOSED",
+ "NEW_CONNECT",
+ "NEW_RESOLVE",
+ "DETACHED"
+ };
+
+ public static final byte OR_CONN_STATUS_LAUNCHED = 0x00;
+ public static final byte OR_CONN_STATUS_CONNECTED = 0x01;
+ public static final byte OR_CONN_STATUS_FAILED = 0x02;
+ public static final byte OR_CONN_STATUS_CLOSED = 0x03;
+
+ public static final String[] OR_CONN_STATUS_NAMES = {
+ "LAUNCHED","CONNECTED","FAILED","CLOSED"
+ };
+
+ public static final byte SIGNAL_HUP = 0x01;
+ public static final byte SIGNAL_INT = 0x02;
+ public static final byte SIGNAL_USR1 = 0x0A;
+ public static final byte SIGNAL_USR2 = 0x0C;
+ public static final byte SIGNAL_TERM = 0x0F;
+
+ public static final String ERROR_MSGS[] = {
+ "Unspecified error",
+ "Internal error",
+ "Unrecognized message type",
+ "Syntax error",
+ "Unrecognized configuration key",
+ "Invalid configuration value",
+ "Unrecognized byte code",
+ "Unauthorized",
+ "Failed authentication attempt",
+ "Resource exhausted",
+ "No such stream",
+ "No such circuit",
+ "No such OR",
+ };
+
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlCommands.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,249 @@
+// $Id: TorControlConnection.java 9193 2006-12-25 04:37:53Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/** A connection to a running Tor process. */
+public abstract class TorControlConnection// implements TorControlCommands {
+{
+
+ protected EventHandler handler;
+
+ protected LinkedList waiters;
+
+ protected ControlParseThread thread;
+
+ static class Waiter {
+ Object response;
+ public synchronized Object getResponse() {
+ try {
+ while (response == null) {
+ wait();
+ }
+ } catch (InterruptedException ex) {
+ return null;
+ }
+ return response;
+ }
+ public synchronized void setResponse(Object response) {
+ this.response = response;
+ notifyAll();
+ }
+ }
+
+ protected static int detectVersion(java.io.InputStream input,
+ java.io.OutputStream output)
+ throws IOException
+ {
+ java.io.DataInputStream dInput = new java.io.DataInputStream(input);
+ byte out[] = { 0, 0, 13, 10 };
+ output.write(out);
+
+ int len = dInput.readUnsignedShort();
+ int tp = dInput.readUnsignedShort();
+ if (tp == 0) {
+ byte err[] = new byte[len];
+ dInput.readFully(err);
+ return 0;
+ } else if ((len & 0xff00) != 0x0a00 &&
+ (len & 0x00ff) != 0x000a &&
+ (tp & 0xff00) != 0x0a00 &&
+ (tp & 0x00ff) != 0x000a) {
+ while (input.read() != '\n')
+ ;
+ }
+ return 1;
+ }
+
+ public static TorControlConnection getConnection(java.net.Socket sock)
+ throws IOException
+ {
+ int version = detectVersion(sock.getInputStream(),
+ sock.getOutputStream());
+ if (version == 0)
+ return new TorControlConnection0(sock);
+ else
+ return new TorControlConnection1(sock);
+ }
+
+ protected TorControlConnection() {
+ this.waiters = new LinkedList();
+ }
+
+ /** Set the EventHandler object that will be notified of any
+ * events Tor delivers to this connection. To make Tor send us
+ * events, call setEvents(). */
+ public void setEventHandler(EventHandler handler) {
+ this.handler = handler;
+ }
+
+ /**
+ * Start a thread to react to Tor's responses in the background.
+ * This is necessary to handle asynchronous events and synchronous
+ * responses that arrive independantly over the same socket.
+ */
+ public Thread launchThread(boolean daemon) {
+ ControlParseThread th = new ControlParseThread(this);
+ if (daemon)
+ th.setDaemon(true);
+ th.setName("Tor Control Parser");
+ th.start();
+ this.thread = th;
+ return th;
+ }
+
+ private static class ControlParseThread extends Thread {
+ TorControlConnection parent;
+ ControlParseThread(TorControlConnection parent) {
+ this.parent = parent;
+ }
+ boolean stopped = false;
+ public void run() {
+ try {
+ parent.react();
+ } catch (IOException ex) {
+ if (stopped) return; // TODO THIS is imporant!
+ throw new RuntimeException(ex);
+ }
+ }
+ };
+
+ protected final void checkThread() {
+ if (thread == null)
+ launchThread(true);
+ }
+
+ public void stopThread() {
+ if (this.thread != null) {
+ this.thread.stopped = true;
+ this.thread.interrupt();
+ }
+ }
+
+ protected abstract void react() throws IOException;
+
+ /** Change the value of the configuration option 'key' to 'val'.
+ */
+ public void setConf(String key, String value) throws IOException {
+ List lst = new ArrayList();
+ lst.add(key+" "+value);
+ setConf(lst);
+ }
+
+ /** Change the values of the configuration options stored in kvMap. */
+ public void setConf(Map kvMap) throws IOException {
+ List lst = new ArrayList();
+ for (Iterator it = kvMap.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry ent = (Map.Entry) it.next();
+ lst.add(ent.getKey()+" "+ent.getValue()+"\n");
+ }
+ setConf(lst);
+ }
+
+ /** Change the values of the configuration options stored in
+ * 'kvList'. (The format is "key value"). */
+ public abstract void setConf(Collection kvList) throws IOException;
+
+ /** Try to reset the values listed in the collection 'keys' to their
+ * default values.
+ **/
+ public abstract void resetConf(Collection keys) throws IOException;
+
+ /** Return the value of the configuration option 'key' */
+ public List getConf(String key) throws IOException {
+ List lst = new ArrayList();
+ lst.add(key);
+ return getConf(lst);
+ }
+
+ /** Return a key-value map for the configuration options in 'keys' */
+ public abstract List getConf(Collection keys) throws IOException;
+
+ /** Tell Tor to begin sending us events of the types listed in 'events'.
+ * Elements must be one of the EVENT_* values from TorControlCommands */
+ public abstract void setEvents(List events) throws IOException;
+
+ /** Send Tor an authentication sequence 'auth' */
+ // XXXX more info about how to set this up securely.
+ public abstract void authenticate(byte[] auth) throws IOException;
+
+ /** Tell Tor to save the value of its configuration to disk. */
+ public abstract void saveConf() throws IOException;
+
+ /** Send a signal to the Tor process. */
+ public abstract void signal(String signal) throws IOException;
+
+ /** Send a signal to the Tor process to shut it down or halt it.
+ * Does not wait for a response. */
+ public abstract void shutdownTor(String signal) throws IOException;
+
+ /** Tell Tor to replace incoming addresses with those as listed in 'kvLines'.
+ */
+ public abstract Map mapAddresses(Collection kvLines) throws IOException;
+
+ public Map mapAddresses(Map addresses) throws IOException {
+ List kvList = new ArrayList();
+ for (Iterator it = addresses.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry e = (Map.Entry) it.next();
+ kvList.add(e.getKey()+" "+e.getValue());
+ }
+ return mapAddresses(kvList);
+ }
+
+ public String mapAddress(String fromAddr, String toAddr) throws IOException {
+ List lst = new ArrayList();
+ lst.add(fromAddr+" "+toAddr+"\n");
+ Map m = mapAddresses(lst);
+ return (String) m.get(fromAddr);
+ }
+
+ /** Look up the information values listed in keys. */
+ public abstract Map getInfo(Collection keys) throws IOException;
+
+ /** Return the value of the information field 'key' */
+ public String getInfo(String key) throws IOException {
+ List lst = new ArrayList();
+ lst.add(key);
+ Map m = getInfo(lst);
+ return (String) m.get(key);
+ }
+
+ /**
+ * Tell Tor to extend the circuit identified by 'circID' through the
+ * servers named in the list 'path'.
+ */
+ public abstract String extendCircuit(String circID, String path) throws IOException;
+
+ /**
+ * Tell Tor to attach the stream identified by 'streamID' to the circuit
+ * identified by 'circID'.
+ */
+ public abstract void attachStream(String streamID, String circID) throws IOException;
+
+ /** Tell Tor about the server descriptor in 'desc' */
+ public abstract String postDescriptor(String desc) throws IOException;
+
+ /** Tell Tor to change the target of the stream identified by 'streamID'
+ * to 'address'.
+ */
+ public abstract void redirectStream(String streamID, String address) throws IOException;
+
+ /** Tell Tor to close the stream identified by 'streamID'.
+ */
+ public abstract void closeStream(String streamID, byte reason)
+ throws IOException;
+
+ /** Tell Tor to close the circuit identified by 'streamID'.
+ */
+ public abstract void closeCircuit(String circID, boolean ifUnused) throws IOException;
+
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection0.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection0.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection0.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,431 @@
+// $Id: TorControlConnection0.java 9193 2006-12-25 04:37:53Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/** DOCDOC */
+public class TorControlConnection0 extends TorControlConnection
+ implements TorControlCommands
+{
+ protected java.io.DataOutputStream outStream;
+ protected java.io.DataInputStream inStream;
+
+ static class Cmd {
+ public int type;
+ public byte[] body;
+
+ Cmd(int t, byte[] b) { type = t; body = b; }
+ Cmd(int t, int l) { type = t; body = new byte[l]; };
+ }
+
+ /** Create a new TorControlConnection to communicate with Tor over
+ * a given socket. After calling this constructor, it is typical to
+ * call launchThread and authenticate. */
+ public TorControlConnection0(java.net.Socket connection)
+ throws IOException {
+ this(connection.getInputStream(), connection.getOutputStream());
+ }
+
+ /** Create a new TorControlConnection to communicate with Tor over
+ * an arbitrary pair of data streams.
+ */
+ public TorControlConnection0(java.io.InputStream i, java.io.OutputStream o)
+ throws IOException {
+ this.outStream = new java.io.DataOutputStream(o);
+ this.inStream = new java.io.DataInputStream(i);
+ this.waiters = new LinkedList();
+ }
+
+ /** helper: sends a single (unfragmentable) command to Tor */
+ protected final void sendCommand0(int type, byte[] cmd)
+ throws IOException {
+ int length = cmd == null ? 0 : cmd.length;
+ outStream.writeShort((short)length);
+ outStream.writeShort(type);
+ if (cmd != null)
+ outStream.write(cmd);
+ }
+
+ /** helper: sends a single (possibly fragmented) command to Tor */
+ protected void sendCommand(short type, byte[] cmd) throws IOException {
+ synchronized(this.outStream) {
+ if (cmd == null || cmd.length <= 65535) {
+ sendCommand0(type, cmd);
+ return;
+ }
+ int length = cmd.length;
+ outStream.writeShort(65535);
+ outStream.writeShort(CMD_FRAGMENTHEADER);
+ outStream.writeShort(type);
+ outStream.writeInt(length);
+ outStream.write(cmd, 0, 65535);
+ for (int pos=65535; pos < length; pos += 65535) {
+ int flen = length-pos < 65535 ? length-pos : 65535;
+ outStream.writeShort(flen);
+ outStream.writeShort(CMD_FRAGMENT);
+ this.outStream.write(cmd, pos, flen);
+ }
+ }
+ }
+
+ /** helper: read a possibly fragmented command from Tor */
+ protected final Cmd readCommand0() throws IOException {
+ int len = this.inStream.readUnsignedShort();
+ int cmd = this.inStream.readUnsignedShort();
+ byte[] result = new byte[len];
+ this.inStream.readFully(result);
+ return new Cmd(cmd, result);
+ }
+
+ /** Read a command from Tor, defragmenting as necessary */
+ protected Cmd readCommand() throws IOException {
+ synchronized (inStream) {
+ Cmd c = readCommand0();
+ if (c.type != CMD_FRAGMENT && c.type != CMD_FRAGMENTHEADER)
+ return c;
+
+ if (c.type == CMD_FRAGMENT)
+ throw new TorControlSyntaxError("Fragment without header");
+
+ int realType = Bytes.getU16(c.body, 0);
+ int realLen = Bytes.getU32(c.body, 2);
+
+ Cmd out = new Cmd(realType, realLen);
+ System.arraycopy(c.body, 6, out.body, 0, c.body.length-6);
+ int pos = c.body.length-6;
+ while (pos < realLen) {
+ c = readCommand0();
+ if (c.type != CMD_FRAGMENT)
+ throw new TorControlSyntaxError("Incomplete fragmented message");
+ System.arraycopy(c.body, 0, out.body, pos, c.body.length);
+ pos += c.body.length;
+ }
+ return out;
+ }
+ }
+
+ /** helper: implement the main background loop. */
+ protected void react() throws IOException {
+ while (true) {
+ Cmd c = readCommand();
+ if (c.type == CMD_EVENT)
+ handleEvent(c);
+ else {
+ Waiter w;
+ synchronized (waiters) {
+ w = (Waiter) waiters.removeFirst();
+ }
+ w.setResponse(c);
+ }
+ }
+ }
+
+ /** helper: Send a command and wait for the next reponse type command
+ * to be received (in order) */
+ protected synchronized Cmd _sendAndWaitForResponse(short type, byte[] cmd)
+ throws IOException {
+ checkThread();
+ Waiter w = new Waiter();
+ synchronized (waiters) {
+ sendCommand(type, cmd);
+ waiters.addLast(w);
+ }
+ return (Cmd) w.getResponse();
+ }
+
+ /** Send a message to Tor, and wait for a respose.
+ *
+ * @throw TorControlError if Tor tells us about an error
+ * @throw TorControlSyntaxError if the response type wasn't exType1...4
+ **/
+ protected Cmd sendAndWaitForResponse(short type, byte[] cmd,
+ short exType1, short exType2, short exType3, short exType4)
+ throws IOException {
+
+ Cmd c = _sendAndWaitForResponse(type, cmd);
+ if (c.type == CMD_ERROR)
+ throw new TorControlError(Bytes.getU16(c.body, 0),
+ Bytes.getNulTerminatedStr(c.body, 2));
+ if (c.type == exType1 || c.type == exType2 || c.type == exType3 ||
+ c.type == exType4)
+ return c;
+
+ throw new TorControlSyntaxError("Unexpected reply type: "+c.type);
+ }
+
+ protected Cmd sendAndWaitForResponse(short type, byte[] cmd)
+ throws IOException {
+ return sendAndWaitForResponse(type, cmd, CMD_DONE, CMD_DONE, CMD_DONE, CMD_DONE);
+ }
+
+ protected Cmd sendAndWaitForResponse(short type, byte[] cmd, short exType1)
+ throws IOException {
+ return sendAndWaitForResponse(type, cmd, exType1, exType1, exType1,
+ exType1);
+ }
+
+ protected Cmd sendAndWaitForResponse(short type, byte[] cmd,
+ short exType1, short exType2)
+ throws IOException {
+ return sendAndWaitForResponse(type, cmd, exType1, exType2, exType2,
+ exType2);
+ }
+
+ protected Cmd sendAndWaitForResponse(short type, byte[] cmd,
+ short exType1, short exType2, short exType3)
+ throws IOException {
+ return sendAndWaitForResponse(type, cmd, exType1, exType2, exType3,
+ exType3);
+ }
+
+ /** Helper: decode a CMD_EVENT command and dispatch it to our
+ * EventHandler (if any). */
+ protected void handleEvent(Cmd c) {
+ if (handler == null)
+ return;
+ int type = Bytes.getU16(c.body, 0);
+
+ switch (type) {
+ case EVENT_CIRCSTATUS:
+ handler.circuitStatus(CIRC_STATUS_NAMES[c.body[2]],
+ Bytes.getU32S(c.body, 3),
+ Bytes.getNulTerminatedStr(c.body, 7));
+ break;
+ case EVENT_STREAMSTATUS:
+ handler.streamStatus(STREAM_STATUS_NAMES[c.body[2]],
+ Bytes.getU32S(c.body, 3),
+ Bytes.getNulTerminatedStr(c.body, 7));
+ break;
+ case EVENT_ORCONNSTATUS:
+ handler.orConnStatus(OR_CONN_STATUS_NAMES[c.body[2]],
+ Bytes.getNulTerminatedStr(c.body, 3));
+ break;
+ case EVENT_BANDWIDTH:
+ handler.bandwidthUsed(Bytes.getU32(c.body, 2),
+ Bytes.getU32(c.body, 6));
+ break;
+ case EVENT_NEWDESCRIPTOR:
+ List lst = new ArrayList();
+ Bytes.splitStr(lst, c.body, 2, (byte)',');
+ handler.newDescriptors(lst);
+ break;
+ case EVENT_MSG_DEBUG:
+ handler.message("DEBUG", Bytes.getNulTerminatedStr(c.body, 2));
+ break;
+ case EVENT_MSG_INFO:
+ handler.message("INFO", Bytes.getNulTerminatedStr(c.body, 2));
+ break;
+ case EVENT_MSG_NOTICE:
+ handler.message("NOTICE", Bytes.getNulTerminatedStr(c.body, 2));
+ break;
+ case EVENT_MSG_WARN:
+ handler.message("WARN", Bytes.getNulTerminatedStr(c.body, 2));
+ break;
+ case EVENT_MSG_ERROR:
+ handler.message("ERR", Bytes.getNulTerminatedStr(c.body, 2));
+ break;
+ default:
+ throw new TorControlSyntaxError("Unrecognized event type.");
+ }
+ }
+
+ /** Change the values of the configuration options stored in
+ * 'kvList'. (The format is "key value"). */
+ public void setConf(Collection kvList) throws IOException {
+ StringBuffer b = new StringBuffer();
+ for (Iterator it = kvList.iterator(); it.hasNext(); ) {
+ String kv = (String) it.next();
+ b.append(kv).append("\n");
+ }
+ sendAndWaitForResponse(CMD_SETCONF, b.toString().getBytes());
+ }
+
+ public void resetConf(Collection keylist) throws IOException {
+ setConf(keylist);
+ }
+
+ public List getConf(Collection keys) throws IOException {
+ StringBuffer s = new StringBuffer();
+ for (Iterator it = keys.iterator(); it.hasNext(); ) {
+ String key = (String) it.next();
+ s.append(key).append("\n");
+ }
+ Cmd c = sendAndWaitForResponse(CMD_GETCONF, s.toString().getBytes(),
+ CMD_CONFVALUE);
+ List lines = new ArrayList();
+ Bytes.splitStr(lines, c.body, 0, (byte)'\n');
+ List result = new ArrayList();
+ for (Iterator it = lines.iterator(); it.hasNext(); ) {
+ String kv = (String) it.next();
+ int idx = kv.indexOf(' ');
+ result.add(new ConfigEntry(kv.substring(0, idx),
+ kv.substring(idx+1)));
+ }
+ return result;
+ }
+
+ public void setEvents(List events) throws IOException {
+ byte[] ba = new byte[events.size() * 2];
+ int i;
+ Iterator it;
+ for(i=0, it = events.iterator(); it.hasNext(); i += 2) {
+ Object event = it.next();
+ short e = -1;
+ if (event instanceof Number) {
+ e = ((Number)event).shortValue();
+ } else {
+ String s = ((String) event).toUpperCase();
+ for (int j = 0; i < EVENT_NAMES.length; ++i) {
+ if (EVENT_NAMES[j].equals(s)) {
+ e = (short)j;
+ break;
+ }
+ }
+ if (e < 0)
+ throw new TorControlError("Unknown v0 code for event '"+s+"'");
+ }
+ Bytes.setU16(ba, i, e);
+ }
+ sendAndWaitForResponse(CMD_SETEVENTS, ba);
+ System.out.println("OK");
+ }
+
+ public void authenticate(byte[] auth) throws IOException {
+ if (auth == null)
+ auth = new byte[0];
+ sendAndWaitForResponse(CMD_AUTH, auth);
+ }
+
+ public void saveConf() throws IOException {
+ sendAndWaitForResponse(CMD_SAVECONF, new byte[0]);
+ }
+
+ public void signal(String signal) throws IOException {
+ int sig;
+ signal = signal.toUpperCase();
+ if (signal.equals("HUP") || signal.equals("RELOAD"))
+ sig = SIGNAL_HUP;
+ else if (signal.equals("INT") || signal.equals("SHUTDOWN"))
+ sig = SIGNAL_INT;
+ else if (signal.equals("USR1") || signal.equals("DUMP"))
+ sig = SIGNAL_USR1;
+ else if (signal.equals("USR2") || signal.equals("DEBUG"))
+ sig = SIGNAL_USR2;
+ else if (signal.equals("TERM") || signal.equals("HALT"))
+ sig = SIGNAL_TERM;
+ else
+ throw new TorControlError("Unrecognized value for signal()");
+ byte[] ba = { (byte)sig };
+ sendAndWaitForResponse(CMD_SIGNAL, ba);
+ }
+
+ /** Send a signal to the Tor process to shut it down or halt it.
+ * Does not wait for a response. */
+ public void shutdownTor(String signal) throws IOException {
+ throw new RuntimeException("Method is not implemented!");
+ }
+
+ public Map mapAddresses(Collection kvLines) throws IOException {
+ StringBuffer sb = new StringBuffer();
+ for (Iterator it = kvLines.iterator(); it.hasNext(); ) {
+ sb.append((String)it.next()).append("\n");
+ }
+ Cmd c = sendAndWaitForResponse(CMD_MAPADDRESS, sb.toString().getBytes());
+ Map result = new HashMap();
+ List lst = new ArrayList();
+ Bytes.splitStr(lst, c.body, 0, (byte)'\n');
+ for (Iterator it = lst.iterator(); it.hasNext(); ) {
+ String kv = (String) it.next();
+ int idx = kv.indexOf(' ');
+ result.put(kv.substring(0, idx),
+ kv.substring(idx+1));
+ }
+ return result;
+ }
+
+ public Map getInfo(Collection keys) throws IOException {
+ StringBuffer sb = new StringBuffer();
+ for (Iterator it = keys.iterator(); it.hasNext(); ) {
+ sb.append(((String)it.next())+"\n");
+ }
+ Cmd c = sendAndWaitForResponse(CMD_GETINFO, sb.toString().getBytes(),
+ CMD_INFOVALUE);
+ Map m = new HashMap();
+ List lst = new ArrayList();
+ Bytes.splitStr(lst, c.body, 0, (byte)0);
+ if ((lst.size() % 2) != 0)
+ throw new TorControlSyntaxError(
+ "Odd number of substrings from GETINFO");
+ for (Iterator it = lst.iterator(); it.hasNext(); ) {
+ Object k = it.next();
+ Object v = it.next();
+ m.put(k, v);
+ }
+ return m;
+ }
+
+ public String extendCircuit(String circID, String path) throws IOException {
+ byte[] p = path.getBytes();
+ byte[] ba = new byte[p.length+4];
+ Bytes.setU32(ba, 0, (int)Long.parseLong(circID));
+ System.arraycopy(p, 0, ba, 4, p.length);
+ Cmd c = sendAndWaitForResponse(CMD_EXTENDCIRCUIT, ba);
+ return Integer.toString(Bytes.getU32(c.body, 0));
+ }
+
+ public void attachStream(String streamID, String circID)
+ throws IOException {
+ byte[] ba = new byte[8];
+ Bytes.setU32(ba, 0, (int)Long.parseLong(streamID));
+ Bytes.setU32(ba, 4, (int)Long.parseLong(circID));
+ sendAndWaitForResponse(CMD_ATTACHSTREAM, ba);
+ }
+
+ /** Tell Tor about the server descriptor in 'desc' */
+ public String postDescriptor(String desc) throws IOException {
+ return new String(
+ sendAndWaitForResponse(CMD_POSTDESCRIPTOR, desc.getBytes()).body);
+ }
+
+ /** Tell Tor to change the target of the stream identified by 'streamID'
+ * to 'address'.
+ */
+ public void redirectStream(String streamID, String address) throws IOException {
+ byte[] addr = address.getBytes();
+ byte[] ba = new byte[addr.length+4];
+ Bytes.setU32(ba, 0, (int)Long.parseLong(streamID));
+ System.arraycopy(addr, 0, ba, 4, addr.length);
+ sendAndWaitForResponse(CMD_REDIRECTSTREAM, ba);
+ }
+
+ /** Tell Tor to close the stream identified by 'streamID'.
+ */
+ public void closeStream(String streamID, byte reason)
+ throws IOException {
+ byte[] ba = new byte[6];
+ Bytes.setU32(ba, 0, (int)Long.parseLong(streamID));
+ ba[4] = reason;
+ ba[5] = (byte)0;
+ sendAndWaitForResponse(CMD_CLOSESTREAM, ba);
+ }
+
+ /** Tell Tor to close the circuit identified by 'streamID'.
+ */
+ public void closeCircuit(String circID, boolean ifUnused) throws IOException {
+ byte[] ba = new byte[5];
+ Bytes.setU32(ba, 0, (int)Long.parseLong(circID));
+ ba[4] = (byte)(ifUnused? 1 : 0);
+ sendAndWaitForResponse(CMD_CLOSECIRCUIT, ba);
+ }
+
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection0.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection1.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection1.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection1.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,625 @@
+// $Id: TorControlConnection1.java 9193 2006-12-25 04:37:53Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/** Extends the TorControlConnection class to implement Version 1
+* of the TC protocol, as specified in control-spec.txt.
+*/
+public class TorControlConnection1 extends TorControlConnection
+ implements TorControlCommands
+{
+ protected java.io.BufferedReader input;
+ protected java.io.Writer output;
+ protected java.io.PrintWriter debugOutput;
+
+ static class ReplyLine {
+ public String status;
+ public String msg;
+ public String rest;
+
+ ReplyLine(String status, String msg, String rest) {
+ this.status = status; this.msg = msg; this.rest = rest;
+ }
+
+ public String toString() {
+ return status + " " + msg + " " + rest;
+ }
+ }
+
+ /** Create a new TorControlConnection to communicate with Tor over
+ * a given socket. After calling this constructor, it is typical to
+ * call launchThread and authenticate. */
+ public TorControlConnection1(java.net.Socket connection)
+ throws IOException {
+ this(connection.getInputStream(), connection.getOutputStream());
+ }
+
+ /** Create a new TorControlConnection to communicate with Tor over
+ * an arbitrary pair of data streams.
+ */
+ public TorControlConnection1(java.io.InputStream i, java.io.OutputStream o)
+ throws IOException {
+ this(new java.io.InputStreamReader(i),
+ new java.io.OutputStreamWriter(o));
+ }
+
+ public TorControlConnection1(java.io.Reader i, java.io.Writer o)
+ throws IOException {
+ this.output = o;
+ if (i instanceof java.io.BufferedReader)
+ this.input = (java.io.BufferedReader) i;
+ else
+ this.input = new java.io.BufferedReader(i);
+
+ this.waiters = new LinkedList();
+ }
+
+ protected final void writeEscaped(String s) throws IOException {
+ StringTokenizer st = new StringTokenizer(s, "\n");
+ while (st.hasMoreTokens()) {
+ String line = st.nextToken();
+ if (line.startsWith("."))
+ line = "."+line;
+ if (line.endsWith("\r"))
+ line += "\n";
+ else
+ line += "\r\n";
+ if (debugOutput != null)
+ debugOutput.print(">> "+line);
+ output.write(line);
+ }
+ output.write(".\r\n");
+ if (debugOutput != null)
+ debugOutput.print(">> .\n");
+ }
+
+ protected static final String quote(String s) {
+ StringBuffer sb = new StringBuffer("\"");
+ for (int i = 0; i < s.length(); ++i) {
+ char c = s.charAt(i);
+ switch (c)
+ {
+ case '\r':
+ case '\n':
+ case '\\':
+ case '\"':
+ sb.append('\\');
+ }
+ sb.append(c);
+ }
+ sb.append('\"');
+ return sb.toString();
+ }
+
+ protected final ArrayList readReply() throws IOException {
+ ArrayList reply = new ArrayList();
+ char c;
+ do {
+ String line = input.readLine();
+ if (line == null) {
+ // if line is null, the end of the stream has been reached, i.e.
+ // the connection to Tor has been closed!
+ if (reply.isEmpty()) {
+ // nothing received so far, can exit cleanly
+ return reply;
+ } else {
+ // received half of a reply before the connection broke down
+ throw new TorControlSyntaxError("Connection to Tor " +
+ " broke down while receiving reply!");
+ }
+ }
+ if (debugOutput != null)
+ debugOutput.println("<< "+line);
+ if (line.length() < 4)
+ throw new TorControlSyntaxError("Line (\""+line+"\") too short");
+ String status = line.substring(0,3);
+ c = line.charAt(3);
+ String msg = line.substring(4);
+ String rest = null;
+ if (c == '+') {
+ StringBuffer data = new StringBuffer();
+ while (true) {
+ line = input.readLine();
+ if (debugOutput != null)
+ debugOutput.print("<< "+line);
+ if (line.equals("."))
+ break;
+ else if (line.startsWith("."))
+ line = line.substring(1);
+ data.append(line).append('\n');
+ }
+ rest = data.toString();
+ }
+ reply.add(new ReplyLine(status, msg, rest));
+ } while (c != ' ');
+
+ return reply;
+ }
+
+ /** helper: implement the main background loop. */
+ protected void react() throws IOException {
+ while (true) {
+ ArrayList lst = readReply();
+ if (lst.isEmpty()) {
+ // connection has been closed remotely! end the loop!
+ return;
+ }
+ if (((ReplyLine)lst.get(0)).status.startsWith("6"))
+ handleEvent(lst);
+ else {
+ Waiter w;
+ synchronized (waiters) {
+ // waiters may be empty after shutting down Tor...
+ if (waiters.isEmpty()) {
+ System.err.println("Attempt to remove a waiter from the waiter list failed! Event status: " + ((ReplyLine)lst.get(0)).status);
+ break; // TODO is this correct?!
+ }
+ w = (Waiter) waiters.removeFirst();
+ }
+ w.setResponse(lst);
+ }
+ }
+ }
+
+ protected synchronized ArrayList sendAndWaitForResponse(String s,String rest)
+ throws IOException {
+ checkThread();
+ Waiter w = new Waiter();
+ if (debugOutput != null)
+ debugOutput.print(">> "+s);
+ synchronized (waiters) {
+ output.write(s);
+ output.flush();
+ if (rest != null)
+ writeEscaped(rest);
+ waiters.addLast(w);
+ }
+ ArrayList lst = (ArrayList) w.getResponse();
+ for (Iterator i = lst.iterator(); i.hasNext(); ) {
+ ReplyLine c = (ReplyLine) i.next();
+ if (! c.status.startsWith("2"))
+ throw new TorControlError("Error reply: "+c.msg);
+ }
+ return lst;
+ }
+
+ /** Helper: decode a CMD_EVENT command and dispatch it to our
+ * EventHandler (if any). */
+ protected void handleEvent(ArrayList events) {
+ if (handler == null)
+ return;
+
+ for (Iterator i = events.iterator(); i.hasNext(); ) {
+ ReplyLine line = (ReplyLine) i.next();
+ int idx = line.msg.indexOf(' ');
+ // value of idx is -1. check this first!
+ if (idx < 0) {
+ System.err.println("Cannot parse event format: " + events.toString());
+ continue;
+ }
+ String tp = line.msg.substring(0, idx).toUpperCase();
+ String rest = line.msg.substring(idx+1);
+ if (tp.equals("CIRC")) {
+ List lst = Bytes.splitStr(null, rest);
+ handler.circuitStatus((String)lst.get(1),
+ (String)lst.get(0),
+ (String)lst.get(2));
+ } else if (tp.equals("STREAM")) {
+ List lst = Bytes.splitStr(null, rest);
+ handler.streamStatus((String)lst.get(1),
+ (String)lst.get(0),
+ (String)lst.get(3));
+ // XXXX circID.
+ } else if (tp.equals("ORCONN")) {
+ List lst = Bytes.splitStr(null, rest);
+ handler.orConnStatus((String)lst.get(1), (String)lst.get(0));
+ } else if (tp.equals("BW")) {
+ List lst = Bytes.splitStr(null, rest);
+ handler.bandwidthUsed(Integer.parseInt((String)lst.get(0)),
+ Integer.parseInt((String)lst.get(1)));
+ } else if (tp.equals("NEWDESC")) {
+ List lst = Bytes.splitStr(null, rest);
+ handler.newDescriptors(lst);
+ } else if (tp.equals("DEBUG") ||
+ tp.equals("INFO") ||
+ tp.equals("NOTICE") ||
+ tp.equals("WARN") ||
+ tp.equals("ERR")) {
+ handler.message(tp, rest);
+ } else {
+ handler.unrecognized(tp, rest);
+ }
+ }
+ }
+
+ /** Changes the values of the configuration options stored in
+ * <b>kvList</b>. Each list element in <b>kvList</b> is expected to be
+ * String of the format "key value".
+ *
+ * Tor behaves as though it had just read each of the key-value pairs
+ * from its configuration file. Keywords with no corresponding values have
+ * their configuration values reset to their defaults. setConf is
+ * all-or-nothing: if there is an error in any of the configuration settings,
+ * Tor sets none of them.
+ *
+ * When a configuration option takes multiple values, or when multiple
+ * configuration keys form a context-sensitive group (see getConf below), then
+ * setting any of the options in a setConf command is taken to reset all of
+ * the others. For example, if two ORBindAddress values are configured, and a
+ * command arrives containing a single ORBindAddress value, the new
+ * command's value replaces the two old values.
+ *
+ * To remove all settings for a given option entirely (and go back to its
+ * default value), include a String in <b>kvList</b> containing the key and no value.
+ */
+ public void setConf(Collection kvList) throws IOException {
+ if (kvList.size() == 0)
+ return;
+ StringBuffer b = new StringBuffer("SETCONF");
+ for (Iterator it = kvList.iterator(); it.hasNext(); ) {
+ String kv = (String) it.next();
+ int i = kv.indexOf(' ');
+ if (i == -1)
+ b.append(" ").append(kv);
+ b.append(" ").append(kv.substring(0,i)).append("=")
+ .append(quote(kv.substring(i+1)));
+ }
+ b.append("\r\n");
+ sendAndWaitForResponse(b.toString(), null);
+ }
+
+ public void resetConf(Collection keys) throws IOException {
+ if (keys.size() == 0)
+ return;
+ StringBuffer b = new StringBuffer("RESETCONF");
+ for (Iterator it = keys.iterator(); it.hasNext(); ) {
+ String key = (String) it.next();
+ b.append(" ").append(key);
+ }
+ b.append("\r\n");
+ sendAndWaitForResponse(b.toString(), null);
+ }
+
+ /** Sets <b>w</b> as the PrintWriter for debugging output,
+ * which writes out all messages passed between Tor and the controller.
+ * Outgoing messages are preceded by "\>\>" and incoming messages are preceded
+ * by "\<\<"
+ */
+ public void setDebugging(java.io.PrintWriter w) {
+ if (w instanceof java.io.PrintWriter)
+ debugOutput = (java.io.PrintWriter) w;
+ else
+ debugOutput = new java.io.PrintWriter(w, true);
+ }
+
+ /** Sets <b>s</b> as the PrintStream for debugging output,
+ * which writes out all messages passed between Tor and the controller.
+ * Outgoing messages are preceded by "\>\>" and incoming messages are preceded
+ * by "\<\<"
+ */
+ public void setDebugging(java.io.PrintStream s) {
+ debugOutput = new java.io.PrintWriter(s, true);
+ }
+
+ /** Requests the values of the configuration variables listed in <b>keys</b>.
+ * Results are returned as a list of ConfigEntry objects.
+ *
+ * If an option appears multiple times in the configuration, all of its
+ * key-value pairs are returned in order.
+ *
+ * Some options are context-sensitive, and depend on other options with
+ * different keywords. These cannot be fetched directly. Currently there
+ * is only one such option: clients should use the "HiddenServiceOptions"
+ * virtual keyword to get all HiddenServiceDir, HiddenServicePort,
+ * HiddenServiceNodes, and HiddenServiceExcludeNodes option settings.
+ */
+ public List getConf(Collection keys) throws IOException {
+ StringBuffer sb = new StringBuffer("GETCONF");
+ for (Iterator it = keys.iterator(); it.hasNext(); ) {
+ String key = (String) it.next();
+ sb.append(" ").append(key);
+ }
+ sb.append("\r\n");
+ ArrayList lst = sendAndWaitForResponse(sb.toString(), null);
+ ArrayList result = new ArrayList();
+ for (Iterator it = lst.iterator(); it.hasNext(); ) {
+ String kv = ((ReplyLine) it.next()).msg;
+ int idx = kv.indexOf('=');
+ if (idx != -1) {
+ result.add(new ConfigEntry(kv.substring(0, idx),
+ kv.substring(idx+1)));
+ }
+ }
+ return result;
+ }
+
+ /** Request that the server inform the client about interesting events.
+ * Each element of <b>events</b> is one of the following Strings:
+ * ["CIRC" | "STREAM" | "ORCONN" | "BW" | "DEBUG" |
+ * "INFO" | "NOTICE" | "WARN" | "ERR" | "NEWDESC" | "ADDRMAP"] .
+ *
+ * Any events not listed in the <b>events</b> are turned off; thus, calling
+ * setEvents with an empty <b>events</b> argument turns off all event reporting.
+ */
+ public void setEvents(List events) throws IOException {
+ StringBuffer sb = new StringBuffer("SETEVENTS");
+ for (Iterator it = events.iterator(); it.hasNext(); ) {
+ Object event = it.next();
+ if (event instanceof String) {
+ sb.append(" ").append((String)event);
+ } else {
+ int i = ((Number) event).intValue();
+ sb.append(" ").append(EVENT_NAMES[i]);
+ }
+ }
+ sb.append("\r\n");
+ sendAndWaitForResponse(sb.toString(), null);
+ }
+
+
+ /** Authenticates the controller to the Tor server.
+ *
+ * By default, the current Tor implementation trusts all local users, and
+ * the controller can authenticate itself by calling authenticate(new byte[0]).
+ *
+ * If the 'CookieAuthentication' option is true, Tor writes a "magic cookie"
+ * file named "control_auth_cookie" into its data directory. To authenticate,
+ * the controller must send the contents of this file in <b>auth</b>.
+ *
+ * If the 'HashedControlPassword' option is set, <b>auth</b> must contain the salted
+ * hash of a secret password. The salted hash is computed according to the
+ * S2K algorithm in RFC 2440 (OpenPGP), and prefixed with the s2k specifier.
+ * This is then encoded in hexadecimal, prefixed by the indicator sequence
+ * "16:".
+ *
+ * You can generate the salt of a password by calling
+ * 'tor --hash-password <password>'
+ * or by using the provided PasswordDigest class.
+ * To authenticate under this scheme, the controller sends Tor the original
+ * secret that was used to generate the password.
+ */
+ public void authenticate(byte[] auth) throws IOException {
+ String cmd = "AUTHENTICATE " + Bytes.hex(auth) + "\r\n";
+ sendAndWaitForResponse(cmd, null);
+ }
+
+ /** Instructs the server to write out its configuration options into its torrc.
+ */
+ public void saveConf() throws IOException {
+ sendAndWaitForResponse("SAVECONF\r\n", null);
+ }
+
+ /** Sends a signal from the controller to the Tor server.
+ * <b>signal</b> is one of the following Strings:
+ * <ul>
+ * <li>"RELOAD" or "HUP" : Reload config items, refetch directory</li>
+ * <li>"SHUTDOWN" or "INT" : Controlled shutdown: if server is an OP, exit immediately.
+ * If it's an OR, close listeners and exit after 30 seconds</li>
+ * <li>"DUMP" or "USR1" : Dump stats: log information about open connections and circuits</li>
+ * <li>"DEBUG" or "USR2" : Debug: switch all open logs to loglevel debug</li>
+ * <li>"HALT" or "TERM" : Immediate shutdown: clean up and exit now</li>
+ * </ul>
+ */
+ public void signal(String signal) throws IOException {
+ String cmd = "SIGNAL " + signal + "\r\n";
+ sendAndWaitForResponse(cmd, null);
+ }
+
+ /** Send a signal to the Tor process to shut it down or halt it.
+ * Does not wait for a response. */
+ public void shutdownTor(String signal) throws IOException {
+ String s = "SIGNAL " + signal + "\r\n";
+ if (debugOutput != null)
+ debugOutput.print(">> "+s);
+ synchronized (waiters) {
+ output.write(s);
+ output.flush();
+ }
+ }
+
+ /** Tells the Tor server that future SOCKS requests for connections to a set of original
+ * addresses should be replaced with connections to the specified replacement
+ * addresses. Each element of <b>kvLines</b> is a String of the form
+ * "old-address new-address". This function returns the new address mapping.
+ *
+ * The client may decline to provide a body for the original address, and
+ * instead send a special null address ("0.0.0.0" for IPv4, "::0" for IPv6, or
+ * "." for hostname), signifying that the server should choose the original
+ * address itself, and return that address in the reply. The server
+ * should ensure that it returns an element of address space that is unlikely
+ * to be in actual use. If there is already an address mapped to the
+ * destination address, the server may reuse that mapping.
+ *
+ * If the original address is already mapped to a different address, the old
+ * mapping is removed. If the original address and the destination address
+ * are the same, the server removes any mapping in place for the original
+ * address.
+ *
+ * Mappings set by the controller last until the Tor process exits:
+ * they never expire. If the controller wants the mapping to last only
+ * a certain time, then it must explicitly un-map the address when that
+ * time has elapsed.
+ */
+ public Map mapAddresses(Collection kvLines) throws IOException {
+ StringBuffer sb = new StringBuffer("MAPADDRESS");
+ for (Iterator it = kvLines.iterator(); it.hasNext(); ) {
+ String kv = (String) it.next();
+ int i = kv.indexOf(' ');
+ sb.append(" ").append(kv.substring(0,i)).append("=")
+ .append(quote(kv.substring(i+1)));
+ }
+ sb.append("\r\n");
+ ArrayList lst = sendAndWaitForResponse(sb.toString(), null);
+ Map result = new HashMap();
+ for (Iterator it = lst.iterator(); it.hasNext(); ) {
+ String kv = ((ReplyLine) it.next()).msg;
+ int idx = kv.indexOf('=');
+ result.put(kv.substring(0, idx),
+ kv.substring(idx+1));
+ }
+ return result;
+ }
+
+ /** Queries the Tor server for keyed values that are not stored in the torrc
+ * configuration file. Returns a map of keys to values.
+ *
+ * Recognized keys include:
+ * <ul>
+ * <li>"version" : The version of the server's software, including the name
+ * of the software. (example: "Tor 0.0.9.4")</li>
+ * <li>"desc/id/<OR identity>" or "desc/name/<OR nickname>" : the latest server
+ * descriptor for a given OR, NUL-terminated. If no such OR is known, the
+ * corresponding value is an empty string.</li>
+ * <li>"network-status" : a space-separated list of all known OR identities.
+ * This is in the same format as the router-status line in directories;
+ * see tor-spec.txt for details.</li>
+ * <li>"addr-mappings/all"</li>
+ * <li>"addr-mappings/config"</li>
+ * <li>"addr-mappings/cache"</li>
+ * <li>"addr-mappings/control" : a space-separated list of address mappings, each
+ * in the form of "from-address=to-address". The 'config' key
+ * returns those address mappings set in the configuration; the 'cache'
+ * key returns the mappings in the client-side DNS cache; the 'control'
+ * key returns the mappings set via the control interface; the 'all'
+ * target returns the mappings set through any mechanism.</li>
+ * <li>"circuit-status" : A series of lines as for a circuit status event. Each line is of the form:
+ * "CircuitID CircStatus Path"</li>
+ * <li>"stream-status" : A series of lines as for a stream status event. Each is of the form:
+ * "StreamID StreamStatus CircID Target"</li>
+ * <li>"orconn-status" : A series of lines as for an OR connection status event. Each is of the
+ * form: "ServerID ORStatus"</li>
+ * </ul>
+ */
+ public Map getInfo(Collection keys) throws IOException {
+ StringBuffer sb = new StringBuffer("GETINFO");
+ for (Iterator it = keys.iterator(); it.hasNext(); ) {
+ sb.append(" ").append((String)it.next());
+ }
+ sb.append("\r\n");
+ ArrayList lst = sendAndWaitForResponse(sb.toString(), null);
+ Map m = new HashMap();
+ for (Iterator it = lst.iterator(); it.hasNext(); ) {
+ ReplyLine line = (ReplyLine) it.next();
+ int idx = line.msg.indexOf('=');
+ if (idx<0)
+ break;
+ String k = line.msg.substring(0,idx);
+ Object v;
+ if (line.rest != null) {
+ v = line.rest;
+ } else {
+ v = line.msg.substring(idx+1);
+ }
+ m.put(k, v);
+ }
+ return m;
+ }
+
+ /** An extendCircuit request takes one of two forms: either the <b>circID</b> is zero, in
+ * which case it is a request for the server to build a new circuit according
+ * to the specified path, or the <b>circID</b> is nonzero, in which case it is a
+ * request for the server to extend an existing circuit with that ID according
+ * to the specified <b>path</b>.
+ *
+ * If successful, returns the Circuit ID of the (maybe newly created) circuit.
+ */
+ public String extendCircuit(String circID, String path) throws IOException {
+ ArrayList lst = sendAndWaitForResponse(
+ "EXTENDCIRCUIT "+circID+" "+path+"\r\n", null);
+ return ((ReplyLine)lst.get(0)).msg;
+ }
+
+ /** Informs the Tor server that the stream specified by <b>streamID</b> should be
+ * associated with the circuit specified by <b>circID</b>.
+ *
+ * Each stream may be associated with
+ * at most one circuit, and multiple streams may share the same circuit.
+ * Streams can only be attached to completed circuits (that is, circuits that
+ * have sent a circuit status "BUILT" event or are listed as built in a
+ * getInfo circuit-status request).
+ *
+ * If <b>circID</b> is 0, responsibility for attaching the given stream is
+ * returned to Tor.
+ *
+ * By default, Tor automatically attaches streams to
+ * circuits itself, unless the configuration variable
+ * "__LeaveStreamsUnattached" is set to "1". Attempting to attach streams
+ * via TC when "__LeaveStreamsUnattached" is false may cause a race between
+ * Tor and the controller, as both attempt to attach streams to circuits.
+ */
+ public void attachStream(String streamID, String circID)
+ throws IOException {
+ sendAndWaitForResponse("ATTACHSTREAM "+streamID+" "+circID+"\r\n", null);
+ }
+
+ /** Tells Tor about the server descriptor in <b>desc</b>.
+ *
+ * The descriptor, when parsed, must contain a number of well-specified
+ * fields, including fields for its nickname and identity.
+ */
+ // More documentation here on format of desc?
+ // No need for return value? control-spec.txt says reply is merely "250 OK" on success...
+ public String postDescriptor(String desc) throws IOException {
+ ArrayList lst = sendAndWaitForResponse("+POSTDESCRIPTOR\r\n", desc);
+ return ((ReplyLine)lst.get(0)).msg;
+ }
+
+ /** Tells Tor to change the exit address of the stream identified by <b>streamID</b>
+ * to <b>address</b>. No remapping is performed on the new provided address.
+ *
+ * To be sure that the modified address will be used, this event must be sent
+ * after a new stream event is received, and before attaching this stream to
+ * a circuit.
+ */
+ public void redirectStream(String streamID, String address) throws IOException {
+ sendAndWaitForResponse("REDIRECTSTREAM "+streamID+" "+address+"\r\n",
+ null);
+ }
+
+ /** Tells Tor to close the stream identified by <b>streamID</b>.
+ * <b>reason</b> should be one of the Tor RELAY_END reasons given in tor-spec.txt, as a decimal:
+ * <ul>
+ * <li>1 -- REASON_MISC (catch-all for unlisted reasons)</li>
+ * <li>2 -- REASON_RESOLVEFAILED (couldn't look up hostname)</li>
+ * <li>3 -- REASON_CONNECTREFUSED (remote host refused connection)</li>
+ * <li>4 -- REASON_EXITPOLICY (OR refuses to connect to host or port)</li>
+ * <li>5 -- REASON_DESTROY (Circuit is being destroyed)</li>
+ * <li>6 -- REASON_DONE (Anonymized TCP connection was closed)</li>
+ * <li>7 -- REASON_TIMEOUT (Connection timed out, or OR timed out while connecting)</li>
+ * <li>8 -- (unallocated)</li>
+ * <li>9 -- REASON_HIBERNATING (OR is temporarily hibernating)</li>
+ * <li>10 -- REASON_INTERNAL (Internal error at the OR)</li>
+ * <li>11 -- REASON_RESOURCELIMIT (OR has no resources to fulfill request)</li>
+ * <li>12 -- REASON_CONNRESET (Connection was unexpectedly reset)</li>
+ * <li>13 -- REASON_TORPROTOCOL (Sent when closing connection because of Tor protocol violations)</li>
+ * </ul>
+ *
+ * Tor may hold the stream open for a while to flush any data that is pending.
+ */
+ public void closeStream(String streamID, byte reason)
+ throws IOException {
+ sendAndWaitForResponse("CLOSESTREAM "+streamID+" "+reason+"\r\n",null);
+ }
+
+ /** Tells Tor to close the circuit identified by <b>circID</b>.
+ * If <b>ifUnused</b> is true, do not close the circuit unless it is unused.
+ */
+ public void closeCircuit(String circID, boolean ifUnused) throws IOException {
+ sendAndWaitForResponse("CLOSECIRCUIT "+circID+
+ (ifUnused?" IFUNUSED":"")+"\r\n", null);
+ }
+
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection1.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlError.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlError.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlError.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,31 @@
+// $Id: TorControlError.java 6831 2005-06-24 18:03:27Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+/**
+ * An exception raised when Tor tells us about an error.
+ */
+public class TorControlError extends RuntimeException {
+ int errorType;
+ public TorControlError(int type, String s) {
+ super(s);
+ errorType = type;
+ }
+ public TorControlError(String s) {
+ this(-1, s);
+ }
+ public int getErrorType() {
+ return errorType;
+ }
+ public String getErrorMsg() {
+ try {
+ if (errorType == -1)
+ return null;
+ return TorControlCommands.ERROR_MSGS[errorType];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ return "Unrecongized error #"+errorType;
+ }
+ }
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlError.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlSyntaxError.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlSyntaxError.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlSyntaxError.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,9 @@
+package net.freehaven.tor.control;
+
+/**
+ * An exception raised when Tor behaves in an unexpected way.
+ */
+public class TorControlSyntaxError extends RuntimeException {
+ public TorControlSyntaxError(String s) { super(s); }
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlSyntaxError.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/examples/.cvsignore
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/examples/.cvsignore 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/examples/.cvsignore 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1 @@
+*.class
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/examples/.cvsignore
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/examples/DebuggingEventHandler.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/examples/DebuggingEventHandler.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/examples/DebuggingEventHandler.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,45 @@
+// $Id: DebuggingEventHandler.java 6831 2005-06-24 18:03:27Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control.examples;
+
+import java.io.PrintWriter;
+import java.util.Iterator;
+import net.freehaven.tor.control.EventHandler;
+
+public class DebuggingEventHandler implements EventHandler {
+
+ protected PrintWriter out;
+
+ public DebuggingEventHandler(PrintWriter p) {
+ out = p;
+ }
+
+ public void circuitStatus(String status, String circID, String path) {
+ out.println("Circuit "+circID+" is now "+status+" (path="+path+")");
+ }
+ public void streamStatus(String status, String streamID, String target) {
+ out.println("Stream "+streamID+" is now "+status+" (target="+target+")");
+ }
+ public void orConnStatus(String status, String orName) {
+ out.println("OR connection to "+orName+" is now "+status);
+ }
+ public void bandwidthUsed(long read, long written) {
+ out.println("Bandwidth usage: "+read+" bytes read; "+
+ written+" bytes written.");
+ }
+ public void newDescriptors(java.util.List orList) {
+ out.println("New descriptors for routers:");
+ for (Iterator i = orList.iterator(); i.hasNext(); )
+ out.println(" "+i.next());
+ }
+ public void message(String type, String msg) {
+ out.println("["+type+"] "+msg.trim());
+ }
+
+ public void unrecognized(String type, String msg) {
+ out.println("unrecognized event ["+type+"] "+msg.trim());
+ }
+
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/examples/DebuggingEventHandler.java
___________________________________________________________________
Name: svn:executable
+ *
Added: puppetor/trunk/src/net/freehaven/tor/control/examples/Main.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/examples/Main.java 2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/examples/Main.java 2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,150 @@
+// $Id: Main.java 9193 2006-12-25 04:37:53Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control.examples;
+
+import net.freehaven.tor.control.*;
+import java.io.PrintWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Iterator;
+
+public class Main implements TorControlCommands {
+
+ public static void main(String args[]) {
+ if (args.length < 1) {
+ System.err.println("No command given.");
+ return;
+ }
+ try {
+ if (args[0].equals("set-config")) {
+ setConfig(args);
+ } else if (args[0].equals("get-config")) {
+ getConfig(args);
+ } else if (args[0].equals("get-info")) {
+ getInfo(args);
+ } else if (args[0].equals("listen")) {
+ listenForEvents(args);
+ } else if (args[0].equals("signal")) {
+ signal(args);
+ } else if (args[0].equals("auth")) {
+ authDemo(args);
+ } else {
+ System.err.println("Unrecognized command: "+args[0]);
+ }
+ } catch (java.io.EOFException ex) {
+ System.out.println("Control socket closed by Tor.");
+ } catch (IOException ex) {
+ System.err.println("IO exception when talking to Tor process: "+
+ ex);
+ ex.printStackTrace(System.err);
+ } catch (TorControlError ex) {
+ System.err.println("Error from Tor process: "+
+ ex+" ["+ex.getErrorMsg()+"]");
+ }
+ }
+
+ private static TorControlConnection getConnection(String[] args,
+ boolean daemon)
+ throws IOException {
+ TorControlConnection conn = TorControlConnection.getConnection(
+ new java.net.Socket("127.0.0.1", 9100));
+ //if (conn instanceof TorControlConnection1) {
+ // System.err.println("Debugging");
+ // ((TorControlConnection1)conn).setDebugging(System.err);
+ //}
+ Thread th = conn.launchThread(daemon);
+ conn.authenticate(new byte[0]);
+ return conn;
+ }
+
+ private static TorControlConnection getConnection(String[] args)
+ throws IOException {
+ return getConnection(args, true);
+ }
+
+ public static void setConfig(String[] args) throws IOException {
+ // Usage: "set-config [-save] key value key value key value"
+ TorControlConnection conn = getConnection(args);
+ ArrayList lst = new ArrayList();
+ int i = 1;
+ boolean save = false;
+ if (args[i].equals("-save")) {
+ save = true;
+ ++i;
+ }
+ for (; i < args.length; i +=2) {
+ lst.add(args[i]+" "+args[i+1]);
+ }
+ conn.setConf(lst);
+ if (save) {
+ conn.saveConf();
+ }
+ }
+
+ public static void getConfig(String[] args) throws IOException {
+ // Usage: get-config key key key
+ TorControlConnection conn = getConnection(args);
+ List lst = conn.getConf(Arrays.asList(args).subList(1,args.length));
+ for (Iterator i = lst.iterator(); i.hasNext(); ) {
+ ConfigEntry e = (ConfigEntry) i.next();
+ System.out.println("KEY: "+e.key);
+ System.out.println("VAL: "+e.value);
+ }
+ }
+
+ public static void getInfo(String[] args) throws IOException {
+ TorControlConnection conn = getConnection(args);
+ Map m = conn.getInfo(Arrays.asList(args).subList(1,args.length));
+ for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
+ Map.Entry e = (Map.Entry) i.next();
+ System.out.println("KEY: "+e.getKey());
+ System.out.println("VAL: "+e.getValue());
+ }
+ }
+
+ public static void listenForEvents(String[] args) throws IOException {
+ // Usage: listen [circ|stream|orconn|bw|newdesc|info|notice|warn|error]*
+ TorControlConnection conn = getConnection(args, false);
+ ArrayList lst = new ArrayList();
+ for (int i = 1; i < args.length; ++i) {
+ lst.add(args[i]);
+ }
+ conn.setEventHandler(
+ new DebuggingEventHandler(new PrintWriter(System.out, true)));
+ conn.setEvents(lst);
+ }
+
+ public static void signal(String[] args) throws IOException {
+ // Usage signal [reload|shutdown|dump|debug|halt]
+ TorControlConnection conn = getConnection(args, false);
+ // distinguish shutdown signal from other signals
+ if ("SHUTDOWN".equalsIgnoreCase(args[1])
+ || "HALT".equalsIgnoreCase(args[1])) {
+ conn.shutdownTor(args[1].toUpperCase());
+ } else {
+ conn.signal(args[1].toUpperCase());
+ }
+ }
+
+ public static void authDemo(String[] args) throws IOException {
+
+ PasswordDigest pwd = PasswordDigest.generateDigest();
+ java.net.Socket s = new java.net.Socket("127.0.0.1", 9100);
+ TorControlConnection conn = TorControlConnection.getConnection(s);
+ conn.launchThread(true);
+ conn.authenticate(new byte[0]);
+
+ conn.setConf("HashedControlPassword", pwd.getHashedPassword());
+
+ conn = TorControlConnection.getConnection(
+ new java.net.Socket("127.0.0.1", 9100));
+ conn.launchThread(true);
+ conn.authenticate(pwd.getSecret());
+ }
+
+}
+
Property changes on: puppetor/trunk/src/net/freehaven/tor/control/examples/Main.java
___________________________________________________________________
Name: svn:executable
+ *