[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r10687: finished documentation for impl classes, unified rest of doc (in puppetor/trunk: doc src/de/uniba/wiai/lspi/puppetor src/de/uniba/wiai/lspi/puppetor/examples src/de/uniba/wiai/lspi/puppetor/impl)
Author: kloesing
Date: 2007-06-27 18:09:47 -0400 (Wed, 27 Jun 2007)
New Revision: 10687
Modified:
puppetor/trunk/doc/howto.aux
puppetor/trunk/doc/howto.pdf
puppetor/trunk/doc/howto.tex
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/EventManager.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/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/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
Log:
finished documentation for impl classes, unified rest of documentation, fine-tuned howto
Modified: puppetor/trunk/doc/howto.aux
===================================================================
--- puppetor/trunk/doc/howto.aux 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/doc/howto.aux 2007-06-27 22:09:47 UTC (rev 10687)
@@ -1,8 +1,9 @@
-\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}}
+\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}{8}}
+\@writefile{toc}{\contentsline {section}{\numberline {7}Architecture}{10}}
+\@writefile{toc}{\contentsline {section}{\numberline {8}Known issues}{10}}
Modified: puppetor/trunk/doc/howto.pdf
===================================================================
--- puppetor/trunk/doc/howto.pdf 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/doc/howto.pdf 2007-06-27 22:09:47 UTC (rev 10687)
@@ -1,1423 +1,1260 @@
-%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]
+%PDF-1.4
+3 0 obj <<
+/Length 2577
+/Filter /FlateDecode
>>
+stream
+xڕ�����`��`�Oy.ljuH9e�+u^���.���e>�6���������^�UX�� ����y ��)ʁsI?9꧈Q���{YX�@J��8O�8��7��x�+�Q�>/���ٕ���q��g��C5-`U�@
+���&#����WV�"��<5�ۙ��'���Q�Z6�%&9���J*����i�K��$��-
+�����#���c-��)"B#��s���9�.ƅ_2��[��R4zs�4g���Y��'�y\mI��C\цy�+�?cށ�E�h�y�J�褅��Ӓ�(���č��bH�Yj#��3V��M�wt���}�K���B���;2Y�Q}��L'����+{����Ffev{L��(έ������l��C6 �8�rq
+J��!���Ē
+ሴ��#K`\@q��ṟ���(�:L Ã�ѽo۵@�$ �ںU�<���ce
+}w�*���6�xUVsaEgu�8���ی<J
+�a���Cc�OD�u�D�e;`ֶ��f���9�:p7m{]�~4۸Q8q�1�B���*��#뱰��� ��V 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]
+2 0 obj <<
+/Type /Page
+/Contents 3 0 R
+/Resources 1 0 R
+/MediaBox [0 0 595.2756 841.8898]
+/Parent 25 0 R
+>> endobj
+1 0 obj <<
+/Font << /F15 6 0 R /F16 9 0 R /F26 12 0 R /F8 15 0 R /F7 18 0 R /F18 21 0 R /F17 24 0 R >>
+/ProcSet [ /PDF /Text ]
+>> endobj
+28 0 obj <<
+/Length 2399
+/Filter /FlateDecode
>>
+stream
+x�YI�ۺ���h���]�w�]��R�$��$d(���_����"
+L�٦�hոI����q�ʶf��(�j��R����M+[T�������-�Հ���m����0,�Ab���c��7�9��=l����3�i0���fW�Pu��Ia����9^�&���ݒI����~PQ��߰{Р�����^+b]��ffr��k�:=�`.;/Z���j���dOlY�8YzXZ�����V����(�����nq!J�����0Sy!q���7�ԝ����|�tY�]&ґ켂$i�e��{d4�؍�b'a��K��<��'�R���c�����V�~(����=.jˠ&�+2������4�`hI9<K:���������O��ea�� "aë�J�����G�j���������qN�g��`�!���w����O��q������=賊FR*��WGHj�i���J�Z�-�YK�؎=8��g�����l?bx��v9p��L�U<!�F��>r�Qe�$)p�a�1x���sM����Cc.<X>3��#�Bٷ�:��@��2%R8D�p���ң8c�����4J�b0�G�D9��Y����;���8�]`�,�$�� L_ϕ��7�E-��,tOUN�=Z+ �p��2P��qѭ}G`N���������P$������B�7h�����#b@�:Ԟ]�i�r^R�-�`y_�P�6ReŞ�i��6a�de�p�_I�ALD�/UA<��F���gP�ѫ����&VW��E��WY�����I�ω�R"}�iٌ�o������I������՞���<sK��F1��g!���Y��(�����+.�L#�[K�5��y7�5��U�I<x�l�Q�+I�MciÕH��o�daI�V1�2y�����y���v+i��`N5�P֘�^< ���^���F?���o�ȹ鰤~-4��q�Ta���<�~F*�+�1��<��s �Q���Hᮦs�,JC���I�/��gn*�a�����hە��<N��i��A�����>�,��K4�!�\�-�u��+��J���d�OO �K�5a�v�����9\������
+h�{!��a��|�� 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]
+27 0 obj <<
+/Type /Page
+/Contents 28 0 R
+/Resources 26 0 R
+/MediaBox [0 0 595.2756 841.8898]
+/Parent 25 0 R
+>> endobj
+26 0 obj <<
+/Font << /F8 15 0 R /F26 12 0 R /F27 31 0 R >>
+/ProcSet [ /PDF /Text ]
+>> endobj
+34 0 obj <<
+/Length 2593
+/Filter /FlateDecode
>>
+stream
+xڕYK�����2�DJ��,���f�H/�,��eɑ����v�,ɪ�^�n�����^/��&��Ee�ҩZ<��G;�w���?ٺ6�?���]�����q�����x�R*,����O��UG&6�}�����h�m�k7e�����h^��1CHۣ����S@W��YkT��X��.�D����� a��8N����\���E�[T��$L���e�y�g��г��PG��Wga뜮���+��U��{���St+3�<�P.u��ƎNK�~X�1ف's�wג�Cw�W����A�p+�<���2�oXam�G)�e%�+��ﯼ]>�,鳗RBo��,+��a�H�2�I�F�UF�K.Ne�����1L��f0�5�F�a=�>���6�[�J`?Cj~�w4S�DN),cEp�T���-97Qo����Q�qp�8XG��<@��Zx]� �ݎ�mO�6�@1-/Q���y��I`��Q��Cv���2���V�ڒ����2� )]͜�*+x��18?���rZ�����,a�%������ρ��(��ID˞�u�x?>��C�2JE����xꙸ�E.9~�������p��'��оz�]�I�]�x����W�B`�+�n%jK!���7\s��
+�R���-��`!�����PH��p�8E�.���dń�\t2���'�x����"�!�%���t�{��7O�s!0u�<v���F%H
+v#�q�S�����,yv�X[�*)������ޒr�Kg�75�SPG�B�3��+Uٲ�mi�(��� G�0�q�����5ܧK��ʲ,�cfVhp����2=,I������Z�^-�O��Vv\�Fgcum-�El�V�PaVx��8)\գ��ݹ�f֨��l��I�O�)xZ��+����po+y|4z˝���+���y��b���Go�pY��A3�-����D��G���A;���x7��i%9l&�O�]���5\�x�Q�*���'��d�����_n"��j�Q��B/7
+z�$ױ�in��ˋ\y���П�_�a�eΓ0_Gx6i6�&U����P��:̕�,f��`\Sɘ�&.
+pW�N�)�4�9ao���:p����TP�g��C�������u�t���t������+��ndstream
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]
+33 0 obj <<
+/Type /Page
+/Contents 34 0 R
+/Resources 32 0 R
+/MediaBox [0 0 595.2756 841.8898]
+/Parent 25 0 R
+>> endobj
+32 0 obj <<
+/Font << /F27 31 0 R /F26 12 0 R /F8 15 0 R /F14 37 0 R /F7 18 0 R /F18 21 0 R /F17 24 0 R >>
+/ProcSet [ /PDF /Text ]
+>> endobj
+40 0 obj <<
+/Length 2497
+/Filter /FlateDecode
>>
-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����f�j�%-��틛|���&�u'�7��QE�>����|2��e�Z(B��|�y��=u����5���U�e�ϡ���'T�ɲ�ﳉΕ�>[��J��Q|����}6V�s�+���ޟ'�jJܞ�v�Y�B�������;��Ī�Mw�UJ�d{]��L~*rB�N(�R���c�fYt����σ��G:qܿ�l��d�C��^���x���2���-������
-�y<�h;��������Y�>)�S7Y�aC��l��`đ�'������A�k?J��wų��$>�����Y���5�tsp���=(l����LX4�-4I��(�/ģg6!Q�}���yt�O|x<E6ꨋ\=.a''�D��dj����>�uÂ-<�=�yCw�WvnXKV�d/��yڎ�,��e��x�_ܼ��85����b-vb�Z�Z)�C����7�q}�ލ!Qh���e,�ZlG�N�)�$�Bx�-�ԅ��`X��@"./��"���/����ۈ@0Gno).�J �]�#�@�_�J����%���02�@��n@b��ih�5���.�8���˹����������I�]IC�m�a�@Y���k�^�lw�1��VP�f�Ӓ�/ɤ}�u����!������}rs�I���$�ݹ1ԭbue홆!MZ�-�W/��k#�t����%���O��>qPL#�O�P- �1�ndstream
+xڭ�����1�l����Eoj+�DB$V @��i��{�����������=��I"�P����06�ȏ�T��̗�{e���Oe����g�
+<�,�L���}�&�"1:�̔��b�LgZ���W���+omW��(�����d�*���EГ� ��M�����i����)����!��x��HK�운np+*�Gfc�K�o�WT�U����!~�Ǝ�<ǭ8����
+H�-��T$��I-���W?_3�,H����7����w��-����b��ȃsNж&����D����p��Z�XfϬ?�};�|_=��H��i~I��U0��+�'�2�o3gr(�u Um��xv�5�`��������M��[R�<�R�8����4�i=����>c�E/B<ͩ� K�]�·v��Ax\�C�� �&%w��)6�hZ��1x�������(8����V1jQm�E��Pi������+�1���BJ-��/����z1���� I%�21p2q������y{����3���>��V�a�dg@˜+*�4�=6�W[�nGXl���+�qoy�M����OȊ��l���z��������B���U����b�QT*�����\d��|����_�]��
+a$|?����PHb��U����t"p'�����&F�=L��9�P�bզ���.��j�R�?N
+�U�/8�xjw��֥0���+��\r�=Fɰ9r�
+�k�����n����d�X>A�D���R,��� U��S���p�<Nj���F~�6��f��v`�
+�;/y��9ȩ��V��ZYs͒���|r�i�|��T�a
+�k[��v�V,����S����U���C��M"F$�P�P`T>��(�1�(H[߄5�Զ�����lb5/u�p��[�ɛ7.���2�����l��+�J�c�Z�C���=_4�"�cX�W(��$��+M�φ���3hmb�"L=�c{[���f��A+l������w\���}`c`�n�6!�� �\�rTw��nH�p��}�&m�ț➷Ȝ�~���:>�@'=�[�-�Ge�8]�.dK�%뎥%cr�q���������6�5��9�(�$`��5����ȶe��d�җ��u$�����grNl��
+ �m��(����i^<^@�����R��t��tD�y�Ŋ�)�[HȒO����7LAF ~�c��\^k����r�t�n�<b���%v$��������|�c56z�I�w?�}[)@�o�� !%r&Bccb��?$���-�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
+39 0 obj <<
+/Type /Page
+/Contents 40 0 R
+/Resources 38 0 R
+/MediaBox [0 0 595.2756 841.8898]
+/Parent 25 0 R
+>> endobj
+38 0 obj <<
+/Font << /F27 31 0 R /F8 15 0 R >>
+/ProcSet [ /PDF /Text ]
+>> endobj
+43 0 obj <<
+/Length 2117
+/Filter /FlateDecode
>>
-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����QڌD�s :��d�c`���Y�ίO�(���Q��X������|��B��;xy��ۦZe������(�����q��52���.�0�����iQ�<l�q��ukR_Xm����B��?������*X�- >�r�
-0���B��L,ܭ�@�����e�m����k��� ה"����.��q�������a��D�+3�4��)0;�'�V���8�c�x>U��<��3M�8���(A�\�ڧ%�ߘ�f^R
-�A`�����%�����O�?gtNH#��)�u����y4����D@�ɯiL%�pn���tGHD��)vT�-����/*RX�sw���H���q���,!<_gX�����ʬ�y�aҒʁz��C���(��#�}jS���-
-����+Q��*kK(�-�U�i���dgq2�9��z"X�z�e��g�G�
-�)�M�Оr�ɝHr.}��]ӞP�����s�-)����{L��������W��te�P���}�ԺJ����t�Ku�W� ށ�䋃/UE����ċT���? ��p�D%��#�$���r�~��"rpb�KdCKB�3�����������#�q�%��]π$�LQ�����.w1e
-~S�)�,�.�M��X%�n-Q�<Y] ���!��8$Z���߮P��跩��)-��v��8ߥ���q"��8"���p~h��1��u�������tŢTd�Z�0ܹ�-���'��,�6������,��+s��YC6?����1
-�[��iȿ��e�S�����Z�/��V5���(f����%��a�Rn��e%����/��T���\#����n�C#���)���mB�¢P�6��*rd�U���k2/����&\=8��W�k��.��|�����m3A~�X��_������D�B��I���8�P9��J��^3�9���9wÙt�k��4�����Z�LυI�:�W�|���.e��-[��S�f�ʺ���*���,Q��A�w���h��$1[5w3��6+�Q��FJ|��,�x�*2�i����ǐ\P�PQTZ�+��Ǘ�~���N�=�[�����u_�C��/T�K���ndstream
+xڭXKs�����0�l��'��cijI*ES��EjHʲ���}��+{@���
+]��U8�E�`8[��"o���ʗ��2���몾(_�[ge��Yپ�+��c)E��]\��������0���~?y��{��C���Bc_���+�"y#9��v�����C�h~5R��5��w%��HZ�
+4���+PY�ӌ�'bOLJ��Y���������k�g�ȅQ�{�iR�7��U�Z�֛�.yn�/W}�T�1��'3)��/�����%�����k��H�E�q^_{�By�H��� �z�.���|�GcOG���n�r�L�>���75|E��m���c�ʍ���K�>���3���|�%��F���kE@��
+t��XZ�sPrL7���S��\m����6��k��Z�;Ǔ������5rlP �����"m�s���Ta&=�S�yD)��b�g`yQ�^HJJ+���JK�$�9�][h:iR�g���[��9���4ۺւ��e�9oh��pw�����;���5n%�%�s����A�R�zV]�9V�����⯮�uBaT�L�I^v\M��D*��l��P�Ti�dX�[U���9hn)_���Wx�^�r����M%iV'@7(m]x�j����<!�����ΈC��Y����[�Cx%t�Td��v9�+Mu!�A��=��s�X���g[.��`W�Ƥvh������1� e�E�.�{���T�QA������B-���3���T( TL��+�d��ZzSRo�G
+5�+�zd���P�#s$5����Y�.W*+�n=��q1�~�;��ˣ?]<�C�cCM�n�����=��J��j�On'���KG�3}����D!�����Һx��P��;�y��&���a�&f�'/:��k��'φ��yߏ$
+!ip!�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
+42 0 obj <<
+/Type /Page
+/Contents 43 0 R
+/Resources 41 0 R
+/MediaBox [0 0 595.2756 841.8898]
+/Parent 25 0 R
+>> endobj
+41 0 obj <<
+/Font << /F27 31 0 R /F8 15 0 R /F26 12 0 R /F14 37 0 R >>
+/ProcSet [ /PDF /Text ]
+>> endobj
+46 0 obj <<
+/Length 1692
+/Filter /FlateDecode
>>
-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����}������$���s��[hY���.UU�"�QA�p��� ɣ,�U����I��t��ey��[������@r���۲�U������Pf�����>�-�5Y}X5Y���h����Y.M�:a�#�K���Z���s�f�/V5�g��i�zX�ex=��" [:*�����BDž-(���6�2�v-�����%�����7iQ+Q����|n���:E�'&n��i]���h;�����x��Aж��h�����O����S\��Niϛ�L���gL�Fu_����}��Ygއq��ʨ�P[g�ʢ���\���� ����L����73�N9��J���VxA����*�GMŔi���Զ�4z<ڶ�B�L,\a�P�ŭ�����d��e��Kwװ?��s�1����#�US�I^�>c1���
-�k)M���0���O�@y#���EJ��3l�����b�d;�^6ex��}p�f�NZ�N�>�w�Ą�M�eZ�Ryb�� .�$|��WU��
-�ܶ�"�w��9/��.X��M_^Ij�}<�F-�Ѝ�����Ox�Ŀx+�� �z|>R{�[���S��W}�-�[I�j`L�S!&��<� �8���y}���6 ���@Req|sJBV
-ڱ����-�3۱>�|���WX���V���@,^q��������,ͯg�E�@XO��a���+�Jx��1�i��Ym�������T��r\(�-8RJ� �r l[nk-���")ѰXWi���o �M������������L���¾�Q��o�$6��d�Px�1M��c�<y'U��lc�T8OU@h���*��݉��>Tķ�%���g7Y�������ނK�v�I�^�^��{����9��@�O�ۀ������-endstream
+x�XKs���z�:��u�Ӧm�V&��X�$�)R)���Q��3irH���}�0�L��ʓ|2_M�+��T�"*������W��q��d���Zw�M?fxe��yMfqVY��d��x^ۮ7
+3�{���������qQ���o{^���v����h���[[k��,�꽤���3I��]����殮��E$�t�Q���;GdI��9�e�r�tC��`��4)����I�
+:��x�꧷o��a�����Le��*�R�ںg"�/��H�}��Bu��vi:L�,�>�O�e�߽m@ٷ;b�,�#��l���i����!b�A�a<���A��d�`+g�@=���8����iZ��8."wZ����
+8�º��<څv�{^iw���=献�����|�G�p���@�S9cψ1���z��T��.�j�B���3������>x���Ζ3ءb�ښQ��G��N�-��8���֢c�=��#V���+�^8�0+I�����<X;�I8�+�v���GϿ���p-V��<�]��d'�칲���?�I�y�1��QR ��T �l��V��E�.���O�f�+FIQ|�i}��u0��7����W[�jAUu��qf�Z��7n��|�;�ܟ~��y�i���) Q�W�����Kݏ�6�����O�����b�8��<��V�������Ay�U �4���2��
+�C�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
+45 0 obj <<
+/Type /Page
+/Contents 46 0 R
+/Resources 44 0 R
+/MediaBox [0 0 595.2756 841.8898]
+/Parent 25 0 R
+>> endobj
+44 0 obj <<
+/Font << /F27 31 0 R /F8 15 0 R /F26 12 0 R /F14 37 0 R >>
+/ProcSet [ /PDF /Text ]
+>> endobj
+49 0 obj <<
+/Length 2359
+/Filter /FlateDecode
>>
-endobj
-40 0 obj
-<<
-/ProcSet[/PDF/Text/ImageC]
-/Font 45 0 R
->>
-endobj
-48 0 obj
-<<
-/Filter[/FlateDecode]
-/Length 2186
->>
stream
-xڍX��-��y&���m�{��Swn����O��}�E/AA������ŏ���8Xl�a�x�
-8��j�� �����A�&n��`����:uW�y�c�m~Z����HV��B���"�W%�����(�:.W������֩�5º�%R�-}�}��H8l��\�G:�
-5]y���nZ�FI^�<����z�����������<X:��-����O{�II���v_�$�imE��Q�,�9������O��F�M���9���e�j3�̈ sVu���$��<3������S&u���t�j����cW��F5W�əppP;ZI�RW�S�%9���Y'�_>�i�w��w�ν�G���ҧ������DK�ZE�;��]��N'���]�X�`��N��u�ˮ|Gc-Tp�݈o��q'�sB���h�K�˛V��BY�
- L���8Ouu�������s����+@�������8�//�juLj��ODk���yWW]�]!�Yx���RB;>��:����-��U[��iK�uKB������s�R��,�-& ��F���i+��z�qA�ɤ�-�ATo��b
-Bl�V��т�g�Nqs�Ŋ7y �����-q�����ͮf�������5�.?u�8��v�Y IK�䕔x���y���������f�� ջ ����g;��[���m� ̯��QG��ܥ�-Ά�=qI%=l��H�-%�Um2!�������BL)9�Ht�/����ʅ�#cn�:��~��}t
-�l2��r#&�
-?f!T
->�"�8��W�^S��&n��_o�|���G�i��i�#��[����xS��4�8<ȳ�5�l�m~��_W���d��p���bF2�������3�Z��:�wU���Z3���V����-endstream
+xڭY�������G���c���f:I&C��ʼnD�d_&�{��\_D`��X+\���Wv��6��z>
+�����P�S��a�H�MY=������;���*��o���v�fb���;e��e�{�>�Mu��M��ֳ
+"Ͳ���GO�j���ݞ��i�A��p�F,�Y>��J_]�n'�����+16J�/��+Z�m�ϮR����І����+C����)C g�ਈX���3&�������ȴ8��.QQW��-�x<d�g@B9��P�&F+��u�ij!���I�R�ǞT��&lXؘ.r;
+1)�G]�ԃ�V&�l
+�*D����L6[��b
+�AAf�m�r���A+ߏ���؋��<�`?2�a��q ��w���lZp/%�M��C(mÜe��:D+��E����I��-aO��+<��$z�+B?���a6p��\�0��2I�9�e��V�8��/Aۆ��e�(ߜ_��cf����V�&��e(��q�7P�D+/���V���d�4���aU��+�}
+��8k!�A,�U�[�����i�&��R���h[#���k�����lw�JF�P!l��?b@�(���>-�p��{X��H�x��uw�|4�����^ B/�^yȣ+!���4�T�U���x�v���{S�FIWC����������蜖�����y�P�@<��)�^��G]`��{a����X�y"�<�Y��endstream
endobj
-49 0 obj
-<<
-/F4 18 0 R
-/F8 36 0 R
+48 0 obj <<
+/Type /Page
+/Contents 49 0 R
+/Resources 47 0 R
+/MediaBox [0 0 595.2756 841.8898]
+/Parent 50 0 R
+>> endobj
+47 0 obj <<
+/Font << /F8 15 0 R /F27 31 0 R >>
+/ProcSet [ /PDF /Text ]
+>> endobj
+53 0 obj <<
+/Length 1738
+/Filter /FlateDecode
>>
-endobj
-47 0 obj
-<<
-/ProcSet[/PDF/Text/ImageC]
-/Font 49 0 R
->>
-endobj
-52 0 obj
-<<
-/Filter[/FlateDecode]
-/Length 1949
->>
stream
-xڍXKs����E ��l�g����j�K��H
-�=S�
-Pe'>h4诟�a8��߳����L��,�g�������/���Ž�+=_���4xS�E�����̃mY�&��w���˟g\2>[D)˅9�*��H��д8�A�{<��ǝ�>��o� )<bYd�,�p�#8|������4h�2���OIˢ�B�Q'\p[{:��rF
-\\��j��!(��@E���g��P4ʃ���ԍ���;��7>�Z�j��]M�W��i���p�����=ъ5��,�!����"��&B�#�:�����`pH�0F���-J�A�"�1u��:a�M*�tI��Kq#�����������q�f��]��)\2badY[�p�-��@4�FJ��Jw��lfB7���L�k�� ��S+(U�ِ��o�������5}OK �#J�U= �5)��E$�vA�Kn��H`�b�FլU�m�������4�AU�-ͮ�MG�0`��_a8��)�c�������J���
-=v�*��|�杈;����l�����|�8���C��-�+3�-endstream
+x�XKs���7j��_��M��� [S�
+�R�2�dz�������g<�dA��0!�$N�����^����*�ן��?��3dQ�g^��6Y^��x":���a�����2)���@&������S_�<�^G�6i�MD>��F�����i��j`�UUDk��R�����;D�(:{��D�P^�¦>���0{.���
+��ͦ���=��G#���:�rw�2WN���S��驿*p��Ak�f�Y��W�R��;��jM����z��x!RL�aJ䲬 ӈ�6͒(
+��)/Kć��i������9H���;�3�U]8 �����e����B�!f�8KR�C�AQ�֧H�W��/!�O��K�o�
+��!������l�3j&A&x:T@��ư@"��b@� �HboeJ�_l-��4��ѹ�J
+���(��80�m�
+u|��ֿ��6c����
+�_Z�Ё
+�`�$�f̠j�]���k̊���\O��6�)p���#�4aB=�E{}��@˭��i[ɀ�(���N���{����Ŀ��N��D���0�nc8ݪ��Rt�.��\�%
+1rf����'���D-���(���lkZ�詈#Ra�a5q4�����z��^��4��P+r?u���8�.�h`�������Uu��� Gjw
+������r���X�U?��'�4f���z��c��1�@m� %*:�Ⱥ<�d��ز1ϼ�aǨ�XOG�����S�W�H��9���j[�#7O!ZUA+�������P��pq�PQ�H�ͺugjz��_���CN���涢�Ui(\��z���T���"������Zz�$������US�0��9��������m84��K(�e6�iA���_�Y��Yt���z��c[?m��zY�=ECA����k�U\@�AH���=ӷ��ˁ.r��É�H�6n d�,��B���̗0%�H��l����̱mJ������N��npt�$�!�ޫ`{��q��qB�x�|]4���=��'+��P��������������Bc��JHw$J{"�+k7(��N('>���k'�S����#�?!n�3.��nV� ���(���/����endstream
endobj
-53 0 obj
-<<
-/F3 15 0 R
-/F4 18 0 R
-/F9 43 0 R
-/F8 36 0 R
+52 0 obj <<
+/Type /Page
+/Contents 53 0 R
+/Resources 51 0 R
+/MediaBox [0 0 595.2756 841.8898]
+/Parent 50 0 R
+>> endobj
+51 0 obj <<
+/Font << /F27 31 0 R /F8 15 0 R /F26 12 0 R /F14 37 0 R >>
+/ProcSet [ /PDF /Text ]
+>> endobj
+56 0 obj <<
+/Length 2234
+/Filter /FlateDecode
>>
-endobj
-51 0 obj
-<<
-/ProcSet[/PDF/Text/ImageC]
-/Font 53 0 R
->>
-endobj
-56 0 obj
-<<
-/Filter[/FlateDecode]
-/Length 1880
->>
stream
-xڵXYs�~���I��C;I�&�Z�>ԝ,�&�����δ/"�X,��?f�3��x�����%��ƳE����a�b�lw;_DZy��V���u���)�V���vEa���fJ�?���n��ثP$� )�u���<W^%�m���?����Y�����];��F5s��&��ߒ�J��+�u#�R����/�nj��{\B
-i��ݭ+��Ӑ[������wݹM���U�����qM����'��Bҽ�ę�����TiW�-D�lm�:/��9g8��|T���`%"�-f�A5�YHn�QyuՕŢ�ݎyZ��t����M"=���!�>�Ct� Lo`xn�ڴ�*b���Ƴr�CI�ȰX��ME>�>����ʘ������"�T��A��-�<��r?�����׃�Cɰ�ny=
-��-#����<<eV��y�T���+�cc�������e�LR\���RNv����8}���t
-��a5�A>�(���@>��&��0%j��v�ηXԴ���;0��:0�)�&%&MkjR���~���k"��帇������ ��$���0)�>��.����%��:��\JH`�<O�T) ��2�ū��n�1���r���]�o$g;R�m�S �,v��n�@��� 16Xv�k3fS�Wz��-�q�=��=W2+Cw�ej���K&���p��f����SI��x�5��O8�Ǥ��9���(��`7�5��6�)��������=����f���'��n�K�
-e�u�3P»�G� -�4*����UҳĽ��,� ���v�]��y��H=i�����\Ϣ�<�ӕ��Bd������5�Q�<ևc-endstream
+xڝYKs��ϯp͉���ڃ���3�XsڤR�P�H- �����
+���,�8�Zm����y�Q�FW��/a�/~]��F�k��E\xv�����~���ݾǕ�>i�G7�3͖���L�k��q������עH��H<����D���UUdQ�Y�W����\W~�q���.�A����x�n0d����8�{��%
++��a��ހ�ٛ����������[3kV*J�%Q�L�e*({�6�2zL%Nf���9�_v`s���`���+?�Q^����������=k�����|�Q�'���T�n1����4����>���a���q�\���\�E�\���Q#�d䒌\��K�g:M"F��Z,��#�t�e.�e#�l䒍\��'eU}4U��[���&6;S�R�)�)6
+��J��1fiJ�<����ΰ��y�����l(+a��+o����n����I�4�����j�$$��E;-����ʏ6����L������!ft��2�q�W�+�˽vÿ�:�> 5-$����r������X-
+����cqK���+�3� ����>�(�p������4�+1�s��[C�Zc`�q���L�Tv���Z��+�
+�'R�Z�&qA~$�&��E5�C��%
+�+IZ&+�����8{\��)~�R�n�r��|�6��w�H����A��Xv�K��f"�2;�`T��\Rа X���r9#d�Q&c��+��`�R�`-p��[��*��+�+I9�2�"ͽ8�Su�ߋ�$+HH�Y͠C*D,�����zR!�1�����Ո{��O+PLs�)��O^^�,p�}��K;�������9T���i�;���f!eM�K��+�3Y|���e�+^~�^&�c�o��@rz�f��\v�3������/���^=4HBқ�{�����7�����ʽ�<����Y��ߊ�lv�n1��/>�펇��5�)�
+����)�r����~�=n��nxV9'x(�����y�!���?}}�߿�]t��Z�O����ߨ�����tws8�-M.��GgL;}7�h�[��m��Uu�Ƚҁ��LhN��M����}�b^Ϥx�T�,��"LI�%�z�z���stream
endobj
-57 0 obj
-<<
-/F3 15 0 R
-/F4 18 0 R
-/F9 43 0 R
-/F8 36 0 R
+55 0 obj <<
+/Type /Page
+/Contents 56 0 R
+/Resources 54 0 R
+/MediaBox [0 0 595.2756 841.8898]
+/Parent 50 0 R
+>> endobj
+54 0 obj <<
+/Font << /F14 37 0 R /F8 15 0 R /F27 31 0 R >>
+/ProcSet [ /PDF /Text ]
+>> endobj
+59 0 obj <<
+/Length 1361
+/Filter /FlateDecode
>>
-endobj
-55 0 obj
-<<
-/ProcSet[/PDF/Text/ImageC]
-/Font 57 0 R
->>
-endobj
-60 0 obj
-<<
-/Filter[/FlateDecode]
-/Length 1907
->>
stream
-xڍX���a�E"�i��$K����Be�G�`����<�<�;�5slǙ=��������C�~V��nu��e1_���ٕ�;��j4ﻳ��f
-ǿ�?�k�-\���ʊGf*�����둊mU>���,�d���������0���� ǎ�c������6���I�<%lJ����t��;$�1��-?���������,<.��3��^x�Y�r��m���D�tT4������������i�����*"F':Ĺ|�-.�7������y��
-<[�!��=Q=�by��8���8!<��cqC�w��0el�1e��4<&��-��Yj/���9�2�rD��Q���س��kU���Jo�����T��. z(
-�-����p�b���LV ��A��]��=}��c��˶Jt}�C$p��,T�ax9ÊH�?̾`�)�����3����G8�J�a|���?����_z��X���G�w}�o$�Q7U��wb��0���(2Q�&&�4I��̍,&�Լf�"Dч]C����
-Ga� B�S����pt��uݰ����`�A͞����z#�K��Pl���2Hw]Z���*tapưS���Q5�D� W�x�!\���y
-�,���t��+$"F~E���<�to\��]�PjTŋM�ySv�9�1E���Jh�9���eպi���]�v-���
-�Ք���D�����JT܇
-��TԦ�^xm<��-endstream
+xڥWmo�����Zջ��%Ζ�K2�0�C�HLDD]��k�^(�4J3t�����l4����� ��t2���yA��e����̏�~�q�^�<��7
+�h4o�$!)�Dc�ֈFh���r!����T��^y��9��wB{yY>��>�d���%;��[��f��}��u�5�#����gO�ɥ9W�E�ES�Ô���_��8=Y^\]~���^��_fgO�O��Q��j��R�G�>9��h
+�H�M��;���n8q�kY��ά���W|�� vb�[ɻ
+�[��c�{;+�.S��oU��+3g#��5}��h����\��ڈ��㻡�x��J]t��pIk�8kYZw7��J��+!nqu�OySֲ�c=����˳��{�|�!k+%l�Z����Ú�6��h����?�/W���s��i
+jb�J�SZC�ʰ�qh��=ٌ�J�
+��5)�'����Sgg���+�s#����$>�'+ق��+;��^"#�E��S�k������yH����<+��6q���+r����n�bDȊ�0���S٫M5
+��;�@ &iP�ޜ�Дj]��(���+��8U\mTT���8�:}���o�v���������2巚%�w�r#� ��+c~�P�Łc[+��B�N�/(�Q��G���S�l��\0�I�s [���'����eֽ�F�w9��j!%�&��Y28 endobj
-61 0 obj
-<<
-/F8 36 0 R
-/F4 18 0 R
-/F3 15 0 R
+58 0 obj <<
+/Type /Page
+/Contents 59 0 R
+/Resources 57 0 R
+/MediaBox [0 0 595.2756 841.8898]
+/Parent 50 0 R
+>> endobj
+57 0 obj <<
+/Font << /F27 31 0 R /F8 15 0 R /F26 12 0 R /F14 37 0 R >>
+/ProcSet [ /PDF /Text ]
+>> endobj
+62 0 obj <<
+/Length 587
+/Filter /FlateDecode
>>
-endobj
-59 0 obj
-<<
-/ProcSet[/PDF/Text/ImageC]
-/Font 61 0 R
->>
-endobj
-64 0 obj
-<<
-/Filter[/FlateDecode]
-/Length 1744
->>
stream
-xڽWKs����fL�@�j��$m�ig��D��F�,�%R%);ί�>$��������Aήf4�v1[;[�g�
-�[$����j��ϗo͑�Ɛ�?V��̐4g��8����p���$�W��Ֆ {R��[\F^���������#q�h��E��|����y2�K8l��w�O?�A=��3�M?���M�8���y���n3Oq�y��+j�j��m�8���t�F*G�r�n�ҹMGf��&t�;ӹͦ�����M=��c�t�=�X�٧�퓫o�HT��M�,������* �i��BN�
-��7E����r�z���PŚSw�����Y1��5���a�CS�W<=Ƅ��������!x�=�@Ç-P'a<4����v����������ee�86��3��-�Qmb�ndstream
+xڭT;��0��H�m�E:��k�R$)�,ˢ�k&w���i�of���e�q��<�N�L�����X�L����>��[�L���s(EFR\���k��\x��C�n������E�
+�'�9��P�Q*]ƙ1ł�7�e����27�C� "����"��!S��,����X�*�+��u���+���p���?�~+b������8B�?�1L�&�X�(����4\Z8$��S! ���d7�\���E�D���}X�0H@�&�����՜o۴(�-xZ��}�+��������h�/*'oG�.��jq�K<��k(����sWI�@,�iԓ.����+��Y=}xZy�y·a endobj
-65 0 obj
-<<
-/F9 43 0 R
-/F4 18 0 R
-/F8 36 0 R
+61 0 obj <<
+/Type /Page
+/Contents 62 0 R
+/Resources 60 0 R
+/MediaBox [0 0 595.2756 841.8898]
+/Parent 50 0 R
+>> endobj
+60 0 obj <<
+/Font << /F14 37 0 R /F8 15 0 R /F27 31 0 R >>
+/ProcSet [ /PDF /Text ]
+>> endobj
+36 0 obj <<
+/Length1 750
+/Length2 576
+/Length3 532
+/Length 1110
+/Filter /FlateDecode
>>
-endobj
-63 0 obj
-<<
-/ProcSet[/PDF/Text/ImageC]
-/Font 65 0 R
->>
-endobj
-68 0 obj
-<<
-/Filter[/FlateDecode]
-/Length 1567
->>
stream
-xڥW������v����6���DۼʢKQqr�#��� �� -�Z��.Y��<Z�Uc�^(�V�|���[�ˍaoG���Lm���%T7�?�ܙ[���ҫ��1���2��}k#Q�ڡ~�������\;�H%N���L�������ޞ���$��;S�T*�j"-�?L�X?��0���B������kʵ�W�<<l�/s$��Z�����j�*�$3�e��r�*�����{�wh��=�6;��tO��L��*}u ����͌�ʆjE�*-�l�˵
-A7���H����|�k���嵦���\�Hs��bÒXP-�5�v�7}���ì��S��\�o��@頦'
-� ��-i>��o
-�����C&=v��ءKj@d�ߵ�
->���G�v�Wɚ�K&�&�h�ٜ?dLqx�}�|L1ij��-B�-��o�-���~����;�H�ű���2cJ�²Z���~��"����#J�($mj��#^��P#�>gl����5/@3�_� cx�a�Ϥ��~�����#��Y���-endstream
+x�U�L�Ju�+���Rp�44P0��u.JM,��I,I�R0��4Tp,MW04U00�22�25�p��,��Q��)2Wp�-�N��H����������ZR�������Z�ZT����h����\������ǥr�g^Z��9D8��+@'��T*���q�����B7ܭ4'�1d<8�0�3s*�*�KR�|���B盚�Y��.����옗����kh�g`l
+��vˬHM �I�HK�N��楠;|`����xkC����WRY��`�P
+���P�6�00*B+��̼t#S3�Ģ��.`
+�
+2�R+�.��/jQ+���$e~s�ʻ���?��|<ċݺ���'}I������xT.��gt���vՏG��U|���~��]�_k��{��
+�}dN<6�-uB��H��c�M�H���q�a�K�K̞�}��˛���m����o���v�
+���s>���.#��ߦ�{�/��kܗ<n��[�����[����d����1���͓_��aՍ-<�+{f�#Mz���=��Y���/�T�}���)���0��:o�K���.l���yE�
+���צ������_�� �ᚔi�s�`��C���I^>�������d�j|��/�p�0,H�M,*�M,� 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
+37 0 obj <<
+/Type /Font
+/Subtype /Type1
+/Encoding 63 0 R
+/FirstChar 15
+/LastChar 15
+/Widths 64 0 R
+/BaseFont /NTVDHS+CMSY10
+/FontDescriptor 35 0 R
+>> endobj
+35 0 obj <<
+/Ascent 750
+/CapHeight 683
+/Descent -194
+/FontName /NTVDHS+CMSY10
+/ItalicAngle -14.035
+/StemV 85
+/XHeight 431
+/FontBBox [-29 -960 1116 775]
/Flags 4
->>
+/CharSet (/bullet)
+/FontFile 36 0 R
+>> endobj
+64 0 obj
+[500 ]
endobj
-7 0 obj
-<<
-/Filter[/FlateDecode]
-/Length1 1095
-/Length2 4446
-/Length3 533
-/Length 5179
+63 0 obj <<
+/Type /Encoding
+/Differences [ 0 /.notdef 15/bullet 16/.notdef]
+>> endobj
+30 0 obj <<
+/Length1 1884
+/Length2 12110
+/Length3 532
+/Length 13167
+/Filter /FlateDecode
>>
stream
-x��gXm��RB��H��@HR0�TC�&
-RD@��4�w���PP�#����^�2�s���s��a%���!� iIi(�mj)�-�Ձ���4��\������ ����<<�G�X���E�`����~A�-D",P~pO���#��� �������%����Y��b{�QJRJJ�XH�����8
-K
-9y-����0�
-���
-J�oר{[������+N:dt5DMRI��cJpm����O�R9[�������Da��Qf�t��� ,KU������������jgʏi>є�?���ҫy�������C����>��ȼ/�nlB��Ⱦ�)<�CY+f��%q���Tx�R�� �sסmiS��{�8�v��%?�5lm7r�ba�oh�㜣��]x.�W�c��͠�Y�u�U�!AY��V8�$�����)Y�3#'a��id!���ܚY0�a0,��
-�n�F�E�FU������C:�ѫ3W�#��h?y>��1�_ίz�ʈ#
-�#<����D�k�w�b�8�w*�m\��"7�[�dӯ1)�0X��l�����@��F�E0��!/`��Ķ7�$��-,/��uob�����;y1�#_�k �|I�.�©���m��H�s�����o�$���ʀ��[�t�*i7|��'�8Q�T�!�:�祖=��B~��Z���Z)Rn��i:m�����Lj��ܻ�Ź�OZ�I.��� �
-|��!L�w�mN)����fJ��٬KZr�9<QR�#ژ9s�����/����9���'����|�O��y��.�]��^l畕b�}3K�/~�i�x����i����м�P�@������t{ܨOp��-�"{Û��L�����������R.}�J?�c��H�w^B�K>��Я�{��yM�b�D����D�r
-j8�8MA���ج�Nݲե?=���Q"{���=�\��l�>d�S����_m]
-�4a�'y��D�8guKpqM����2%�S�����p�(�����j�3���wK�QH���!�Qg�L����&��T�����(>A<�)�6�\��U��v�P41��h��si滿�Z?�-����i|��:Z܌�s��ͪ-r��k�����<Ɓ�M^ۑŃ���k,���_�����`a嶌tQ�E���-BV7_��g�fp*��/���'u�l#'W�T�I��?>�x2c2�q�6�^Ӡ�%珔v됮��S�g�WY2��u��Pvf�\���ɓ��-���M5̣��u���,姨y}�92(��ם�[U�Ҿ�ɜ�;Ci���E���PE�C^�7�8]��)Z�� 3B����O��������� �j�n$���P;*����;̍-�_Te���bz5�����t�a�̒f��`C�0�RZ�{�Ҿ��W��6j;���;J!Q���Zݔ7?�B=Y���� ץ��vo���'�%�gm����j�D�����6�N��9
-�v�Oy&=�@4��d_�.�xB��
-��c���y$(���N���,��������
-X^R�,���oP���Y���/T�l����W�[fj�ʼ�L\q$��H�����:9�X�X��<v���r�iBuZ�ݡ��@��(���$I�2���v ��/h�q��k�����B����Rn����nJ�����P��n�����jI�wl����˹����V�1���F6�-,�!�ܯIY�w�8ؓ�b�j���)�\&a��yQI��Lj�E>��=k������BTy1�C���M���g$_�^SX̍"?�Z������.w~̲jpŀ�95F�"4��M$��a���L�ě����������( E�%nb.�5Mf�!�%��E�26�ӊ�Y���^���!b�a�o0[�}���9'1���M0��u���Ujjt�|*}ɵ�v�;�U,�������R�i������2j{�v���,:���(��]�h������9�aw���ڽ��U���sw����`w廬v?$���w?���S�"����Ge�7�]��J��x^-�����.�t�jgYoXk�O��m�S���'��ڵ<�7!��SdBn�y�2��WUh���-��� �7����nv����LJ�т��
-�L-endstream
+x��UX̶h��k�+��3�a0Zz:X�8��@fB�+�y0��D���gae_Lf����˿>�������H�K S���
+?�XH����o�+���ec��4[�Y��������>ʓW�� �o�nF�Y^mDZ�Y���;��!�+R�Ks���+����� zu�{�C88�$�l�C���`d8VD��o�c�$�N�Ү߀:]���
+��!�LᢃXD����6�6c�
+e=��&���5��?�fvU�z=!^�& ���g��5/�PU`�����CWK�2�����f+^+]y��;Bϕ?KCvl��OQ�"��V��؍�Y9'ôj�[J�1�� {�_y�S�W������bz�(�&>�� �����;���\�x��)�]����@��7�����fX�U�h��Ȝ����Vچx�*iY�V�d�Y��dn�����f�p��JW���n�XJ��+�j#V�L`a�rڥDrC���p�����'��ڰ�0�"�v��qX�9��ؾY���y+Ŋ��#������&)��7?m!?'~tdC2�~��,eS���j�\�J�������h;�+�geQ{ �n/%trT���t�4�� ������0��M�|�4��J�e����LAW��\h���%������C��α0�
+������>$��Eqb;'��:�}
+���r��� ��%���k�3 �+)aq���{�����w珦J��9���N�����^10����h����D(���c=����"�\y4����T�i'�o%�e����9��5��p�Q��G��Ѥ�;;-�g���p<�>��T�p�"a��jK|y���O���9�.��+���<���#�l��a�'`o�.������mXW������+���ܙ!r�{'��s��wFtqd���gY���:~����~'�1�է]Ѯ�ĉ'��Aܫ� \L��d{JL:E��Zo��vO]l�V�0��������N���B���WI��+��HD!�q^`�����`x�S5���r�谞1�ߓN�l����`+�u�*�VQ��ֻ8�GXl2?����y��K�B�y#���65����$�������߸��+ޢ'�MsrVA� �͕����� ʞ��+��������7_�/�R�� k&�͏�n���� ƽ�A��'Xza*
+��lm#���pϲ\!�/���ټ=|F[�7Y�DԱ�m�6������f��c;PvN���{�p��Ό�������"
+ٹ��,ݜ��l�k5���T���m^z�js&
+RX��+VD��C���/�
+��l�%���s,J������Ȇ�!���p(�vn�v��������mJ��q�?�]2'Ng���1��-0Oɸt�����k�)��śni��f۹ߌ��nu�bZ'U��7���f��dK���WtЦ�`�KC�˙xW�]�װHQ�#����e�W�q�E��iY0����|*_\�Ʀj��|Ѯ��h ���������C��E�r�%}�eM��XQAGS�2~��D��uou���&Rm��sID�Sb9���ߪ�G�F��e��B�0O��GZ�3H�v���ve��6&_�/��+h�
+5M�<E�둒?JY�9N�P{�n-��I$�}�+����4Ŝ@���'3s
+�(S4J�[��Q�#�L��Gq�Y���D��O7��<�+��W�����.{2z J���o�(���W<
+q�1>4`U�+q����sp�!o|K��"�!8���܁���*�#)5�^7�v�B1��:�1�F�E\C�d��G�h#���ұP��z{�iG?\\��:�$2���GN:��l���\f5&n����?ڤ5H�?�9��Z���#�a�F�C?�]bF���m���%JG_�Ő�D���gxx�J�=8m�������`��7&b�!�Z�A�'����p�%e9'���������F�0������-�m�y��2 rh�
+��S��rS� �zL��l�w� �xx������Lk� '_Ɯ����]�1��n|
+���9h���:���-Ѥ�6|�9�m�;O��E *W~�xd��CReC�c1nLq��|�HH+g��9J=�������BRK�5 �2!'ͪ���2zWL�=
+��|�W�k朦n��1'�K7��U��a�-�W6���]�C�,<��o-���'a�� 2c���$<�&����n��K��u�3�F�ڏ$�Jzt��S�C�p8�a2{�%�����x�^��4���-.�:PW��8U���VL�|#��P�>.*Y\�QUPrA���#:�����R��@�)�;����|��F��y�X��8zv�:���x�CS,�ƈ0����)�M7B�+��06�Q`+��\ü~� W���cб��.�;��\�����a�*ю[�[5<˻T�Zvt�?���'�?ud������;�����3��/�X2D�m>���
+ +�8�`��Ơ�Ě�}�
+�{k�;�أn��ԅy��9��"����"6��la�0�M�m.
+��:!ڕ=>ng��i�#�sW�l��/{�
+��b�ۈ`�إ���8Ֆ��T��+eq�[���
+��u��˓iz$>��в���g�K.�y�I�w�O�k��Q���v4���dp0�F�#�]
+���{�0�@-Q�ix�J���S��{�
+����������i�s�e�W����.��]�����e��5�K:f����k������6�\J�x��+�2���$�no���a+���l�`-�TVo�,A��[���a������ț �h�(����G&/u[h�7մQ77���A��m�C�!�4��K�*��`N��ǡ������-�ʜ���a�닰�Ip{_)[m���P���̊g��L�����l��ul��������u8F�ۖm�o2�����ſM[��aˊ��U����LP-er�WJ5��s-q��ՠ<���M&��mF�l�z�G�K�?��\�+*�R$���
+�Q���b�K{�,ɣUк�(7������οʿ�=������7ϮD��W����^�=�\ׯ8�C��"] db��|h��y�%
+ṟy]�+��
+(���P��#��x����\�h�$��nT��B�Fy3"e����X6QЯ�x�m�m��?��/�9S���0؛׳��h1������������N~v����%��$����"ex�nv� �aew��]pS��-��R4��^ �:�b0�>�f����`I�mL�۲Y��`�8���g."ǝ.�g�������VL�����al5I|�����i��.]��<����,)�������k_+��.�8ݓ8��D� ۖ�p�^�n����Mr!��a�u���T�.t�Ti��\�������=٦��đn~��T��5�rO�W/�s,bV9�Q�q6�����et�������z}l�Ѽn��
+���~`m�#���*�y����ρ
+P"<D�ю�ǝ��.�>u�u���
+��_VtfA�^�:�P+��ZD!Hj�� l#�C�����������_?�������,��1*n�/��}O���6��u�^�2+qʴ��l�\i��a4�Ќʳn&6��I`�^���=ȡ��N����=n~���?1z�&c���Dqm^�y01f��{u�<��E+r���&�>�kfBVH���܇?hXgh\�M��Y`�ʤ�7%|��nr-Rõ��X^Ξ��mF�좾tXč�6b�Ϣ;o4�F%�U�����2~؆����;��r=�K���������a�I�S9n��Q�sէC�������������˳��C�ơ�¿���x�e(��}Co���%c�0��i+�%�aY�i8A?a����*l����'���G���͖@I�ɃK���BUԄ�;:�H7�tNW�R-D:���;`��mѵ:Q��+F�����"�P�uit���u��\�:S� �&��Dx��3"�D�:�Q�H�+AO� �i��RǓBu�LW������Qpb�r~X�?S:�Y����w��+�\�b���
+<�Т,�!���6����F�y�;b��>����8�@��B��}z]����^cT��h��Jزs�&zo���a=���F��WI�+����VQ�O���m�v�\\h��<���f��A[9�(γ�?+�I��-�sc�|4�h���͗�^����L�� A�+���)̍S3c��γr�?�Hi�l9#u����ӄ�49+W��}j��}�2�I%� ���v)�^���i��.��K�G9�T� >�ќ�9��abI"����z���U����~�Aϐl����8��Ϸ�E��a/
+P�����'�yws'��ڵG��t�36(\���T�/"ᕳ/2D���[�^��eZx�U����r,�TI�]�
+Bg�Z ��Q�V2��?�{���``�ie����msbwd��Ҿv�* 8|6$�~��"u��ӗ��Ӎ+��L��Z*h����_�Ԅ�%�����+q'E��T%z����M�%y��Kq^'���D��<��J8f���}��l�n$��K�f@Ĥ��f{����R���r���j�%��ɤ�|�w����}KW�����R�endobj
-11 0 obj
-<<
-/Type/FontDescriptor
-/CapHeight 850
-/Ascent 850
-/Descent -200
-/FontBBox[-34 -251 988 750]
-/FontName/NJEYML+CMR12
+31 0 obj <<
+/Type /Font
+/Subtype /Type1
+/Encoding 65 0 R
+/FirstChar 33
+/LastChar 125
+/Widths 66 0 R
+/BaseFont /NRYWIT+CMTT10
+/FontDescriptor 29 0 R
+>> endobj
+29 0 obj <<
+/Ascent 611
+/CapHeight 611
+/Descent -222
+/FontName /NRYWIT+CMTT10
/ItalicAngle 0
-/StemV 65
-/FontFile 10 0 R
+/StemV 69
+/XHeight 431
+/FontBBox [-4 -235 731 800]
/Flags 4
->>
+/CharSet (/exclam/quotedbl/parenleft/parenright/asterisk/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon/equal/A/B/C/D/E/F/G/H/I/L/M/N/O/P/Q/R/S/T/U/V/W/Y/underscore/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/braceleft/braceright)
+/FontFile 30 0 R
+>> endobj
+66 0 obj
+[525 525 0 0 0 0 0 525 525 525 0 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 0 525 0 0 0 525 525 525 525 525 525 525 525 525 0 0 525 525 525 525 525 525 525 525 525 525 525 525 0 525 0 0 0 0 0 525 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 525 0 525 0 525 ]
endobj
-10 0 obj
-<<
-/Filter[/FlateDecode]
-/Length1 1091
-/Length2 4694
-/Length3 533
-/Length 5429
+65 0 obj <<
+/Type /Encoding
+/Differences [ 0 /.notdef 33/exclam/quotedbl 35/.notdef 40/parenleft/parenright/asterisk 43/.notdef 44/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon 60/.notdef 61/equal 62/.notdef 65/A/B/C/D/E/F/G/H/I 74/.notdef 76/L/M/N/O/P/Q/R/S/T/U/V/W 88/.notdef 89/Y 90/.notdef 95/underscore 96/.notdef 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y 122/.notdef 123/braceleft 124/.notdef 125/braceright 126/.notdef]
+>> endobj
+23 0 obj <<
+/Length1 1140
+/Length2 5257
+/Length3 532
+/Length 5984
+/Filter /FlateDecode
>>
stream
-x��g8�k�ljh�G�#!�1J0$��D�1��0F2z�� H��A0z�!�"щ���s����/������U���PH�(X��%�`1q-(�p���@�����.�@(�+x��w<���j���"bը����"�H�R�W�ڛ�V�ҪYjS���6U���{�����9��
+� �H7OA�X��m ���;O8��*
+KI����0,!-*%-�M*"Q~h8�ȫ�+�E�vn@m;O'([b�4DB�?!���+��@��u��8�h��+�Z���;����ꯢ�r3�
+� �����Y��Az�����o���$� ���e�@0��@�6u��� V�n V� �/��� V����~�������PPD\���4�����]f(�
+�"!2��u�ʅ#��
+�� :�m[HC�q]��/֘�WdҐ����7?�T�}��=ӿ�� S�
+u����n����d������=^%�/D�-�_��+��c���"��[�݀2ݽ,R,b�2�H�sq��~ҕ����+���)�I���]L��1�����Ƌ�܈hwB�۸lv���r=dz��e��s���>�bA�T��j%[8�,�r����Ա&=kS�?�2��k �#�+
+h2S��e������_m�έ�G'Y�����:{J~��@�z'^��y^)�C0�����>���y&!|�أ��<���N�/}��pmDR�e���*��gO���v�+��p�V�WF��8�ҙ3�Y�G*�/�t{bj�~>������s��U7�g>���~ӵ0L�=�\fm���Q����ӏ����+|�e�]Ý�������S�W�:�{��ժ�b��� �V*���G�i=y�52LG�dm�r�jO�C_�~S��8Dq���h�۫�n� ��������\V�� �ٓ��#?
+���)��xKk����_@�p�g�N�~Kj�����Oe��q���2�f�������b�F2�&��b���
+L=�sOI衏4�ඈ����Ve
+�-�N>:�֨P�X0đVV��NKK�P�C�-�~M)��{m$&�f��~��71>V�̭��^#�X�7r��=��<���٤�YmYΈ�J(�%�G���6��^�OIhvH\��6L�m%4�����h���l/�e�E=A�U%�� ��0B�����X�lp$���%-�Pp��lh�`�Yr[������p-��+`2�ڊi�fJU���u�ִ6�#���P�%��u������ˢ�d��ތ6�%;5
+ �e+h�����M�M�e��H�[J�Me���_c|��!o��&?גݤ����Mo������Q���������\.1��&��g:�q�2�E��^�,ns�@���:�|[��$����A���<Ix������Z�̋�:��q����[����>;>|/�2�T�M����v����X��)%�Z��g�C�.F��һ�Մ��i�
-G�B����@�o0��°@���Da)@BS|���JIAv(���%\
-�y���iq ��
-���G0�
-��������0��( 9�H��
- f]~C9 �Α�!a#�(A8��
-��4�� ?%�����ޢ�R@Q B���2���5� w�#4W\\FN����!1�� c �aav��t&8����֑��
-����-*R�1��Pg��܊�������������5�-��z�p���5)�4���{:��-1n��]�A�k'��9� C�W��3�zVH0�όw�D��U����7������ ?h5�-�����{'��'��"���vK:MISV��J
-�~%
-Ư[f�����Ӗ���#�BR�w���q\R���(�6�������ZR ݺ7�e���Q��#{�c:~���o��K� �{'�;��k��<k����m�2�����z��&�~�2���-���~i���"�X���5V��ϋ���Sn,����� �(��D@�� ϒT��e踬�F���uv��t���;���٣�f>��34� �6��1��*�ռ?ր��*3��_�j4#�`��*�3�nL�X���n\�J�^��pӭ�U�>>\��p��t�X=n�9���4S[����C�AR�Riin+���V�+L��/���@(������,��i���{��pA�����?�����o���zCUg��w�K��l����33��#T��ul�;�_w�k��5���������^:�c��D��[J���`f�B���'�{�R4/�������[��G��MX/���͛�oqܩ� $M���N�ǵԕ���d�g�ʥ����'����������8鉧�A]{,C�%�x9�me95қZ���x�v�+�2�x��oS �-щ�@9�we���|ɦȝ�(�����t�g�}Pc�z��;�&�l.SIZf g���-� �FnEn�Z��G�����Fw�[���U��+I~�O�<\����~�����r�^C��i��+��4�[2>�,��&R��y
-�i" u%� ��-�s��'�=�����-}���ߐ,x��Z��-�m+G��V�,���?��_�-�G�)_%�b�]�V��Z��Ф��v��Y%��6�Gk�O�����K�=��E2������9����~�{["����Բ ˟es�Q]���
-��F�?�����b��H�
-�L���q�������ɷ凂��d&|QJ>���)
-�h���Ȕ���#^��m�4������3�%
-0��
-^.xɿuSw��J�=��<AD�S��-�#�⓿58sQ�Z��qZ
-h,��D���B$;����^ПЪ�r�����J�����D� zj��ޯ�.7����q<�W����TM�]���31������@�E=-�T��m~'���gn�
-����p�5u��-����f<�����H�(-��,�UG�@��_:�h�y�A~�'�g����jü5�M��%��PGLda\�
-
-<�B���������������[�jf��j+��4��
-��Lz�!|kS�Y�W2�ӵY�V�[\��Kކ)K�����3T���Hٗ�v���}��MK>��~M�Y��-�]D$�5�����5�=ǹ�4�ų͑�)����� ���6Ո�[�^����5oA�n�-����������Im�����������嘺~+`?/dü��W�4��Y�$f
-��V@�י+��w�����Z!?��
-��X�9(�W�U�J�D5���W�Ur��$
-��|�Z*~��?���{���\�3�H��Y��¸B�+���gp�W��!uf�x>G9j��^�̸l��~���+�hb�gjS���]b�fT��1���v,�~By�o�z)�z�4,9�.�o>�&=��sb�o]a��T�G?�{s@v��M�G�٥�s_�ݫ;w��)r8'C�j�p��f�!G�߄q~j�1���,��G�s���4�����u܀�.��h堧�-���/i��*V�bN
-�0{Q�N������ғ�Q�����aZ����T��N�_;���9�D�J����g�X>�����y��l�_�Q����|��cWH����\�̈���/��v)z#+G{u�R��C̿��.Q�k/��3�h.f�)r��^^4��ڔ���6��������}Fr��gѕ*L�]٨Z/��R��*�����벜\W���� ��,�*�"��_ye''ݞ͚[�ڻ�9�Go�\>\sc��b
-M\_%.���N�d���0)���[����z9�X�9��+[�9�)#���K�=�(8bĨut?����µ���S�����4��ͮ��-���q<������"U��H��As�.��)�>ٯ%�j�83�L �;�Ņ�7�UQk�@����s�����=d,�7���A��7?��=��F�L��o����IN��̭S����niY��_PV&k�-�1�g94Z�#�T�
-6����OD�v�R
-�_ٹ���D��������`�� ݃�7 ���P=��������>�����O3��t)v�S�3ȘQݼS�����5V^��1J��)�Ji�m�[�
-���j��!���+I=>�Ջ{ ����Zkt��7b�c�J_MrH6�������69#��.x5����L�y�e�D�2v��}}e�t��n���#a�m�1��1%�d�x�Q ��-'ؿ�������ݐԕ�?"����%(��̗@ea�9�C�Զq�吧�~��q):$��ܬA���:�$�ć�-endstream
+H�_Dy]̠��\����ەl8�܈퓕{��NX���Pa�+�yW�����*�7����"�ݹD�d/L6�o�x1��Am�D,~�H����轶A�n���*���X�Z���J�CN>`��d�6�k�u�J&l'"��=� Z;j\/P��\��bL���=ʢ ��9붤��k`��:G�M����9�����зܹ0����~�MY������o6/�y_�n��z)��U�{�s>=h���(.F�{-�z�a|$������+n�e�C����%Ga�W�����S��m(G���?C���)D2��$5b�}ȼM3��%�4鴅I�>��K,���{�D�K�R��mK�a���B$,˘�|�Ԍ�օ���]�G{ep�E��X�����T�w�������� {x�'(��!��e��Vn�V��h�;!Og��nQw5���������R��Y`�Ȏ
+�8� ���&�L�l3P����x��H(͏�����T|l���Qo�Υ@��6|��
+
+t0�5Ĝ��|{JB�LH���ў��t��Ӕ�ʛ�>z�q*����(y�v�y;
+�'~Ɵ��V8H��N9+5�+.{��=߃�q]�ٍ��l��A�𬌝��+�R+�N�\��_�RF�i#��
+4^0�*��(n+-h�~>K�a��U5��*�4Ćm���4�y60�q��Vz)u� @l�C1�|/�}P@��(�5�O�X`[��z���Y������(���/wؘ�]+��֒=H�Ƙ?g�̭g��tnP�ی�ޤ�O��e�ك�F7�����9<ks� ���Dk��%��zk�
+j<�m=���X�6$֤���Hh�R��]L�������
+*��T�7��)��&��Z;�(M�=G�>e��$p�@1aM����.��:edA�zn��Y�t��gS��\����y+�2`.ћ0�RS� �S�f8��������2�+,���Ί�F��n+Bg�|6���pg�&gI֏�/ճ��z����Ђ�]�}��$���A�����'̪絰F�����*����IK���PF�7�Ul"��C��7V�R�����C{"vh� endobj
-14 0 obj
-<<
-/Type/FontDescriptor
-/CapHeight 850
-/Ascent 850
-/Descent -200
-/FontBBox[-53 -251 1139 750]
-/FontName/SFIZFA+CMBX12
+24 0 obj <<
+/Type /Font
+/Subtype /Type1
+/Encoding 67 0 R
+/FirstChar 12
+/LastChar 121
+/Widths 68 0 R
+/BaseFont /CPLBKL+CMR8
+/FontDescriptor 22 0 R
+>> endobj
+22 0 obj <<
+/Ascent 694
+/CapHeight 683
+/Descent -194
+/FontName /CPLBKL+CMR8
/ItalicAngle 0
-/StemV 109
-/FontFile 13 0 R
+/StemV 76
+/XHeight 431
+/FontBBox [-36 -250 1070 750]
/Flags 4
->>
+/CharSet (/fi/comma/period/E/P/T/a/b/c/d/e/f/g/h/i/k/l/m/n/o/p/r/s/t/u/v/w/y)
+/FontFile 23 0 R
+>> endobj
+68 0 obj
+[590 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 295 0 295 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 723 0 0 0 0 0 0 0 0 0 0 723 0 0 0 767 0 0 0 0 0 0 0 0 0 0 0 0 531 590 472 590 472 325 531 590 295 0 561 295 885 590 531 590 0 414 419 413 590 561 767 0 561 ]
endobj
-13 0 obj
-<<
-/Filter[/FlateDecode]
-/Length1 1300
-/Length2 6348
-/Length3 533
-/Length 7162
+67 0 obj <<
+/Type /Encoding
+/Differences [ 0 /.notdef 12/fi 13/.notdef 44/comma 45/.notdef 46/period 47/.notdef 69/E 70/.notdef 80/P 81/.notdef 84/T 85/.notdef 97/a/b/c/d/e/f/g/h/i 106/.notdef 107/k/l/m/n/o/p 113/.notdef 114/r/s/t/u/v/w 120/.notdef 121/y 122/.notdef]
+>> endobj
+20 0 obj <<
+/Length1 786
+/Length2 1538
+/Length3 532
+/Length 2114
+/Filter /FlateDecode
>>
stream
-x��eX�m�I��%��r:D�D Arf����A:�CAI�nAB����������ֹ����\�z���P�5L�@��e-����D���:�-nQD�a-�������-�-�HX�m����� Ɛ�1����1��?���1��?���I� Ɔ�����73d���JcF �& �`����Q�
-C���9�f{��y�D���XN,�[&8�����-����/��y�^��Ж�k95o�0G��`�|"S���2�5�j-L+rĽ��@s�M������f�L�z�+V"V� `��,����JPj�O"ԾN��!�G�~c�j��#'ͥ�Tk�}�ӌ���~
-���%�����e�yΔR������s��%�k�?*'J��,�6��^��籊��2�x:�#���+�g�d���|�E����sE�c"���f�-%W���V'F��6�I����,7���7��pg�=���sׯ�KO�^u�`��$�b^�����-o�^��s�r~3�����A�����ÙeU�5[o`c~Q���`�! �,u��_h��v��Z�*�~r~������L�l�a"�e�/U|��P�:2��[.P�%&��c��X3�+
-�G\X������z��������Rm.��X�,]�ge��Hg����-���YHM�ޜo�����5j��2yW${��P$F_Oz,������f������-�dZ���]-�0'E��{�C���ڡ�▨t�j\y�W4ĝ���U���1��=o�w��:9��������[ګ1�*_y&g-s������D��1�����Ѓ��l >��
-KE��:3p���N�(���u���J���5v;^�A/=�&����!��Rw����7�[�M��*9��Bȴ�İbn���+�:]���}A�R�|�l�y�*r�sT�!�N�PP��yDK���/�>�������3Pp;�Y�=������oMj�0�"=�������'٦��G����f��f�XE.%��h�G���yI{�����9 ���S?�c7g���y�0���7
-��BhOxO�Y�:�)E�*?J�U����Djo��H[Tۧ��٧_�������%��W,3R��ػeP:@������-e�����/���Yw���mJk[��N`�ŔR5��>�Vp'��tR���S�=�s�_\
-]�70ԕ>�w�����iXK�L�-�k6���7��IT�m��]����x��r�58�T���kC['{��;��ڨ��J�6v9L������!^F�JկyyQ�G1|&�[51�O��Oc�S͕���]i���6�D�i��WEh�%���&c�,��d �?�M�I{~��B�p����}"��4[a"���]�a&�*�"�H����-MY�-�(������?f��`�w�͇":u:�^M�c�[�it�������\'�U`�h���r�c
-�:��d;��@X�����<\����4.�a��養���5ޟ��F��T��}'�'��[�������C�:ʐ+����y�&Z�p�9���o��GuP<s9�_��U��-�emk'-�E��H�4 ���O`���z�>;S[�Iv���Ȋ
-�C��G *��i��3|ML�[Z4/�����,i�W�����ލ�d�ϧ,Z����e�5qrh�Hș-��-Ac[��$���!Z�Z����W���'s̞ܳ�8�s5���6������e�đŋG���L��E�q�ɦ��J��0{�-�b�����S��<i��C-7��˰(�Dd��A,9/S��ǰ�z�F̧�����!c<��$�ICT���z��T�u>��P�h��ͺ%<p@ӗ���,br��<N�>����3Gg��t-�����_R���k<_
--�?;#hnG�N��ȓ,��ylF���Z3���>�+��[��s�1d�+Q�K���Ms���T���<S'�K��5Ԕ�^>kO`��(�Y���>���[�{_�)S�UBN?�⎶y����*&H�/>����}���8����G�Ս�=�cZ}�$�e%X�P羃7�!��0m�lNb������Sݪ̲�g�k�FY���� t�*�|헂��iKy��k���P1������I���uN<_���Â�{���V�}��NB�V�_���f�T~�>Vh-H�]5{Ne芳)i��E
-�X:��T3�Ok�'N"��.z��7�����h��������ƫ9(f�E��tw�R��k��F+��� Ҷȁ���Z��I��V�T�{`9L���Ĩ;�Vަ����Q�V"b��匍�r����:G��E�=����d5D��ҍV��$�a�,�-�Ur�h��T+)�G����ذZ�����h�z��/��X��П����$�IiF[r�]�4��'��Q���i�u"������Ĝ*ݓ����@4�_�b�y�_�_�����A�J�FMh���������p�o�l�N@p�w�Z^8�b�(%����
-����.���ۂl ���o)&@�,��:�wN3����}
-��{q���R�����#
-
-Ň�=\��m�s+���7�#���I��=�ֻ�cu������Co��>����-|�(����*�X���^
-��QGS�{��Έ��'4�6y �>���ގ�D��2Z�T�su�z�]pɶ��s��aӝ���ױ�چ�YȀGf��.��!B�dF�+�1�����4Z���B�M��@pt���KQ|���}jn
-;*-�MC5����P���I�[��z^���9i�;%܊0�d�0��Q��Q(Y���D-�t�B9�
-endstream
+x�Rk<T��TD�\ګl"13�1�ӦR�1�0ff
+kf0���]�~Ĩ�:�������\c�(ըD�����<��w}y���y�����0@g&�F��m+'��Y2S��̤! �HaBt.@H/���a�g��,�2�ҹ���6�L
+C�}��A
+����ѩd{(��/%*˙ R�$���(ߚ�ŶdI�$-��O�Bl"7���˲�����
+��e��o�r��
+��� ����ͅa�#h�
+Q�+���SM�C�v����R�Bݟ$��o�-��S��������V��^��� s��J�_,*9�kU^������%��[>Ī���[����W��l�;�$������'��TD;�VL50���_���p���'�5��lh�9d+�L����m�U���o�E�*=�����,^�Uy�k�wfL��o��'8��g\1�/�|k��jϬ������� 2$�&�� endobj
-17 0 obj
-<<
-/Type/FontDescriptor
-/CapHeight 850
-/Ascent 850
-/Descent -200
-/FontBBox[-251 -250 1009 969]
-/FontName/XCHVXE+CMR10
+21 0 obj <<
+/Type /Font
+/Subtype /Type1
+/Encoding 69 0 R
+/FirstChar 49
+/LastChar 52
+/Widths 70 0 R
+/BaseFont /RMDKMR+CMR6
+/FontDescriptor 19 0 R
+>> endobj
+19 0 obj <<
+/Ascent 694
+/CapHeight 683
+/Descent -194
+/FontName /RMDKMR+CMR6
/ItalicAngle 0
-/StemV 69
-/FontFile 16 0 R
+/StemV 83
+/XHeight 431
+/FontBBox [-20 -250 1193 750]
/Flags 4
->>
+/CharSet (/one/two/three/four)
+/FontFile 20 0 R
+>> endobj
+70 0 obj
+[611 611 611 611 ]
endobj
-16 0 obj
-<<
-/Filter[/FlateDecode]
-/Length1 1867
-/Length2 13928
-/Length3 533
-/Length 14981
+69 0 obj <<
+/Type /Encoding
+/Differences [ 0 /.notdef 49/one/two/three/four 53/.notdef]
+>> endobj
+17 0 obj <<
+/Length1 786
+/Length2 1532
+/Length3 532
+/Length 2109
+/Filter /FlateDecode
>>
stream
-x��eT�϶���N�qww
-��ww���ww��ι�����{?���rbEezac[C����=33@T�����I��\�t2��:�x-������W���[kk�������O���B\-�8�T�tb���ކ:x�|b���44R@�G܄w��V/1�̌l3.���^�N�8J�E���Zd�}PH��;;J�٬��j֍펎`�1�?#ŧق��mr�k�D�(��3�ʭ#9���ŷ-��r�a`���*������>���֬ ZcJX)�(�.��o��pO��-f��=|ݷ�P����RyL�)gx�=�w*z�!xN�,g���쒁-�1Q� U���V�Kb�LĂ��d��L��0�j��92"�iQ��锡�nEx������`/�H�_טEra�N7�Ռ��D�u��e~n�.<�s��+���B��3���=�"C�����"��X��|Bj�(D�mZ_�C�l�0��s���ȉ(߂O|Y��1����m2�O��*)�������N�`����7�Xe�v��:w5W�Q�Ϧ
-�����n����B��a@I���{���]�t-Vf�(B>[��d�s�Y������<�����,X�����xgXQ���S��"#�D�UE'��kg�ET#�
-�ș���������,���G+z���Z��̾y�uv�?�1'l��
-Ϗ�����@�k�T�hu堘/7AZ#m+���>A{\߹�D\<�0���Щ1��V�-��邆9r����.��� Kǎ�Б��$�"p��������S�-py4��-I����A�����C9�d�h���4 e�"h�J
-�g�]�y���� |�F��2�x�ۖ�ʳ�U����W����������d��~ʶ(���0���� ,�U%t�,,�*�{]N
-X���5�/���XE����%�`bҟd���g�u=�����2��x�75��D�� ��v�ߣ�d���0�F�YG�"�6 OHS�<��6���1�2���j=������
-]^��d���ͯ�Y��s;w�{u��ʰY��DV���������ǧ���h���q�ǟ9:������
-��b����)�ݵ���r�vs���aL�)�z;�^[1�����8<�Y��*��uݵ%ɓ!tBA4X���p9������y�^���G����
-xn���Y����:X NPkYy����?e��k�\����+Vnҋsg�#��x��Zv���Et(>����^n":��(rޜ���4����+��8�a�˃}�B��k�Qݾ�~��6i�H@/��Q
-�������rG����E�܇g�/��)�%��>J���(%�QG�xbx�z���|�������P�i���i������^C�|z�8�����IհL�D�l(���k�z���nY��2�w`����v����Saԃ>�d����8?��[#�����K
-1�H���ݰ&�`������݄&�J������,G8k�9��׆Xi�B����Z�D���u�nIa�];d�h��@6�6>R���Q�'�V���ej��{��1<~��=�g�-涾�f�z��9�4C�;:��կ���.ȗ9m����K0��Op+H����1U���*X+�s�v������V���!9���~mV���U
-w�dհ�X*�����@�\l�'В`��K��|���
-�Ts8�K��o*&������:-���Pw�a-����bV�e3b�!�(\[,UQpBR��h�PZ�&s.=�����Ağx�F:�א��d�����c��AwO^��K�PE��ȱ�)>��Z総��>���wAo�oX(��+���1�t����J\$����f#v��f%����2��V��3�C�9a���.�O`0L��ե�u������U�Z���qn%Oҍ��� /����:o�_�5���Ő�?���7�)�<�ܠ���79�[���� ym���_!6-$I�J[�erX�� p����� [
-��(/�8�we�u��<fO� V��������-ȫ�tA��I?Af (k�|���N͏F�G��"���/������o��.8`2Ƈ��7S(oJ!K˷*�sN(N�H.��Z�y_M-F�?��&��;{�u
-�Q��ëiӂ��N>��5֊rNl.a�������6�t��@т�3;�*��;��,4���������K�7�n�{�nJ(�����.�����5X%��\�����������67�`t5��.�H�Cm����$x��{IJ�l5����JΫLF�?/.E�d�ո��N{.��9?�ㆃ@������=�m��v��k�3���C������� *C�VnIff�a����4a���N�l��/�j��֠���T��Q\�����ܿC��N�+�(hzQ�5nG�G���3)��������)���cD����ݩӎ|��F&+f���|s�IL�6CK�8
-c�F���ȩmo'G]$��Q�c����6��������k�Kx����S���\â4�\����b�� \�7�`��V����Ozo?�����Kr�N����?�l�zU+��Z}��d�V��Kjy
-�z:RL��v&7a��G3�{0ٔ½����a��]hþ�v��>3�U�q-��k-�&��;���\G3yfݴ�sE�o��)��TU�z���+.GFg��g IC[h~ F��T�Y�{��6@�N�?�@�X3ܘ�����d��[�`5��'����a��KS4�X�):����y�n�K�u��4��B_�Y��Ah���}���!92�7�ġ�cHY�~�ʞ���i��3��g�Ϫ���O��2H�� ��e�B}�@��-2�j�m�@e����(��8>ۯ��}���6��8:�wH~�f#���V5���U��UZ7�A74�
-��&g5b��U1�*�obL4*8��������y[��f��̠O�R�#�$��|ɭN�zBg>6Uz�~OI����k��Ƕ������6�4�*��m�I�]�>#�)����j!��o����
-㤭��t���X��o�N�]���~iT�d�rh���nӗ����-�-��!e��"��wZ�"z@@��D��Wk(��f�S�+�k^�%QS�MDž8Wݎ/"��~ɦ��Z�FR��̟�L���2���2����js��PΆPs��$)hȿ�GiK�%`m�H��Pjj�yg2Gr]+�BqT�ys�����������VF�|��I��m��$,䎘?����t'�d���v��{�͕m�ttW��Q(�x���B}]3�D��-yOJ����-�,%k���w<{���@��1@M��o���.PL����1VПiԧ��YӁ�b�ڡ3}pT�<*�S)����F��|�n'����գ�B�F���Ym��4_����E#ZJr��X[p�$p�#`���L��JdB��iS�~�̮%{}�: �:�����D�=�6����� �:�� ����!�H�8�>��Z&:�Sf>$'�5�![H*���`���R�q��?d#�'���L�����2�?�-DH� �b?�v'��}㭾8ؐ������wHM���0��)Ql�����$�����,>�����q.������2��������M��#��(c��ѦI��(���W��F������Zv�i�~"���8�����Z�{G �F��4<h!u��k���t�?P�5����5�&,�����Js����s�,�|>�z��s"�Kl���f��ԿZ���T%��No�<����AA��DX�B���x��-��]��u![�}Ի��W;���������x�'m��
-��pN8,T��h�����t���d��o���M�a�d�{u[����[��RBII��w� S��u���pqPq%��-J}�Nii|U1��z#�+�鼤�v�'s+l������[�-n�_��U)um�R�O��a�?�c\��G�j�(�U�rN�`��V����K��o$k�%ण��J�'�g�L�Qe!��Ov���5Qwrƾ�Շ�5���*.�JO۹ߒӇ$�3�"TӰz ɨ�W%.�/�L��7��o��@���q���� !��wPu��X�-Xp�2�Dž��8�K�r5�ܽ�~lp�eU.�d����L7R=!�0���mtL WZ�=���@2LLQ�F�縩���W]:E
-�(�Tx�m돟�<ջ�e�hc]?�u���\������YM,��Yi���C�">������p�+^�&�9�����"���B����Nߩfo������C��D
-��5��[���똹D�=��KB��(��|�*�گ�W�<�Qm���+�:
-���0�ofz�#ʞ��"ǑT�j��_(L����RR����4X3�W/�v�R�;6�3�����A���J@��I�ǪП��j�X��fz��9���������x����<��BF��C�x43�H.��A-����bY�$�߰��c}4N���z5�"�~<�l?7�kP$߷Y����'�a���ք��Ҏ��d0��A������Ȍ���<���լ�p�-�
-�� Yy���;D~��x������r䣵�%��o�z�����dJ�}��i��H�$�tN�*[���[�D������_��z��t��~��#���@1)a��=�������F7Ǎce2��-����-�����L��=FԿ���8���c�X*d���Ĭe����J������H�����0�-�5v!����VH ��9�(�?�(C�`����&���5�U��(���1�O����:�U��J�u��Y��7�(u�A���j�T�������eφ�'=S<�;�'/��0�����;B�F�`Qw8��lmF�]��S�/�e����f�j�A� �����F�q�S
-��~��i�ô�L'*�{��]rvB�R.�b���+���j��B�R��0�%p���f�� a��=�v�E�����2?�������l��Q����!ߛ|=T�KAQr��o� �M?�}��aت�L����������g��9���o7��[�8�@�@H��/;����-���7A_O��vx�� ��ʁ�5͎���OR �8o��^hȀ�Cw���(ќ��
-�uF�����/.�Y�����ZK���V@C�ܞ��L�Y���l����!ڒ������oh!���c2����i��l�~)�-J�����Z%礖�p
-�?�
-�л[^F��^�<�endstream
+x�R{<������hsr{]&��K��QF�uM�.c�3���DjS"���agʢ�KIjZ�R4���s��������!���P(H��k�
+���06h��Rx����+�Q Oh�L������f�+'�
+S_���$��O]
+�����8|�< �I���*W6�1؊q�.�"�)�p@`�i�+�(6���t�[zL[{+�|5���Lj�������ii��+y���:ٯ��|��0M����W5of����OsV��ET:�ֹ��k�j��e��%�1�����8�l[i�@6;�ϧO�>�Yl��8����Cn����(��Dzt��o<`�S�����w����w�fkE6z��$����O?Y���G>+<����q���5���u��`Wן\�����#�}���;s�����o��*~��G���+����/,>v^���9Pk��.^�l^�t`c�qp�·>�m�#�Z+����^-������T&H��{�\�ndstream
endobj
-20 0 obj
-<<
-/Type/FontDescriptor
-/CapHeight 850
-/Ascent 850
-/Descent -200
-/FontBBox[-27 -250 1122 750]
-/FontName/GCOOBE+CMR7
+18 0 obj <<
+/Type /Font
+/Subtype /Type1
+/Encoding 71 0 R
+/FirstChar 49
+/LastChar 52
+/Widths 72 0 R
+/BaseFont /WRFXWO+CMR7
+/FontDescriptor 16 0 R
+>> endobj
+16 0 obj <<
+/Ascent 694
+/CapHeight 683
+/Descent -194
+/FontName /WRFXWO+CMR7
/ItalicAngle 0
/StemV 79
-/FontFile 19 0 R
+/XHeight 431
+/FontBBox [-27 -250 1122 750]
/Flags 4
->>
+/CharSet (/one/two/three/four)
+/FontFile 17 0 R
+>> endobj
+72 0 obj
+[569 569 569 569 ]
endobj
-19 0 obj
-<<
-/Filter[/FlateDecode]
-/Length1 845
-/Length2 2042
-/Length3 533
-/Length 2661
+71 0 obj <<
+/Type /Encoding
+/Differences [ 0 /.notdef 49/one/two/three/four 53/.notdef]
+>> endobj
+14 0 obj <<
+/Length1 1842
+/Length2 13515
+/Length3 532
+/Length 14535
+/Filter /FlateDecode
>>
stream
-x��y<�ǕD�Jv�~YJd6Ƙ!ːB4���&�hf,C��
-�$��-�d�5��J��Y��tF�Ϲ����~��|����d�hà���rpF(��m��l
-�nMd�X-�@�J����-[F�������RBO8G����g�d
-]�-�!�?5B��-���$HV�"e��������js��(=y4.M��v��
-�u��ǟ�0������3�G��WI`[�8��T��%��g�^*� ��o������!���\��]:pj�FK��O���x5��S����E��MZ�v��d�Dט�[+Ռ?\���M�k�Sb��#�����4#졜m��T�ơ��kVF��)&�I�1���F�{l��u�%}'s~h�KE"�w��셌��N&p���� i([������6��(���-9��)8���M˥�(�h�Z��(5�֕�ϴI!إ�$�KT5x��~�p��4hO�q�Wn�}E7gѺt;vh��:��<��B����!Z���!/Z��\d;*eom��\p����5k&Jn����_�S�~vM�tD-E�����-���#O�߽�&�9���L*c�M�r��P|qd�!����5�aGo��b��@�����2���kf��T�V�ك�·,L�|��]��ɐĊ�
-?ӊ����ȿ?F�}��Ҋ�W����A�Y:-y��J�����r����������śp����Nd;�����
-i���[f���Wm�8y��q����`���f��,��SO����|tF�D�J6h�?�t��z�i�f*�ۣy�i>����-�i�l�&����!6'[߇k_g-u��Ƃ�?���$�}��_c�z�-}���%���������g�p����A���;���&垵�9��v�_N��ڦ=W0jV̨n&ܰݾ��! Y�
-7J�@ĬBZ���F�',��*���w���.Ĝ�nS�m�7�OA��r_d�Y�xr�}�)���]k��i�&լ�����;�����rU0QK��9��W4E�j0H��uT���Ϝ��7p�G��k��W����%�[z�-�ٯ�)�l��#¡�i�$E\9�t��I}q�
-�7=#0�}��k�PG�؉�stV���Ҽ⺒h��ȯ�����'�ػ�����W�f^��~��Wsbz����Rp����>��a?�5�ySo�����T��u5��y�����:�������E�ފs{�?]�~����Y���b�f�����T�@��_�؝��{�����!��ϨQL���+�Ђ�6_�
-(� �/8��Z������HT��3hDf-endstream
+x��eT]Ͷh�kp����=��{�.�xpwo�r������9F�Q5k�*(�+���l�i�+��t��ۙ���+i�0�#��<�d�z� ��̌�-�1���+����x�Ћ�{B�^=3�REz������]�4�]?��gSRm6 3i���҂l���h�N|��!j���d���xx�0�=���L�8V)э�O��}^b�Yqsi�Sa�!椑��q������1l�g����Jwu���嫦썝��r}��DzQ�]�������r|r����iGu�U̦���M�1g���Bj�_����z�ҷ���>~%����j�� �Am5Ύ'�Z�Ɍ�B��
+����^ȭ���~����ǽ��;I$��9�/�%L%}Q�ZB���PY6��������������n�����Z�c�ܠ�k��ؿ-��nT��cn���H�==����j�K�����F��o�`���
+Ü����@�#��Z�CP.E�A�(��y�"��=�]vW>:D��/��˻�j�`G�[��p����O��e�z2رt�6�}\� ��kW�5����f@��m�Z���"���9#$U��#�}��:�.Q�JI��3�.���Kl<X��V�.�5���8Ü�L7�w�p����"���6�����/�+�7WlV(&ɐ�����.�[E�7Z��>�-�rmk,�h�堦{���T���1{��In�d�O&-i�A=�W��.���������B�^x���{X$��rqö���Q�<�����
+�����:uW��1�[���{�C`V
+��i���:���b�߫�]���?G|q��8cJ�{a|�v�,F2�ޔ$��lPJ������Ub�Q��ky1��F;��ja���&|�d�o��gg��q��1���|�v�����Ų�EG�Qߟ���Јe�>�Xu(���N�nH�O��0����sY�9�� z5����'J��!e��������ͺT-������
+u� w������0xBv��&
+qʜ����ע&XT�xe2�ąLD�4|C����e�$��zඋ@�YO7
+W1;�3Z��4 �t �C[�~$��|l�d�Y4:X����F�;t���������� 5�G��$`�������������+m!��[�4�1mӤ����i���J��:��yxс������vM�$��^���.e67c�e��+7d~;�+���"�x�<�E�E)�����2�sK�l����c,1)���u����r��
+6��zTv$�+��ɦ%���#ض��!������ou)�9����
+�#�,CNʤ.jZN!7X����qN���df_�JO�����[��/�*��ou���f�Z��K�G��v�;��-;N�DX����~`S���*C/���E$�����I}�,rP8r�Bm�sdL�����i����{��V+�6,�zєL�)��˒�|L�X�-�S<#�n2re����Y�B@g���XW��b%F-��k(�R��E�������T��k7�Kd���k��5�~�2�cȆ��/��z�c��8r�>!���zz�Q~p,��X3�fߺ�
+K�R��)�u����~�)��ɽ����鉙�ڭ�̞+N�|�<����5�,Ҹj�y�0�&�Ai�.�� P��̢@�_�~�?�\ӛRq���w�Z�+w��H�8y��Џe)�kѩ��4���u�·s���L���M��=T�T�U���~C�ݓBD��y(�"~�ϒ�����#W���8���q%tq��Љ�d��+��9�uQ+{\t�d��Dqšpb�lf����"&��r&!���Ķ�����V��x؆i"�i$�v�B`��tW�0���"-)�,5O��2(l���������;�K�E�`�
+�z=�ܡT1&�뭳�F��[v`ֱ��<
+�@u�k�$.e�V�a���ʨ���r,�\��0��GoW��- <Ƌ&���� E9o���L&/xt�b�t�v@�������V�nNC)?��ʹ��+=|?빢{ʅ���1||mp,H��@-��ADD�����Z��_E;���;��{y\�Z�'����y���0\\LM�l+���L���:�[�����И)�l����B�ޘ"����̙#��q��:ל`)H<��WX���i���k{���jV��j>]��O
+�p!��W�����ܜ���A%' L���GǕ�'���Jܡ�z���W��֥s%װ)l����2��+8��
+O���z�p/����*�X`̑kp� "u�w�G.(~�f���9`@C+f�1U���_*ڊx�++c�Y`_Z �c9B��T> ���!�q�}�-�1Qa0����#�<�_dI�+�x�����7��ĨyB���:n0i�S �Z^傥�u��;5øb�S�A�7 P4���U����X::�8�ܘ�*� '��0�>�7i)/
+���D�@z�X�M��U���������-S�ݑ��^�b���Lbzs��}-M��'u���ϊO�r9�{U��s�%4>���V�2�&�]]Q?��d�����82��RE/݅XzY����C��ŷ�C������3y�ΒK��+�i`6T�NL���۴D������I*,!�~�Ҩ4����"Œ�B;郯��,d��
+y)V�����w��2[ʢ�'^�a�+��ƽ;K#Hy�����L�D��~��U�#��/��N�J[sa*��Md�fX�����0�����S��Ld���ۡ�)�2�J)[I|9�u쎤L�7�&z�VAJX_���vF3@⻃�m�����⛙����Dt�� Cy����CN�WB�b�==�G�T �FDM��@chyuU/�W���2���m/
+sb�����kp
+D��=��vV���=����:b8`:Z���9����uHKbU5:#�":%]t�������+;����>"������
+�T2��!᠌N��v3pI�͎t���p��Z}
+dV����*�+L����.��<�%����m1��*;h��D�F�#�����H �U��n�ﱤv���sC(��אG��
++�\�Ƃo�����m�/n�^��Fu��O��cU����I4����
+���+�ȡ2vꀲ�3��~��}�:��x�X,���﵄A)p1D��īTSbH���Ut���������� ��;�{#��ޫHO+�Έi��o�n�)UY"Q2l�tq��ˮW�@��*Sz��G������4<m����dέj<0)���~��,����������Jn�����10���GQzσ�����BJ�l����I�ל��4���^�b�p�??�P 8�2O���@?9�&F����*qh(�j
+���G����{��|�"rwM�I��$��0�t��B�Ua��C��qV�45���#���-�)�>��دl*Y��h6�.������J�[r��W���
+�,ej|�m�ۄ{�=5�����wQ���w��n��W��
+��:��KA��Aw��d�۾�r�_��c��Ң"�M�p[
+��7_�����\����֫����\ȥ�ʀI�{��W8Tv��7:������[�����-������[���j���� �d��+V��I/�J(�c~����0���q��r���<���(De$k8���2M�z����s������W
+��� �Ok��+�Z��_�����X�w��c(�*8� K��t�����V̕�j���1E<��k�\LP�l'�:BA�D����5�[MG�o�f�����u�7�r."z��e�t�Qrk�ژ܂U�D��K=��+���ܣt��X�"�(���?Ci/Y��Ú�KXM�����*�>KF.�P�[7�RB�x�p
+�s�Si4qG��Y;7�M#(x��w��M����@s����%wN�i��ϖ����נ��TQp�K��������x�na�)wĪ��-���0�y��tPǤI���������E!������0�ѝAd��;bU��`S��~���bxi��sG;� �o� 2w\f���o?��|)��T�ՠ~7k�W��5[4�'�[?��|9����nj{�� {�b��m�v�<�۠���ռ-���M���ww
+UfE����Uv������Y� ����O�hِ�Gt-����,��^,��p��kݪ���'�J�=��<�|t�+y\W��FOm��(��-D��s�}�&}C�3����9l؆�.��;7O�.����̳��+��D�=-o��B��t�?��'����a��51��Y
+�����b���8a��&Ֆ��Ͱ�0E��Tּ�L|<F
++�1����|�4ӏ�jq���+8}�R���v�d_ɚ�{��[�?":ˁ�Ar8����
+�|�C�-1$ׁ#.��o�����w�>�?ٵg�A�%����#�U�C*>��������|�P**_n5���mB�x�ר8<J~
+��
+6Y���ܾ_� 1\�a�:<]��������p���5����b���(%
+w�DGo���r@]/����Cj�W��Hy�����G���;)�hI7�haxu�i�S�l\�_��XL�� �,A���NH]�ݷ�����}m��j���b��a������_����@sc��L�9{���r�ᦩp o �mVT��b��Q�>����a��}h�-����F��������K_���a�t�S�;���bޙO�y��N�N��Q`��T�����ݷ;C�V�l�{�������sa�F��"����v$���39�~@�l��/���ǽ�����D9���q���eZ��+���y�dc3�h����`) Ԩ���g���Tߓ|y��:,�YfX�q,ز��1�O�-*��λ�S��,z�(��[�'��u�m�jK�R)��U0�Ы���O�N�1���/J��
+.��)��]��yL��x������f�n6���ď����Iۏ�Ču�A�����"���a��Q/+��fY���CZ)���e"_�]Qx�@�U�
+Y�S�rۣ���$U��sQ���p0�W��3�ʾC�Eɾ� {�W��w"�}�G�t����1�v���M��g1�}i�������t�C�r2_n}��S��,YM���i(�&���;�]�;����C�����tIO.Wjx�����:e�*�X�;]�{���ڕ��7LM�>,Q�M��Av����8#?��)���7N�F��g��a:Y�e��K�bG��'���F8bJ(C���n��������L��W�<��S���)R�:�� #�p�����W$�L�`*��y�[L�����I#<gC��wb�g�ŨT�>��9EQ�>\K�O���T����H��S�BL��6zͷ�#s�KLЦ|��B�`Ժ�����$
+"2�S��`���z=2�S����2�O����+��*�ڧ/m�gD�C����z�z'u�L�{�Ҝ��=P��vF��TK*=����{�����wЯ����;��+���@���=�Q����u\l1��=h)y@�`��¬���).jgT-Uy>/O�ˠ��PX����~m/�+f�HQc��헝&h�`B�i��U����aO��/��5���/e�c'�tG1P���N,/*�ҙR`7v��яlzG�����M��k�J�$C}�>�"F��\��cX�Kj��-�e�zu�ئG��.�p�>|=jZ�:��<ʸx���,��L[���)�$ko�g<��ryW��k��vm��ЗІ���땆�TYS�0���mm����T���V@�F�`�M!����{ t+q��5e����X��yAA�}�Е7�aQHI����_[&����FTTϳ3C��k�e��Y��$�4�x�%��h釨��n�jK��d�����$[�%�}_.P������ �?��h�gc��ndobj
-23 0 obj
-<<
-/Type/FontDescriptor
-/CapHeight 850
-/Ascent 850
-/Descent -200
-/FontBBox[-20 -250 1193 750]
-/FontName/DMIVYN+CMR6
+15 0 obj <<
+/Type /Font
+/Subtype /Type1
+/Encoding 73 0 R
+/FirstChar 11
+/LastChar 122
+/Widths 74 0 R
+/BaseFont /EWBXHF+CMR10
+/FontDescriptor 13 0 R
+>> endobj
+13 0 obj <<
+/Ascent 694
+/CapHeight 683
+/Descent -194
+/FontName /EWBXHF+CMR10
/ItalicAngle 0
-/StemV 83
-/FontFile 22 0 R
+/StemV 69
+/XHeight 431
+/FontBBox [-251 -250 1009 969]
/Flags 4
->>
+/CharSet (/ff/fi/ffi/quotedblright/quoteright/parenleft/parenright/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/question/A/B/C/D/E/F/G/H/I/J/K/N/O/P/R/S/T/U/W/Y/quotedblleft/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z)
+/FontFile 14 0 R
+>> endobj
+74 0 obj
+[583 556 0 833 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 500 0 0 0 0 278 389 389 0 0 278 333 278 500 500 500 500 500 500 500 500 500 500 500 278 0 0 0 0 472 0 750 708 722 764 681 653 785 750 361 514 778 0 0 750 778 681 0 736 556 722 750 0 1028 0 750 0 0 500 0 0 0 0 500 556 444 556 444 306 500 556 278 306 528 278 833 556 500 556 528 392 394 389 556 528 722 528 528 444 ]
endobj
-22 0 obj
-<<
-/Filter[/FlateDecode]
-/Length1 845
-/Length2 2056
-/Length3 533
-/Length 2671
+73 0 obj <<
+/Type /Encoding
+/Differences [ 0 /.notdef 11/ff/fi 13/.notdef 14/ffi 15/.notdef 34/quotedblright 35/.notdef 39/quoteright/parenleft/parenright 42/.notdef 44/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon 59/.notdef 63/question 64/.notdef 65/A/B/C/D/E/F/G/H/I/J/K 76/.notdef 78/N/O/P 81/.notdef 82/R/S/T/U 86/.notdef 87/W 88/.notdef 89/Y 90/.notdef 92/quotedblleft 93/.notdef 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z 123/.notdef]
+>> endobj
+11 0 obj <<
+/Length1 1292
+/Length2 6411
+/Length3 532
+/Length 7207
+/Filter /FlateDecode
>>
stream
-x��{8T�5%9DB�[�w�w��3��5c�1C(��%�P��%j#��-�FIR��[.g����;����{�]4M�_Њ���`n��+��F���)]���x���^��P$��-�i����U�Q��9��unj;�LO�A��w�6"º�b%��Ȗ��:l}N3@�G�<GV%�
-���f��S���lS}Ԉ�:�����W�[������|��r��n"����ɞjY>���T����^��:rL��pj���(�ʇ§q��U�c���{D"�) �B�C�5��*��;�(X[���\�Rw�$
-�Us������OV��:��[ J��<-<,w5M\iI*�kmטh�h@M<���c���ZN��C#�O�n��d��I>�N4n��-U?�����}�v�����%z����D��<-�;�endstream
+x��e\�]�DJRz$��;e�``����CJT:%�Q@BJD�AJ$�iA��[��<����w�����nh"�E9�QHo!�0H���l ��EH��U��7�T{� @� *+�����k
+�5������+�a�`X'�7L�B���8"�P��4@�����j�����8D 錀D��{��aPC�7��x��Ð���m���jƆ��3Ͽ
+���i��e���A��4�`#","�b�����#�BB++J��+�o�c4jKQ�Zߖ��ݨM��iZ(nsq��e*���4�S���d�s�xw(kxs��<�����ZK��V��Ðkr=VHk�`����Ki���9��� n�jO��O�����P� Ɉ���Z)j��_����P�D;��D�m��,-~9�tOݯQ{wD��'�L�s �a!;��0q&���4_��b�k\�¥���S��P��5s����P�5��kK�����ƛ�����[�}����/�.��;g����G"Êy^X|R�^�oڡ���������X�/@h(�|f���%� {�i62������t���&3��ǃ5�#��S�-�9�+A�R��� ۄ��6����H�8d���*
+���0������+m��;z�g�7i;��3�M���+��&I����������΅�|J�"��d4lr��u�Zg���!�B2�@�Xk����GCNl�u-^���~�p�*uD~^EQY��>����_��]���C��3y����|a�M�v!w|hm ����=tJ���Wy����rSO�����$�=ʾ+����Y�`@�<�rF�?�+4����TR{�>Z�s&P�f�='@�O0d����:�uL~��$��q��CB��y��\�4�W��㾱��Ì���%0�)��t���uQ&Ơ���u#+S�Kj��w��\��1'�+���s�tܵME,�������s�]���m�����457١MX����t�J�6�Q/J�OtmvR*_7�T<���������Gφ_5/+
+5�L&��C���/���=���&�ڀ���+�:3lK\�q�× �k����(�"T�R���(�r�d�������V��o�5�2�v�Zb���
+~���B���?�yH�'��Ê(
+np� I�Oj��+��ù�:=�x��E��#gsK#I8ܥ#�v4ny<��lx��<����.�s<Z���G~����/*nG������_
+�kvn���?�s�x@H�>�}��e
+�+B�B�s@��o�o>���q�75�n
+z��j>?��w�1��R���q)�Ӻ8��lf��]ǼG:�_�2��֏�o�S���Jf^T��ۨ����ó���wؽr��EԼhMC��8-���*Bˎ~�U���#DT�2����&��N�i̭f,��B�wl"O�����,��b
+B��z����t*L��wFcŇΟy�������(Uk)+ nǘY�1���� �6�]���"�(�IuՒOl}dGQ0�X;���_��9��%��ʜ�H����$���=�����W�����q:����d��esA
+vGT�J{�������>P�_}����BEWq0R���̸���{�g3y��t�^v��8�^h� �{����gy�b57�N����HC'��#q�g�������W��lX��ieݵ^4���+��KM����>���Sj��Z:Fs��i���y.�M�ҤAv�����]����Z9�8��5 �81Q%��L�Q���O��:r�����"Q�4*1%��Y.�lv�E����GG8��'(~�<����0�*���L�3�����H�!�G��G�U4s����X�`�4U��z=�|�*��+��>S*a���h����8r�H�c8���� QK����ո�g�}|��v���T\�Ut�͒�h����o7��R�Iy`�>���+���3a��u
+!���r����l��k6O~`�?����U���տ�X���pJF��n����<�b�������>��.���6߷���{�"&�����p����sK�E,ǎw��[ބ��e��c�C�.�u�B�
+���xӄ����e�8RF�\Htt^=��4,llO��Ys��*���1�;aR����jsu2Q��K�
+|寭!�9vv���p�����&G�76G��������1���/I��E�¬�
+y6E�bp�1?�Ϛ���nGͧ�+��Os��cF�g�C�Z��S��C�@�1��gb�r�4S��,��)5��h����q���kא]*�n�Y�l�&a�=��K�͓�Dc�;�c�E���G��H<M`����[8N{*5�*��oI7�
+�N=A��6Yٱ5�|�C�d
+l�ѐ1��^���t}'��Q�܋�w�U�_"f��rOey�������8=q!'mEa����:���US˓��d�Y8KH�E���aH�G����ggc}�WV����#J#Zo���Ty{8gZR~��+I�؝71I�^����%ח�����%���8�2�q�
+�+�����Y1��O����&��b`���t��|�ƕ>15����eh���i旘���_��b�(��o�e��sb9x�h������Y�C��G��s��|�]m������J��Xhfx�-7T�K2���"���������I��Ӽ�n��!��G_|��ԉ��\�Y���#'[E����y���1̦K!� �MYZ���5�7���T��q�O�hk/t���i����"�������+�Z�⬖"�xW�S$O������#��o*�th����7j<~��v&bҳO�#��>U�\`��~V��I_ѡ8�'r��q.�5`��+�
++��P�2�:���c��ڦ��o�t?�H�S���N������J��
+ZZ8�
+��#�T�1|z�ɸplwg6��$�+,]������F��/��K��9'@���n;C�iQ��w�d���R��\#�H"k���Q�=�ǘ���H9����q m��F���`N�Jl*sVb���ӌ��sL�c�m"��,��4ڌ���g��]%1S�3��k��(̨��Ga�o �M�����se�*�6�p���uC�
+(��rɓ0����3��|u�`�w҈4�8e3�'H�$�\���N�$E�r�����F[E/��pڵ�,u�n"�jtj7�}_ܜ�H���l�?T��D���Q��T���/�2�L2v:X/��֗
+���> ���+X�+βo3)͙��}�[��v#�q�(c�Q�����l�Z�>\cA
+n-�30��;ۚ
+]�_7�������P���f>�:�ʯ���Эf�lD_����~}T�M}'@y������U#��n�+�������.��87�sk0-�[l�]�����E���+a�%ŧu8�m�2�+��}���%�2���5roAZ.>��46��S&O��Z��!��
+��c�d2ŷĮ�K}�i�������Y�O���4&Q��+O�K|3�5[z�L�_ �w�uC;���� ��M [{���L׆���l�<�?P�)p��t;v^� W����'
+@00�����/
+�endstream
endobj
-26 0 obj
-<<
-/Type/FontDescriptor
-/CapHeight 850
-/Ascent 850
-/Descent -200
-/FontBBox[-36 -250 1070 750]
-/FontName/MAMUVX+CMR8
+12 0 obj <<
+/Type /Font
+/Subtype /Type1
+/Encoding 75 0 R
+/FirstChar 45
+/LastChar 120
+/Widths 76 0 R
+/BaseFont /PERPTG+CMBX12
+/FontDescriptor 10 0 R
+>> endobj
+10 0 obj <<
+/Ascent 694
+/CapHeight 686
+/Descent -194
+/FontName /PERPTG+CMBX12
/ItalicAngle 0
-/StemV 76
-/FontFile 25 0 R
+/StemV 109
+/XHeight 444
+/FontBBox [-53 -251 1139 750]
/Flags 4
->>
+/CharSet (/hyphen/one/two/three/four/five/six/seven/eight/colon/A/E/I/K/T/W/a/b/c/d/e/g/h/i/k/l/m/n/o/p/r/s/t/u/v/w/x)
+/FontFile 11 0 R
+>> endobj
+76 0 obj
+[375 0 0 0 562 562 562 562 562 562 562 562 0 312 0 0 0 0 0 0 850 0 0 0 738 0 0 0 419 0 881 0 0 0 0 0 0 0 0 782 0 0 1162 0 0 0 0 0 0 0 0 0 547 625 500 625 513 0 562 625 312 0 594 312 937 625 562 625 0 459 444 437 625 594 812 594 ]
endobj
-25 0 obj
-<<
-/Filter[/FlateDecode]
-/Length1 1289
-/Length2 6434
-/Length3 533
-/Length 7245
+75 0 obj <<
+/Type /Encoding
+/Differences [ 0 /.notdef 45/hyphen 46/.notdef 49/one/two/three/four/five/six/seven/eight 57/.notdef 58/colon 59/.notdef 65/A 66/.notdef 69/E 70/.notdef 73/I 74/.notdef 75/K 76/.notdef 84/T 85/.notdef 87/W 88/.notdef 97/a/b/c/d/e 102/.notdef 103/g/h/i 106/.notdef 107/k/l/m/n/o/p 113/.notdef 114/r/s/t/u/v/w/x 121/.notdef]
+>> endobj
+8 0 obj <<
+/Length1 1065
+/Length2 4472
+/Length3 532
+/Length 5179
+/Filter /FlateDecode
>>
stream
-x��e\T�iA�k��a�.������RR���Q�� QZARD@)��w|������̵���9Xu��ew7PP@P
--� 98 H�pwS��`R-}-�7��S����-\�@���Dk��?����_�?}�?���5|�@���B���(����B�������C7��`0X\�������!0��Nzg������)��\�y��0gݓ���g������߭�����JU���h6��ۭ��Y�8�/Dyt�ҵ\Ύu/���4nFou`H�*�Y����؛Q��Q�>�6<��mRu4i(m.|-��^Z���'�Tz@�GY�yd3��� �� �!�'"%��ZYmן��Q
-"�h(�+�ܱx�n���d�l|?��h���m��\�+ͦ���/�
-���k�3P�7�k[�/����5\�V��y�����z�G�������`6��
- �V#z���q��+�D8)2���#P��9��U������_�l�bM���!�l�0Q��# oKQ�HC��+�_=���m�ұ���D����X�Z`Ã-�_(/]�Xؘ1�c䯢Ǽ[<ZV��QrJaN��d�\J�����˙�mqf�`���3S~�+���ΆRf8'C����J��+�X-�P�!�)�R:c,��ЮDw!�m�P�����p
-�q�/�\���m��I��PG�����8gx}gQ�1��C�j���A��yĵ����*�����6�X�-&�fr+X �|uV%���Ⴢ��9˭2�p�� ����)�%�u!-#pc��Y�Zj�R&�����,/D�{.�p[��7`
-sH�����֡�;��@o(����$�0���/;G�oZx�K�a�VUn��)e�s���J"ג��|M�u��L�����Xk�i>�?~��˹T�0
-��er�B�g�^��ў��
-e-�Kn�N�VC/���*�B�9ڵ,u���g��iq!��KD��]�ͥ��I��l��R^&�f�"�g��2�>z(J��s������1�p��J2��
-�J�fEx-o�A?7�R�Q1#1&Ax��\
-/��(ae�H_H���]�ڍ#N�ŷ&�����ƣ��<��48R�{��$��K�|lu���!c(�a�������j�Y9�wV\�U_�a��+�ta.4�[N�Z�ƷI�ȰvFvtΝ��EG�"��
-�պJ��g�������o�B�QzU%���<
-�w�'�gsI��]x;��<�#O������/��o�H����/��t�������>����U�/,-������a艰�d6�>����B�)S;��p�#XvRlS�*������7~:��Tڱ=�������/CP���ݥfk�R,�T<E���v�F��|�T_�'���䄨
-�-���)����o�����Q�}+���J�{�'q�cS�kGٹ<��k�'z`�d�{N�
-D��^�N0<~����Y���}�x3A��ߵ���|d�1W����>G\N����x���4���r�++@.���P����s��c����V������-���m�V5�8������J�Zf<�D�Y��orlCώ������ԝ|��ǐ�؞,<9Yc,��"�,���/��C���<Q}�b5��Z5�#�VN���m��ޣ�Oktat�ͭ����x���g�f,�5�5���Ғ��̽oU����(��V��(!V���Ql��Q���S�v��W�催��!��=Y����pU���rЧ�8K��|7��� J�(�R;A�;��6knb>R=h�k�Z�o)������l�������#樗:�R�'RAxMm���)m��-�I`���-C�9�R�m����p|W%�����y��Ĉ�=��qXg6�&�_5j�dD�cS ��`�)K��}�Z=J�g�i%r,z[e���5�Q�n�Jѽq���95�M����W���W�
-�����`�$l�6"'PU�Kio)叏�o�m�MĬ-i_a��㴰�(��3����3�9�R*/�H�n�fR��«h�^��b��dw���±�ڃ�X�_��8t�*�ʇ�5F����,�B��qށ�F%��z@�A�s�ֳ�k�������\���6���|��O�J8��]B����Qy�P Q�%.�����._X�A�R�.���~Ͳ��X�y�O��������ec��Z�aHʢ�x�B01��S�L���ρ�[8�P�.��S��V���w���
-1FQ�|�v���݁�*�?���R]f)��#������T���(Y �Q��;����F�2��8���
-AIU�"�s�C��>���?hᵤy M���j�j?A����<���7�N��Z3��&�y�8}z���U��,��^���?;��������4ie�gN�����tֱ����O��.^_��S�dۊ��Qbe։��m�:ۚ�I�P&Q��L����'�N����
-�
-xZ�F�*�/�Z���6�-��{�ɰ叉�����0[�m;pIQ����ߛå��ޠf1�������6�{#�['
-'�����_>�3�V郮�z����3��hIG�%�p�N?;**,4Y���K�]e���`�Fތ),����e�-��u���,W��)�R̤��~�?�����l�D���O��q��-y^ɕ^GD��/:��%���ecL�A<㾹kG��{����i��~8Vx@6��!u��=�������Ԯ2��/ao�FU��n}�5�N
-�����Pw�5��E���bG�O�ۜ<\�>���s&Ϩ|��z]6=��0���G���ɝ:s!#RivT���[ڮq$����E���q�����.��������^��i�����40%�0�,�Sq���g&E�߷���BR0�H\샡��,�-�N,G�+�_U��d���6��4䔪e�9{y���|員�����XA��P�w+*Ô�Ȭ��\��ĕ����E)؏8��j2%�m�y���?��E�-r�<�ou��t�Y�XS�\מN)����5U
��nj�֒@=a��Ӗ���E:/��ᝧvy)u�Q�R1k&�4W;K푂��&��h˴
-�c#jw�����w��`���R�}����I��%��J��2]S��4^7>H�#��*��om=�tc��+�<8�ͭ:5[�����ɠ�P��b_
-�
-tM�2s���u��L��ۻ�_��D�l>.U|r����*�k�[�"^�M�5�y6�#�k�^~�q�U�4n���&XC�r�8_�~���ɌT:n^;>�H$�cS�L��8�3���a��_���pd�_5�02�$IK��v�%B.A]՞��:LwL�kQfc.m��'��fV�i3-h��Q�o�$�>Nl��%H�9�.)��U����wJMVϥ���.R���L�04�|r
-��*ᷟ����V���Y�0�
-݄��
-���[o�ƴ���+gR��K_� d��QW?���lv[CT��?8�νf9�F��%Q�������c%����x�_�G�������q�-�ㆀ{�-~���kn=!Ю5����=|{~����`w�t����eР����6q�s�5W8�J�rc"γ�\!�@���0�e��!ҰTV.�Zl�_/SOd���%ͅ%v�:q����3
-HMƵ�s�`~�+�����Cq�� ������r���,E9V<!w������)0w������D��B�΄��`�9
-endstream
+x��gXS۶�DzUTD`!EPH���"5 ���*Ei� i"%H�&E�����^�+M:������o�9��k=01�PC����Q4��R+�(����rT0���!���
+)fѿ�+;�=(k����v����$d56��U��ұ�d����������~�ӝk�z<Wt�����n���[�a댗��èʆ��rߺ��~һ��un����HӀ;����c��ۏ�9�+�3�b;�LzĝW��j{.��M9�۾�.1yG�Z$o(��r�hs-�wQH� �
+j4�3�6�D�H8_rj!F
+���p:���c
+�f�u����֯�l���8l&���{���_��[5�g��+~tض�v�4���o���� ������J�V���F�T��k�a��gQ�s2&����T\ޝ��O2��G������d#��Rbv���:�$WH��U,�[-��������C�\�$<���1*��r���X��z�k�R�N���+ϫ����`D(�����I%ag{ۉ�:V�㒙�a"���Z�rB���/&�F����XA�j=8whA��.xX��5�.Km\�6n�wVc,�i�$� �&�5I�X�[9�`�V���N��&|5;o�u��>����Ý,�����K��H�!|����_��K�x����%���-��IO�Ǵs>i�ٳ���X2����l������q|����6����r�·�Ntq˙� ���nB�ys�Ϲm����1��6�Q���ؕ�=���
+��l$�p5�6�?�"���N�%
+w���a>x�D��=�um��2�G>�?B<V��1u96u�US4�n
+��gآmbHR��%djm.�_0��)�g����sҘs�
+8�ܮ᪵
+'9\�\�>ܡ�T�T4U��p2� K����8�l�2�p:2ٺ�Q^�6�r�y]�m���4obn�R
+�R�r&�����
+յh�${E��}�덕��壋��ӳ�����F�@]��kV�51j�n�T�����+]�-�yi)�:DQ��=�ջV�4�����p��.6�-��ө��#�>�h��CC����~�
+��[b
+����vګr���E���E�ݲ@�|�1� �y�)[�
+�9�����υ������z�t��_��@[l
+�5�ܮQ7�h~�����ɶ�<��ŏh'���<�m���ڈ5���p�y�.;<��s�l��,�S���#�P�UZ�q�9!���N������9VI��1w�Х�܅�:�Yr^��sY����a�S�]E�9����[E:B�FGg��0U��5����;j>�Xv�/��~+��M����`��lL�4x�Cq���ݝ(?a9T�&���&��DƢn'�D�7�˹3�ܯ�Ҟ�����6�iv=�x�Ga»tB��H+SU${Y�c~znR��`5����M���%\�g1<����������G��7��95Wk�FVt���K����){F�f��Ә&��
+�
+����R5O���k#5�����řT���ho���#�}^ˍ�:�+�
+�gv,[��u®̵͊ɽ�r��H��m��Dw��S�4cd�a?�\:5h*yTp{����_|�R������TNh�(m�����x�x@�5�B&��� \��&V�6�W%�6�sU0+p<�&�&³"��:��ɧ�|Яe5l�+�;�+:�9bPN�}���+��ٲ��k����4�T6��¯�?�k�*�c?�4���p�ry�!�_Au��#}��\j�o���|�?�0������,�ژ��?���R�1�81��w�'�+ק�� z���:����E�96�e����%�v6��S�s��y��F�336G=��7�����L|��{k�n�y���N��a��ù�o��-��B@.B�3�͋���*tۨ��ې�]���=XjxvW�TBM�CW͌�1;�m�{� Udz�Bf{Ďar�K�N������ڎ-�<�ċ�&��㽨���a����>�yx��:�8��ڂ���}?���5˪z������������G5��-���&i?��������/��s�ndstream
endobj
-35 0 obj
-<<
-/Type/FontDescriptor
-/CapHeight 850
-/Ascent 850
-/Descent -200
-/FontBBox[-4 -235 731 800]
-/FontName/PQEIII+CMTT10
+9 0 obj <<
+/Type /Font
+/Subtype /Type1
+/Encoding 77 0 R
+/FirstChar 44
+/LastChar 120
+/Widths 78 0 R
+/BaseFont /ZCHLIL+CMR12
+/FontDescriptor 7 0 R
+>> endobj
+7 0 obj <<
+/Ascent 694
+/CapHeight 683
+/Descent -194
+/FontName /ZCHLIL+CMR12
/ItalicAngle 0
-/StemV 69
-/FontFile 34 0 R
+/StemV 65
+/XHeight 431
+/FontBBox [-34 -251 988 750]
/Flags 4
->>
+/CharSet (/comma/period/zero/two/seven/eight/at/J/a/e/g/i/k/l/m/n/o/r/s/t/u/x)
+/FontFile 8 0 R
+>> endobj
+78 0 obj
+[272 0 272 0 490 0 490 0 0 0 0 490 490 0 0 0 0 0 0 0 762 0 0 0 0 0 0 0 0 0 503 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 490 0 0 0 435 0 490 0 272 0 517 272 816 544 490 0 0 381 386 381 544 0 0 517 ]
endobj
-34 0 obj
-<<
-/Filter[/FlateDecode]
-/Length1 1874
-/Length2 12070
-/Length3 533
-/Length 13138
+77 0 obj <<
+/Type /Encoding
+/Differences [ 0 /.notdef 44/comma 45/.notdef 46/period 47/.notdef 48/zero 49/.notdef 50/two 51/.notdef 55/seven/eight 57/.notdef 64/at 65/.notdef 74/J 75/.notdef 97/a 98/.notdef 101/e 102/.notdef 103/g 104/.notdef 105/i 106/.notdef 107/k/l/m/n/o 112/.notdef 114/r/s/t/u 118/.notdef 120/x 121/.notdef]
+>> endobj
+5 0 obj <<
+/Length1 1069
+/Length2 4336
+/Length3 532
+/Length 5041
+/Filter /FlateDecode
>>
stream
-x��UX�����Ch� �����%Xp�58 $�3��7��������ʸ������X��r����sh�B����r�!���B����������_��y1��:xx3r-��g�(�F^��,5�J<�؏�^ӭ���?��u-gyH�.ݝ߳t�>c���
-q��:�tc�l�W
-���Ϊ1p�zъ�:��x�i��l3p�^�]R�K"���� ͺG蕐|<���F���������.w=�����k�E��nA@�Nbe�s�)+&�(�.�B�e����o�C���ғU'-�l6������g*Q��Q0S.��ﷆ�|U{1kCzQ�����Ɖ���m�\�>c
-�8�pM�aZ�!hZ����M����&_b��E8���)d�ٲ!C����M¸OvL�Z�ݭ��P���Y����������g7��+3��T|HG%ߌ�E
-+�w�T�T�>�2ٽ},:{g�
-��\D5Tdc��.���ITx����De?E�v2�Н���7Ȫ�o�N- Y64���G�@H�p���-T� iW�:r����������r���������p;�(Y��FXfi8ܽ�]��?��|4�����������dK�ƓYϏ\2b�"�Kґ=�,�T�Gs��I��� q��.���9I����nI
-���du>���g6ny��gq�@���ƄM4�$l7����{��F����̒.�C$?�l��,ɕ�l8�?-=4�$`<kl��Z8B_i4�0����*k�5�f�����;���lcl]\'���s*����E�À��F�
-�dzWT�^�tƐOHC�����5 ����ž�?Gg\a>�8G&��-�>��V��G��<��Gi����Q���9+t����c����>�����a�w���+��%�m%Vް_` _R���m-ڲ� �"�ߋ�79��ԺWpU��X_����;�)����FԽ����M7�,�%��:�t<�LS�{D[$��
-� ������6J�u�z��������]�8mp��4}g+�֯�����-�� �����sq��.]m2Y����s��U�G0��!D��E����������F�#�����n�����2��{�*�~���d>�T,��S������}�^�n�����J��(�dSf���Zh�ޑ5�KD0�.g�Z���Vf�)@%�oQ������L�U*��/$J��!�i�J��g��X�R-(��&���v;%��GE����C����xN������_��A�{���m_��J�f�!����-��3��7-IƁ��#6�f1U���ohm5�®L%���Y���$O���*�"��郩(t��%���A�!*1�����ӎ��vn�]F���e�4Q�6,��n������Ϸ�1��9�R�0K{ϊƄ�ϩiZ����l�'怖�w�:���<�}�T�Y(t(P��o��\X�ܮ�8�����P1'��M�g'f��8�Ho��NH����0��;�[;6f@~����4S�!��\#�l�z�V�H'¶
-�]1�������>U���A�-z>@���Qr���?��o���ʣׅ.���-HPХ�= ȾN�ü�z�s���@>4"DԊ���v���W�ֹ�[�g|#��|S
-W��o�`CI/���@$Ь`�@�����`u��WO.EPϠ������S�z}��Z�]�5�|q�gl8��W���ѻ+jR�i/"��Ć�!.�����Dr��[A�z��j�\�8{�zjˀb�,,����2RՐ0L<���.d�-���"�S��6"]��~o���~�`��<2�������]��V%�4�͛�g��~�����#���zzc褌.+��iJ|�lm�[�6l�]e!C����H��mІ��ǧ�����y ��[��[vG�?B�H����bd��W��rg�1Wq�f�#�đ����.<�;���h�F䕰v� ��P)�'�_��E�2JFD��6̫@��$�����@��J]W��|�b��S�2���������?��cQ������k�#��w��뗋A/=[EÁ���>[-^+_���������Ӓ"����dd���p���J%�v(�W'���`��O&�>�!#7���Zu�p=���3`)�?fV�a�)6�>���;
-�N�c
-��W��V���n|&!������S��Bd�v���);�{�!
-�JW�E�470��Z�5�;�#{<������d*�1�����2h�ŧ��l;]W8Z���Ųٌgs��D����ք��{��t�w����{Zٔ]�8�q�a����w���t���-�3ӇDͨub��ƪ�KDs����[K�N�IJ|�;1N4pmL�/���/�)����^�l7��,�W~�|-8��_���O�0��\<m�"��S8�wI�%��n�F�63��[~�
-��à�0��^��������;bo���ؤ��"�
-?���`�{��������܍/�1=�<�
-_&РQ��VT�?'O�<�ڴG�Z��;ϴ51Tz+s����"Y������X�#\��m�S.3Ȧ�����3��
-�Q�qh�?/�tE��CY<4Z̤_Xй����*{���*����wNHZ�O��U>�5�WrM.8���X(���J��d���H�,Q}��\�1��|jq���v�Q�V����M3��j���p��7<�~
-[#�2H���M�-�t����[����7�;�SE�����CQ���<"U~r��1��?,X�X��B�uH}��=�
-O_`�mY�T���j���y�:F�Q�{Ӽ
-�����Ee�1J}��l�T� ;�^%g3?���m�}�=�Kp��>=؍���4�TU���2��-R�B1D��3�����vjx��6��k���/+,�������D�а��V8Z�}�+�u5�V /�]��x���s
-�}�zP#4���Ϊa���(Q9���g����9WwB���=�>wN��JE�K�1��/.��~~y`�.!�B���/N.�C�-��4����}���n?�.� �py��/�̻t'���n���Sb����ud�̣?r���1�)��@�f�� \�͟���ȹT6�ܧ���;w7~ɖBo��Z��W� $(Wpa��9�R��������,"JQl������|��9�3�G��IF�=�������+�2͉=<�t�_��n��)�mj���I���^I�H��(F��7��ޖ��x��%��(�
-���������8|��-����9W�Ƞ*�T�Fԁ!�N���gd ���b{��Z2�i�a������PŌ~��7:����tL�-V��s!TMD���Ip��s������~�SE�UD�Ȗ�ӈX5��Q�X��.O�5��pD�^�#���S���v�i��g�ͦ�@�"�n?_�O�����Z)�����c+�П�l���gn$?�e��ĠB�GB'���OV�.^��$�Z$
-ɨ�9K)ab��=�2�#��X�K����N'�Y��&�R��U�=څ[m�3���Jd�P��/4E���P�S
-���"R����r����6l��J����d�'�X��EE���URJ�8b��)�?'��-�]>09�9Lճdأ��y���lܙ�Y_Q����������}V@������{8ñ���UNrta�wVt(q���,a�>-]�G�Mm_�W+���O�-�}�Z>)�K ۂ[G���C_ne%j ���[oK)Wÿ����T��x�U"���
-�{2u���Z���4��13����5SJ�g�K[�ĝ�OZj&c9����y�B"GkHL�
-�N3
-��
-k�$�����TM�6I������5z\ڼ;v0e!^hs��"��O�����c�h�"����|ˤ��7��9���({�>���M����h��އǧ�bܮ����c��?��-s��G�<�W�-O��
-ad�O���MF����y�xǰ�}*q�����?E�`4���He��a9�p�+*�.f�������������0(H�#��v=Ҋ$d��<��%��n���-������9ch�7�_����C@\������-��(c������-N�`� cz���~����EQ����μ�$�ԐI}I 1����;�xa��-�HT�7a��P�H�����]��~L���E�V�������z���}\����������vQ2�ai�R\��ߊ���W%J�X�-7H�������9
-c��2Q�-endstream
+x��gXk���JU@ġ#- %�(һ�4)R" I�7EA�JՀ4Azo
+��t� �����?s���}f͚k��K�T���T��!bY@�@�4||�$�+�Ũ���+#��Ny �� ��h�Š�Ҏ��%�:�q5w4Z��g��#
+wF����+C)(+��������%Q9�̮|��_ض-��r�!�Jw����;��,��.��K��� =|����f��+>�%i����M��7�_j�x���\(Ы*�Μ𗑶4`Qo��)'����ji�^�ƈ@���y�O�NM�'�jq2g0kR�<��@�&��]Ҋ�`���G���R̊�<h��������aw�ɉ͚���(_��>���-6t��p.̺��3s�*�ILn�6�����z J�B�H;x�bLf������x�����)�t���<�m�/c�vم��-3|�{>Ѳ�Cm�h^��g�]s��A�����7zx>}��v�.���J�w9�Sy7��BC�9�>O�]���c�ظ�����H"}���)����|)�4�%^��Ӂ:d2I�ᢏ�a�1�\n���&�� g�<P+��E��z6��2�R~ѴkY�!�=v^=
+K�G��Ә����l,��ݙ)-����H����/R��U�?/>����p8�8,�u���2h�OT�Щ;��������'P�҉n7�7��'hy<�~ו�Ձ�Q�j��Űg�,ç�a��"7��s&�^�f�8�[�n����m6�/.p����Q&��ᄥn���u��9��_��c%9�څ��w�ƚ{���+�o���.|\�uql�Y�nё~~(��L�0x�e�ɸ����5� ����5�꓆��G�*c=�R���_O���p?�Ց���0<a+��eD���v�F
+���m���2^8�)��G{�U�;�1��[n^������50��{�v2�~Z��)�{�n ��X���P<�S���a��|'�B�/[T���^��H��GJ!�y�5���6���l�Rr���o�f�)�Lլ��3�`^�B�T�"��+s2>>ٛ�L6�އ���ש���-cu�����e�Qڡ�����ڳͷ�̘$���g�FB]�����)8��ޡն���3 }4.�=�Ћ2~#�2QS���5GQVB�.�Oة�O����|{eG���v�B�==���9����qSU�/������:M����;�b���W�@��y���h^�:�~e�n\������uD�:�vB��G:�ޔsu����'���}I��]�fs����o\Z�M=uW�g�^�3#}:o×��G�U65^�oj%Hw����<�����/Y0�����fs�J�����F�xn7ˏ\8Ŕ�����u
+����m��N�)L
+qvt�<��r��Jw�Fz=�?b�+�(�$��1�ᰶ����.(g_��ˑY&���3 ������.����q�v���xT5�%}���1�)�N�0E#4������^}lݢ�|4�{����}��l^�����S����ؙ;�լL��"o��f{]b�����J��R�T�5Es��^��oUt�Sh�M�_l��'��?yn.��C+�e].}�G���W����8�H�f$K��P���ʶ��~�|���{����M^����,��������͔f�t�q�o��=�����H�>S'X�R�ky��|�+w�HO��Z��pS�p�j��:�����1�|�{Z�]G����/_;���r)ڵ>O����YH,����/j;6l_f]g�>,t��
+�P\{��9Z^��Z'h��(��3�Y�F����˒�*k�x�<~.7O%��M-����������֪������*��S��O^�B�^�?ס�U�X���K�������,o��P�~�!�X�ӯi7��mR�F�%����A�Q���Z�k�-h��O�dobj
-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
->>
+6 0 obj <<
+/Type /Font
+/Subtype /Type1
+/Encoding 79 0 R
+/FirstChar 39
+/LastChar 118
+/Widths 80 0 R
+/BaseFont /NPUWBR+CMR17
+/FontDescriptor 4 0 R
+>> endobj
+4 0 obj <<
+/Ascent 694
+/CapHeight 683
+/Descent -195
+/FontName /NPUWBR+CMR17
+/ItalicAngle 0
+/StemV 53
+/XHeight 430
+/FontBBox [-33 -250 945 749]
+/Flags 4
+/CharSet (/quoteright/hyphen/A/G/J/P/S/T/U/a/b/d/e/i/l/m/o/p/r/s/t/u/v)
+/FontFile 5 0 R
+>> endobj
+80 0 obj
+[250 0 0 0 0 0 302 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 693 0 0 0 0 0 726 0 0 471 0 0 0 0 0 628 0 0 511 668 693 0 0 0 0 0 0 0 0 0 0 0 459 511 0 511 406 0 0 0 250 0 0 250 772 0 459 511 0 354 359 354 511 485 ]
endobj
-41 0 obj
-<<
-/Filter[/FlateDecode]
-/Length1 777
-/Length2 594
-/Length3 533
-/Length 1156
->>
-stream
-x�U�L�Ju�+���Rp�44P0��u.JM,��I,I�R0��4Tp,MW04U00�22�25�Up��,��Q���2Wp�-�N��H���������ZR��������Z�ZT����eh����\������ǥr�g^Z��9D8��-@W��T*���q�mK��dgaq��99~�� ��!����S U��[PZ�Z���Z���4<�Ԕ�\tYϒĜ�Ǽ�]C=cS�Df�[fEjJ@fIr�BZbNq*X<5/��;D���Y�ـ����ʂT�r0��SQf�B�����!P!��h����d���)$%VrS�g�Pm�����Z��Zt��^^~ P�0lj���@�������W����,t-̀&�)���֢*�,,M�500�0�x-���(5�������65�"5�k��Y�_\U
-��
-�ԣ6J�������v�sO��酹�֜9����w���9��ק��⼒���[�ͽ����jI��.������Ee��~�?v�PE_:�S�۹O�Z��:}�Ӎ�9N���^�������Y�mX%W�؟��n�����+���?�ɚ��s���M='�S�|_�o����Ôf��/h�l�������o��7��������������m�aس���N���NEU����}�o��m�ƾ3����_�!��3��/[S�}�e�����w\�����\��,����6V��5]fk��61|Q͟�B����}���K����W(��,�暕-������-VɎe�=k��υ
-:~�������Wo����7#[H���K�S����Z��O����g��<�i@!�`X����XT���X��-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
+79 0 obj <<
+/Type /Encoding
+/Differences [ 0 /.notdef 39/quoteright 40/.notdef 45/hyphen 46/.notdef 65/A 66/.notdef 71/G 72/.notdef 74/J 75/.notdef 80/P 81/.notdef 83/S/T/U 86/.notdef 97/a/b 99/.notdef 100/d/e 102/.notdef 105/i 106/.notdef 108/l/m 110/.notdef 111/o/p 113/.notdef 114/r/s/t/u/v 119/.notdef]
+>> endobj
+25 0 obj <<
+/Type /Pages
+/Count 6
+/Parent 81 0 R
+/Kids [2 0 R 27 0 R 33 0 R 39 0 R 42 0 R 45 0 R]
+>> endobj
+50 0 obj <<
+/Type /Pages
+/Count 5
+/Parent 81 0 R
+/Kids [48 0 R 52 0 R 55 0 R 58 0 R 61 0 R]
+>> endobj
+81 0 obj <<
+/Type /Pages
+/Count 11
+/Kids [25 0 R 50 0 R]
+>> endobj
+82 0 obj <<
+/Type /Catalog
+/Pages 81 0 R
+>> endobj
+83 0 obj <<
+/Producer (pdfeTeX-1.21a)
+/Creator (TeX)
+/CreationDate (D:20070628000323+02'00')
+/PTEX.Fullbanner (This is pdfeTeX, Version 3.141592-1.21a-2.2 (Web2C 7.5.4) kpathsea version 3.5.4)
+>> endobj
xref
-0 77
+0 84
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
+0000002779 00000 n
+0000002664 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
+0000090585 00000 n
+0000085270 00000 n
+0000090430 00000 n
+0000084456 00000 n
+0000079003 00000 n
+0000084301 00000 n
+0000078094 00000 n
+0000070609 00000 n
+0000077936 00000 n
+0000069279 00000 n
+0000054466 00000 n
+0000069122 00000 n
+0000054122 00000 n
+0000051739 00000 n
+0000053967 00000 n
+0000051395 00000 n
+0000049007 00000 n
+0000051240 00000 n
+0000048176 00000 n
+0000041916 00000 n
+0000048020 00000 n
+0000091377 00000 n
+0000005512 00000 n
+0000005394 00000 n
+0000002916 00000 n
+0000040611 00000 n
+0000027165 00000 n
+0000040453 00000 n
+0000008395 00000 n
+0000008277 00000 n
+0000005605 00000 n
+0000026849 00000 n
+0000025464 00000 n
+0000026692 00000 n
+0000011229 00000 n
+0000011111 00000 n
+0000008535 00000 n
+0000013624 00000 n
+0000013506 00000 n
+0000011310 00000 n
+0000015618 00000 n
+0000015500 00000 n
+0000013729 00000 n
+0000018279 00000 n
+0000018161 00000 n
+0000015723 00000 n
+0000091485 00000 n
+0000020295 00000 n
+0000020177 00000 n
+0000018360 00000 n
+0000022831 00000 n
+0000022713 00000 n
+0000020400 00000 n
+0000024482 00000 n
+0000024364 00000 n
+0000022924 00000 n
+0000025371 00000 n
+0000025253 00000 n
+0000024587 00000 n
+0000027079 00000 n
+0000027056 00000 n
+0000041422 00000 n
+0000041073 00000 n
+0000048729 00000 n
+0000048434 00000 n
+0000051641 00000 n
+0000051606 00000 n
+0000054368 00000 n
+0000054333 00000 n
+0000070109 00000 n
+0000069725 00000 n
+0000078642 00000 n
+0000078396 00000 n
+0000084930 00000 n
+0000084713 00000 n
+0000091060 00000 n
+0000090835 00000 n
+0000091587 00000 n
+0000091654 00000 n
+0000091705 00000 n
trailer
<<
-/Size 77
-/Root 2 0 R
-/Info 1 0 R
+/Size 84
+/Root 82 0 R
+/Info 83 0 R
+/ID [<0005E6093B0F959C8EA0BF96414DD1C4> <0005E6093B0F959C8EA0BF96414DD1C4>]
>>
startxref
-92138
+91908
%%EOF
Modified: puppetor/trunk/doc/howto.tex
===================================================================
--- puppetor/trunk/doc/howto.tex 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/doc/howto.tex 2007-06-27 22:09:47 UTC (rev 10687)
@@ -74,22 +74,28 @@
homepage.
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.
+this framework or make sure that it is in the path. You also need to put the
+Tor Controller API classes somewhere. 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:
+You can then start the examples with one of the following commands from the
+base directory of this framework, assuming that the Tor Controller API
+classes reside in directory \texttt{../torctl} (line break behind
+\texttt{examples.} included for layout reasons only):
\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
+java -cp bin:../torctl/bin
+ de.uniba.wiai.lspi.puppetor.examples.
+ AccessingPublicWebServerOverTor
+java -cp bin:../torctl/bin
+ de.uniba.wiai.lspi.puppetor.examples.
+ AdvertisingHiddenServiceToPublicTorNetwork
+java -cp bin:../torctl/bin
+ de.uniba.wiai.lspi.puppetor.examples.
+ AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork
+java -cp bin:../torctl/bin
+ de.uniba.wiai.lspi.puppetor.examples.
+ AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork
\end{verbatim}
\section{Example 1: Accessing public Web server over Tor}
@@ -221,6 +227,12 @@
network.shutdownNodes();
\end{verbatim}
+A typical output of example 1 might look like the following:
+
+\begin{verbatim}
+Request took 527 millis
+\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
@@ -297,6 +309,19 @@
network.shutdownNodes();
\end{verbatim}
+A typical output of example 2 might look like the following:
+
+\begin{verbatim}
+Waiting for 2 minutes and observing RSD publications...
+RSD published 38514 millis after first circuit was opened
+RSD published 38514 millis after first circuit was opened
+RSD published 38779 millis after first circuit was opened
+RSD published 100074 millis after first circuit was opened
+RSD published 100567 millis after first circuit was opened
+RSD published 100568 millis after first circuit was opened
+Exiting...
+\end{verbatim}
+
\section{Example 3: Advertising and accessing hidden service over public Tor
network}
@@ -394,6 +419,14 @@
network.shutdownNodes();
\end{verbatim}
+A typical output of example 3 might look like the following:
+
+\begin{verbatim}
+Request took 13808 millis from client to server!
+Request took 14851 millis for the rount-trip and 1043 millis from server to client!
+Exiting...
+\end{verbatim}
+
\section{Example 4: Advertising and accessing hidden service over private Tor
network}
@@ -491,6 +524,19 @@
network.shutdownNodes();
\end{verbatim}
+A typical output of example 4 might look like the following:
+
+\begin{verbatim}
+Successfully started nodes!
+Successfully built circuits!
+All RSDs published!
+Started server
+Handling event: APPLICATION_SENDING_REQUEST
+Handling event: APPLICATION_REQUEST_RECEIVED
+Handling event: APPLICATION_REPLY_RECEIVED
+Handling event: APPLICATION_REQUESTS_PERFORMED
+\end{verbatim}
+
\section{Architecture}
Though the examples show how to use the simulator, they do not provide insights
@@ -526,8 +572,6 @@
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
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -3,7 +3,7 @@
/**
* 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
+ * address and a port are given to which the client shall connect. Requests are
* performed by a background thread, so that multiple requests could be
* performed at the same time.
*
@@ -13,9 +13,10 @@
/**
* <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
+ * Performs one or more HTTP requests to a previously provided address and
+ * port. All requests are performed by a thread in the background, so that
+ * this method may return immediately. That 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
@@ -32,6 +33,8 @@
* is fired.
* </p>
*
+ * TODO may this method only be invoked once?!
+ *
* @param retries
* The number of retries that this client will perform. Must be
* <code>1</code> or greater.
@@ -48,8 +51,14 @@
* Thrown if an invalid value is given for either of the
* parameters.
*/
- public void performRequest(int retries, long timeoutForEachRetry,
+ public abstract void performRequest(int retries, long timeoutForEachRetry,
boolean stopOnSuccess);
- // TODO we need some way to interrupt performing requests
+ /**
+ * Stops all requests that are currently running.
+ *
+ * @throws IllegalStateException
+ * Thrown if no requests have been started before.
+ */
+ public abstract void stopRequest();
}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -3,37 +3,43 @@
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.
+ * A <code>DirectoryNode</code> represents a Tor process that acts as
+ * <code>RouterNode</code> and which is further a directory authoritative
+ * server for the (private) Tor network. It inherits most of the configuration
+ * and behavior from <code>RouterNode</code> 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.
+ * Combines the fingerprint of this node to a <code>DirServer</code>
+ * 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.
+ * @return <code>DirServer</code> 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.
+ * Thrown if a problem occurs when determining the fingerprint
+ * of this node.
*/
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.
+ * Writes the given (possibly empty) set of onion router fingerprints to the
+ * <code>approved-routers</code> file of this node. This will confirm to
+ * directory clients, that the given routers can be trusted.
*
* @param approvedRouters
- * The set of approved routers to be written.
+ * The set of approved routers to be written. Each provided
+ * string must be formatted as
+ * <code>nickname 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
* @throws IllegalArgumentException
- * Thrown if <code>null</code> is passed as parameter.
+ * Thrown if <code>null</code> is passed as parameter;
+ * however, if an empty set is passed, an empty
+ * <code>approved-routers</code> file will be written.
* @throws TorProcessException
* Thrown if the <code>approved-routers</code> file cannot be
* written to disk.
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -1,9 +1,9 @@
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
+ * An <code>Event</code> 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
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -3,11 +3,11 @@
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.
+ * The <code>EventManager</code> 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
*/
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -16,7 +16,9 @@
* 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.
+ * nodes and before writing configurations to disk. This operation can only
+ * be invoked, if network status is
+ * <code>NetworkState.CONFIGURING_NODES</code>.
* </p>
*
* <p>
@@ -55,8 +57,9 @@
* 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 IllegalStateException
+ * Thrown if network is not in state
+ * <code>NetworkState.CONFIGURING_NODES</code>.
* @throws TorProcessException
* Thrown if an I/O problem occurs while starting nodes with the
* <code>--list-fingerprint</code> option, reading files from
@@ -68,16 +71,14 @@
/**
* 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.
+ * logging purposes only. May neither be <code>null</code> 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.
+ * neither be <code>null</code> 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
@@ -99,9 +100,9 @@
/**
* 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.
+ * This operation can only be invoked, if network status is
+ * <code>NetworkState.CONFIGURING_NODES</code>.
*
- * 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
@@ -123,6 +124,9 @@
* for incoming directory requests. May not be negative or
* greater than 65535.
* @return Reference to the created directory node.
+ * @throws IllegalStateException
+ * Thrown if network is not in state
+ * <code>NetworkState.CONFIGURING_NODES</code>.
* @throws IllegalArgumentException
* Thrown if an invalid value is given for either of the
* parameters.
@@ -131,11 +135,11 @@
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.
+ * Creates a new <code>ProxyNode</code> and adds it to the
+ * <code>network</code>, but does not yet write its configuration to disk
+ * or start the corresponding Tor process. This operation can only be
+ * invoked, if network status is <code>NetworkState.CONFIGURING_NODES</code>.
*
- * 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
@@ -149,6 +153,9 @@
* for incoming SOCKS requests. May not be negative or greater
* than 65535.
* @return Reference to the created proxy node.
+ * @throws IllegalStateException
+ * Thrown if network is not in state
+ * <code>NetworkState.CONFIGURING_NODES</code>.
* @throws IllegalArgumentException
* Thrown if an invalid value is given for either of the
* parameters.
@@ -159,9 +166,9 @@
/**
* 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.
+ * This operation can only be invoked, if network status is
+ * <code>NetworkState.CONFIGURING_NODES</code>.
*
- * 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
@@ -184,6 +191,9 @@
* the mirrored directory. May not be negative or greater than
* 65535.
* @return Reference to the created router node.
+ * @throws IllegalStateException
+ * Thrown if network is not in state
+ * <code>NetworkState.CONFIGURING_NODES</code>.
* @throws IllegalArgumentException
* Thrown if an invalid value is given for either of the
* parameters.
@@ -195,12 +205,10 @@
* 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.
+ * logging purposes only. May neither be <code>null</code> 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.
@@ -215,8 +223,6 @@
/**
* 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();
@@ -244,11 +250,10 @@
* 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>.
+ * <code>true</code>. This operation can only be invoked, if network
+ * status is <code>NetworkState.NODES_STARTED</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
@@ -261,6 +266,9 @@
* HUP signals. Negative values are not allowed. Typically,
* values should not be smaller than 10 seconds to permit Tor to
* stabilize.
+ * @throws IllegalStateException
+ * Thrown if network is not in state
+ * <code>NetworkState.NODES_STARTED</code>.
* @throws IllegalArgumentException
* Thrown if a negative value is given for either
* <code>tries</code> or <code>hupInterval</code>.
@@ -276,10 +284,12 @@
* 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.
+ * assumed to return very quickly. This operation can only be invoked, if
+ * network status is <code>NetworkState.NODES_STARTED</code>.
*
- * TODO allow invocation of this method only in correct state
- *
+ * @throws IllegalStateException
+ * Thrown if network is not in state
+ * <code>NetworkState.NODES_STARTED</code>.
* @throws TorProcessException
* Thrown if an I/O problem occurs while shutting down the
* nodes.
@@ -291,10 +301,10 @@
* <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.
+ * operation was either successful or has timed out. This operation can only
+ * be invoked, if network status is
+ * <code>NetworkState.CONFIGURATIONS_WRITTEN</code>.
*
- * 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
@@ -304,18 +314,26 @@
* @throws IllegalArgumentException
* Thrown if a negative value is given for
* <code>maximumTimeToWaitInMillis</code>.
+ * @throws IllegalStateException
+ * Thrown if network is not in state
+ * <code>NetworkState.CONFIGURATIONS_WRITTEN</code>.
* @throws TorProcessException
- * Thrown if an I/O problem occurs while startint the nodes.
+ * Thrown if an I/O problem occurs while starting 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.
+ * Writes the configurations for all nodes in the network to disk, including
+ * <code>torrc</code> and <code>approved-routers</code> files. This
+ * method is assumed to return very quickly. In case of a private network,
+ * <code>configureAsPrivateNetwork</code> must be invoked in advance to
+ * this method! This operation can only be invoked, if network status is
+ * <code>NetworkState.CONFIGURING_NODES</code>.
*
- * TODO allow invocation of this method only in correct state
- *
+ * @throws IllegalStateException
+ * Thrown if network is not in state
+ * <code>NetworkState.CONFIGURING_NODES</code>.
* @throws TorProcessException
* Thrown if an I/O problem occurs while writing to the nodes'
* working directories.
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -13,7 +13,7 @@
* constructor using reflection. Currently, this is the only place where we
* reference a class from the impl package.
*
- * @author karsten
+ * @author kloesing
*/
public abstract class NetworkFactory {
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -13,6 +13,7 @@
* @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
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -14,16 +14,17 @@
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.
+ * The configuration of this node 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.
+ * The configuration of this node 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,
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -3,31 +3,33 @@
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.
+ * A <code>ProxyNode</code> 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 </code>ProxyNode</code>.
*
* @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.
+ * Adds the entries for a hidden service to the configuration of this node.
+ * 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.
+ * requests. This can, but need not be different from 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
@@ -43,17 +45,17 @@
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!
+ * Adds the given set of DirServer configuration entries to the
+ * configuration of this node. 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>.
+ * <code>null</code>, but may be an empty set.
* @throws IllegalArgumentException
* Thrown if <code>null</code> is passed as parameter.
*/
@@ -61,21 +63,35 @@
Set<String> authorizedDirServerStrings);
/**
- * Returns the node's name.
+ * Adds the given configuration string to the configuration of this node.
*
- * @return The node's name.
+ * @param configurationString
+ * The configuration string to be added.
+ * @throws IllegalArgumentException
+ * Thrown if the given configurationString is either
+ * <code>null</code> or a zero-length string.
+ * @throws IllegalStateException
+ * Thrown if not invoked in state
+ * <code>NodeState.CONFIGURING</code>.
*/
+ public abstract void addConfiguration(String configurationString);
+
+ /**
+ * Returns the name of this node.
+ *
+ * @return The name of this node.
+ */
public abstract String getNodeName();
/**
- * Returns the node's state.
+ * Returns the state of this node.
*
- * @return The node's state.
+ * @return The state of this node.
*/
public abstract NodeState getNodeState();
/**
- * Determines the onion address for the previously added hidden service with
+ * Determines the onion address for a 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>.
*
@@ -83,22 +99,29 @@
* 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.
+ * @param version
+ * Hidden service version; can be either 0, 1, or 2. Note that
+ * version 2 may not be implemented in the regular Tor sources!
* @return The onion address string consisting of 16 base32 chars plus
- * ".onion".
+ * ".onion" for hidden service versions 0 and 1 or 16 base32 chars
+ * plus "." plus 24 base32 chars plus ".onion" for hidden service
+ * version 2.
* @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
+ * the given <code>serviceName</code> as directory, if the
+ * given <code>version</code> is invalid, or if the
* <code>hostname</code> file could not be read.
*/
- public abstract String getOnionAddress(String serviceName)
+ public abstract String getOnionAddress(String serviceName, int version)
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!
+ * can only be done if the node has already been started, i.e. is in state
+ * <code>NodeState.RUNNING</code>!
*
* @throws TorProcessException
* Thrown if an I/O problem occurs while sending the HUP signal.
@@ -108,13 +131,14 @@
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.
+ * Shuts down the Tor process corresponding to this node immediately. This
+ * is done by sending the <code>SHUTDOWN</code> signal twice, so that
+ * those nodes extending <code>ProxyNode</code> which have opened their OR
+ * port shutdown immediately, too.
*
* @throws IllegalStateException
- * Thrown if node is not in state <code>NodeState.RUNNING</code>.
+ * Thrown if this 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.
@@ -125,8 +149,8 @@
* 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>
+ * configuration of this node 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
@@ -150,15 +174,29 @@
throws TorProcessException;
/**
- * Writes the node's configuration to the <code>torrc</code> file in the
- * node's working directory and changes the state to
+ * Writes the configuration of this node to the <code>torrc</code> file in
+ * its 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.
+ * Thrown if the configuration file <code>torrc</code> cannot
+ * be written to disk.
*/
public abstract void writeConfiguration() throws TorProcessException;
+
+ /**
+ * Copies all locally stored descriptors of this node to the data directory
+ * of the given <code>node</code>. Note that this method only makes sense
+ * when used with a modified Tor implementation!
+ *
+ * @param otherNode
+ * Node to which data directory the .rsd files shall be copied.
+ * @throws TorProcessException
+ * Thrown if copying files does not succeed.
+ */
+ public abstract void copyDescriptorTo(ProxyNode otherNode)
+ throws TorProcessException;
}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -1,11 +1,11 @@
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.
+ * A <code>RouterNode</code> 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 <code>ProxyNode</code> and
+ * adds some router-specific configurations and behavior.
*
* @author kloesing
*/
@@ -13,10 +13,11 @@
/**
* <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
+ * Determines the directory fingerprint of this node. If the Tor process has
+ * not been started before, it is started and immediately stopped
+ * afterwards, so that the Tor process creates a new onion key pair and
+ * fingerprint for it. 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.
@@ -43,7 +44,7 @@
* before specifying directory servers for this node.
* </p>
*
- * @return The content of the node's fingerprint file.
+ * @return The content of the fingerprint file of this node.
* @throws TorProcessException
* Thrown if either the temporary <code>torrc</code>
* configuration file cannot be written, the Tor process cannot
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -11,11 +11,21 @@
public interface ServerApplication extends EventSource {
/**
- * Starts listening for incoming <code>HTTP GET</code> requests by
+ * Starts listening for incoming <code>HTTP GET</code> requests from
* clients. Any incoming request is answered by an empty
- * <code>HTTP OK</code> reply.
+ * <code>HTTP OK</code> reply. This method may only be invoked once!
+ *
+ * @throws IllegalStateException
+ * Thrown if <code>listen</code> has already been invoked
+ * before.
*/
public abstract void listen();
- // TODO we need some way to interrupt listening for requests
+ /**
+ * Stops listening for requests.
+ *
+ * @throws IllegalStateException
+ * Thrown if <code>listen</code> has not been invoked before.
+ */
+ public abstract void stopListening();
}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -5,9 +5,10 @@
* 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.
+ * unexpected problem. In contrast to this, all kinds of programming errors of
+ * an application using this API (invoking a method with wrong parameter values,
+ * in wrong state, etc.) will instead cause appropriate runtime exceptions from
+ * the Java API.
*
* @author kloesing
*/
@@ -15,15 +16,16 @@
public class TorProcessException extends Exception {
/**
- * Creates a TorProcessException without a detail message or cause.
+ * Creates a <code>TorProcessException</code> without detail message or
+ * cause.
*/
public TorProcessException() {
super();
}
/**
- * Creates a TorProcessException with the given detail <code>message</code>
- * and <code>cause</code>.
+ * Creates a <code>TorProcessException</code> with the given detail
+ * <code>message</code> and <code>cause</code>.
*
* @param message
* The detail message of this exception.
@@ -35,8 +37,8 @@
}
/**
- * Creates a TorProcessException with the given detail <code>message</code>,
- * but without a cause.
+ * Creates a <code>TorProcessException</code> with the given detail
+ * <code>message</code>, but without a <code>cause</code>.
*
* @param message
* The detail message of this exception.
@@ -46,8 +48,8 @@
}
/**
- * Creates a TorProcessException with the given <code>cause</code>, but
- * without a detail message.
+ * Creates a <code>TorProcessException</code> with the given
+ * <code>cause</code>, but without a detail message.
*
* @param cause
* The cause for this exception.
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -9,8 +9,8 @@
import de.uniba.wiai.lspi.puppetor.TorProcessException;
/**
- * Example for accessing a public Web server (www.google.com) over Tor to
- * measure access times.
+ * Example for accessing a public Web server (here: <code>www.google.com</code>)
+ * over Tor to measure access times.
*
* @author kloesing
*/
@@ -20,16 +20,15 @@
* Sets up and runs the test.
*
* @param args
- * Command-line arguments are ignored.
- *
+ * Command-line arguments (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
+ // though we only need a single 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,
@@ -57,7 +56,7 @@
// create client application
ClientApplication client = network.createClient("client",
- "www.google.de", 80, 7002);
+ "www.google.com", 80, 7002);
// create event listener to listen for client application events
EventListener clientEventListener = new EventListener() {
@@ -92,9 +91,20 @@
manager.waitForAnyOccurence(client,
Event.APPLICATION_REQUESTS_PERFORMED);
+ // wait a second before shutting down the proxy
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+
// shut down proxy
network.shutdownNodes();
- System.out.println("Exiting...");
+ // wait another second before exiting the application
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+
}
}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -22,8 +22,7 @@
* Sets up and runs the test.
*
* @param args
- * Command-line arguments are ignored.
- *
+ * Command-line arguments (ignored).
* @throws TorProcessException
* Thrown if there is a problem with the JVM-external Tor
* processes that we cannot handle.
@@ -45,14 +44,15 @@
RouterNode router1 = network.createRouter("router1", 7021, 7022, 7023,
7024);
network.createRouter("router2", 7031, 7032, 7033, 7034);
- network.createRouter("router3", 7041, 7042, 7043, 7044);
+ 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
+ // add hidden service
router1.addHiddenService("hidServ", 7025, 80);
// configure nodes of this network to be part of a private network
@@ -62,93 +62,72 @@
network.writeConfigurations();
// start proxy node and wait until it has opened a circuit with a
- // timeout of 5 seconds
- if (!network.startNodes(5000)) {
+ // timeout of 15 seconds
+ if (!network.startNodes(15000)) {
// failed to start the proxy
System.out.println("Failed to start nodes!");
return;
}
+ System.out.println("Successfully started nodes!");
- // hup until proxy has built circuits (8 retries, 10 seconds timeout
+ // hup until proxy has built circuits (10 retries, 10 seconds timeout
// each)
- if (!network.hupUntilUp(8, 10000)) {
+ if (!network.hupUntilUp(10, 10000)) {
// failed to build circuits
System.out.println("Failed to build circuits!");
return;
}
+ System.out.println("Successfully built circuits!");
// 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;
}
+ System.out.println("All RSDs published!");
// determine onion address for hidden service
- String onionAddress = router1.getOnionAddress("hidServ");
+ String onionAddress = router1.getOnionAddress("hidServ", 1);
// create server application
ServerApplication server = network.createServer("server", 7025);
+ // start server
+ server.listen();
+ System.out.println("Started server");
+
// create client application
ClientApplication client = network.createClient("client", onionAddress,
80, 7042);
- // create event listener to listen for client and server application
- // events
+ // register event listener
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!");
- }
+ System.out.println("Handling event: " + event);
+
}
};
-
- // 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
+ // wait for request to be performed
manager.waitForAnyOccurence(client,
Event.APPLICATION_REQUESTS_PERFORMED);
- // shut down proxy
+ // shut down nodes
network.shutdownNodes();
- System.out.println("Exiting...");
}
}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -22,8 +22,7 @@
* Sets up and runs the test.
*
* @param args
- * Command-line arguments are ignored.
- *
+ * Command-line arguments (ignored).
* @throws TorProcessException
* Thrown if there is a problem with the JVM-external Tor
* processes that we cannot handle.
@@ -78,7 +77,7 @@
ServerApplication server = network.createServer("server", 7005);
// determine onion address for hidden service
- String onionAddress = proxy1.getOnionAddress("hidServ");
+ String onionAddress = proxy1.getOnionAddress("hidServ", 1);
// create client application
ClientApplication client = network.createClient("client", onionAddress,
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -20,8 +20,7 @@
* Sets up and runs the test.
*
* @param args
- * Command-line arguments are ignored.
- *
+ * Command-line arguments (ignored).
* @throws TorProcessException
* Thrown if there is a problem with the JVM-external Tor
* processes that we cannot handle.
@@ -87,12 +86,12 @@
return;
}
- // let it run for 5 minutes and observe when RSDs are published...
+ // let it run for 2 minutes and observe when RSDs are published...
System.out
- .println("Waiting for 5 minutes and observing RSD publications...");
+ .println("Waiting for 2 minutes and observing RSD publications...");
try {
- Thread.sleep(5L * 60L * 1000L);
+ Thread.sleep(2L * 60L * 1000L);
} catch (InterruptedException e) {
// do nothing
}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -1,8 +1,3 @@
-/*
- * 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;
@@ -21,182 +16,51 @@
import de.uniba.wiai.lspi.puppetor.Event;
/**
- * Implementation of ClientApplication.
+ * Implementation of <code>ClientApplication</code>.
*
* @author kloesing
*/
public class ClientApplicationImpl implements ClientApplication {
/**
- * Thread that performs the requests in the background
+ * Internal thread class that is used to perform requests.
*/
- private RequestThread clientThread;
+ private class RequestThread extends Thread {
- /**
- * Logger for this client
- */
- private Logger logger;
+ /**
+ * Flag to remember whether requests are performed at the moment (<code>true</code>),
+ * or have been stopped (<code>false</code>).
+ */
+ private boolean connected;
- /**
- * Target address for requests; can be either a server address or an onion
- * address.
- */
- private String targetAddress;
+ /**
+ * Number of retries to be performed.
+ */
+ private int retries;
- /**
- * Target port for requests; can be either a server port or a virtual port
- * of a hidden service.
- */
- private int targetPort;
+ /**
+ * Flag that determines whether requests shall be stopped after the
+ * first successful reply (<code>true</code>), or not (<code>false</code>).
+ */
+ private boolean stopOnSuccess;
- /**
- * 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;
-
+ /**
+ * Timeout in milliseconds for each retry.
+ */
private long timeoutForEachRetry;
- private boolean connected;
-
- private boolean stopOnSuccess;
-
+ /**
+ * Creates a new thread, but does not start performing requests, yet.
+ *
+ * @param retries
+ * Number of retries to be performed.
+ * @param timeoutForEachRetry
+ * Timeout in milliseconds for each retry.
+ * @param stopOnSuccess
+ * Flag that determines whether requests shall be stopped
+ * after the first successful reply (<code>true</code>),
+ * or not (<code>false</code>).
+ */
RequestThread(int retries, long timeoutForEachRetry,
boolean stopOnSuccess) {
@@ -206,6 +70,13 @@
new Object[] { retries, timeoutForEachRetry,
stopOnSuccess });
+ // check parameters
+ if (retries < 0 || timeoutForEachRetry < 0) {
+ IllegalArgumentException e = new IllegalArgumentException();
+ logger.throwing(this.getClass().getName(), "RequestThread", e);
+ throw e;
+ }
+
// remember parameters
this.retries = retries;
this.timeoutForEachRetry = timeoutForEachRetry;
@@ -219,25 +90,8 @@
}
/**
- * stoppt diesen request egal wo er gerade steht
- *
+ * Perform one or more requests.
*/
- 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() {
@@ -251,12 +105,12 @@
socksPort);
Proxy p = new Proxy(Type.SOCKS, isa);
- // create target address for socket -- don't resolve the target address
- // to an IP address!
- InetSocketAddress hs = InetSocketAddress.createUnresolved(targetAddress,
- targetPort);
+ // create target address for socket -- don't resolve the target
+ // name to an IP address!
+ InetSocketAddress hs = InetSocketAddress.createUnresolved(
+ targetName, targetPort);
- // start loop
+ // start retry loop
for (int i = 0; connected && i < retries; i++) {
// log this try
@@ -331,7 +185,7 @@
// wait for the rest of the timeout
long timeOfTimeoutLeft = timeBeforeConnectionAttempt
- + timeoutForEachRetry
+ + this.timeoutForEachRetry
- System.currentTimeMillis();
if (timeOfTimeoutLeft > 0) {
try {
@@ -379,13 +233,189 @@
logger.exiting(this.getClass().getName(), "run");
}
}
+
+ /**
+ * Immediately stops this and all possibly subsequent requests.
+ */
+ 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");
+ }
}
+ /**
+ * Name of this client application that is used as logger name of this node.
+ */
+ private String clientApplicationName;
+
+ /**
+ * Thread that performs the requests in the background.
+ */
+ private RequestThread clientThread;
+
+ /**
+ * Event manager to which all events concerning this client application are
+ * notified.
+ */
+ private EventManagerImpl eventManager;
+
+ /**
+ * Logger for this client.
+ */
+ private Logger logger;
+
+ /**
+ * SOCKS port of the local Tor node to which requests are sent.
+ */
+ private int socksPort;
+
+ /**
+ * Target name for the requests sent by this client; can be either a server
+ * name/address or an onion address.
+ */
+ private String targetName;
+
+ /**
+ * Target port for the requests sent by this client; can be either a server
+ * port or a virtual port of a hidden service.
+ */
+ private int targetPort;
+
+ /**
+ * Creates a new HTTP client within this JVM, but does not start sending
+ * requests.
+ *
+ * @param network
+ * Network to which this HTTP client belongs; at the moment this
+ * is only used to determine the event manager instance.
+ * @param clientApplicationName
+ * Name of this client that is used as part of the logger name.
+ * @param targetName
+ * Target name for requests; can be either a server name/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.
+ * @throws IllegalArgumentException
+ * If at least one of the parameters is <code>null</code> or has an invalid
+ * value.
+ */
+ ClientApplicationImpl(NetworkImpl network, String clientApplicationName,
+ String targetName, int targetPort, int socksPort) {
+
+ // check if clientApplicationName can be used as logger name
+ if (clientApplicationName == null
+ || clientApplicationName.length() == 0) {
+ throw new IllegalArgumentException(
+ "Invalid clientApplicationName: " + clientApplicationName);
+ }
+
+ // create logger
+ this.logger = Logger.getLogger("application." + clientApplicationName);
+
+ // log entering
+ this.logger.entering(this.getClass().getName(),
+ "ClientApplicationImpl", new Object[] { network,
+ clientApplicationName, targetName, targetPort,
+ socksPort });
+
+ // check parameters
+ if (network == null || targetName == null || targetName.length() == 0
+ || targetPort < 0 || targetPort > 65535 || socksPort < 0
+ || socksPort > 65535) {
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(),
+ "ClientApplicationImpl", e);
+ throw e;
+ }
+
+ // remember parameters
+ this.clientApplicationName = clientApplicationName;
+ this.targetName = targetName;
+ this.targetPort = targetPort;
+ this.socksPort = socksPort;
+
+ // obtain and store reference on event manager
+ this.eventManager = network.getEventManagerImpl();
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "ClientApplicationImpl");
+ }
+
+ public synchronized 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) {
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger
+ .throwing(this.getClass().getName(), "performRequest", e);
+ throw e;
+ }
+
+ // check if we already have started a request (TODO change this to allow
+ // multiple requests in parallel? would be possible)
+ if (this.clientThread != null) {
+ IllegalStateException e = new IllegalStateException(
+ "Another request has already been started!");
+ this.logger
+ .throwing(this.getClass().getName(), "performRequest", e);
+ throw e;
+ }
+
+ // 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");
+ }
+
+ public synchronized void stopRequest() {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "stopRequest");
+
+ // check if a request is running
+ if (this.clientThread == null) {
+ IllegalStateException e = new IllegalStateException("Cannot stop "
+ + "request, because no request has been started!");
+ this.logger.throwing(this.getClass().getName(), "stopRequest", e);
+ throw e;
+ }
+
+ // 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");
+ }
+
@Override
public String toString() {
return this.getClass().getSimpleName() + ": clientApplicationName=\""
+ this.clientApplicationName + "\", targetAddress=\""
- + this.targetAddress + "\", targetPort=" + this.targetPort
+ + this.targetName + "\", targetPort=" + this.targetPort
+ ", socksPort=" + this.socksPort;
}
}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -1,8 +1,3 @@
-/*
- * 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;
@@ -16,19 +11,59 @@
import de.uniba.wiai.lspi.puppetor.DirectoryNode;
import de.uniba.wiai.lspi.puppetor.TorProcessException;
+/**
+ * Implementation of <code>DirectoryNode</code>.
+ *
+ * @author karsten
+ */
public class DirectoryNodeImpl extends RouterNodeImpl implements DirectoryNode {
+
/**
- * Initializes this Tor node, but does not start it, yet.
+ * Creates a <code>DirectoryNodeImpl</code> and adds it to the given
+ * <code>network</code>, but does not yet write its configuration to disk
+ * or start the corresponding Tor process.
+ *
+ * @param network
+ * Network configuration to which this node belongs.
+ * @param nodeName
+ * The name of the new node which may only consist of between 1
+ * and 19 alpha-numeric characters.
+ * @param controlPort
+ * Port on which the Tor node will be listening for us as its
+ * controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * Port on which the Tor node will be listening for SOCKS
+ * connection requests. May not be negative or greater than
+ * 65535.
+ * @param orPort
+ * Port on which the Tor node will be listening for onion
+ * requests by other Tor nodes. May not be negative or greater
+ * than 65535.
+ * @param dirPort
+ * Port on which the Tor node will be listening for directory
+ * requests from other Tor nodes. May not be negative or greater
+ * than 65535.
+ * @throws IllegalArgumentException
+ * If at least one of the parameters is <code>null</code> or
+ * has an invalid value.
*/
- public DirectoryNodeImpl(NetworkImpl network, String nodeName,
- int controlPort, int socksPort, int orPort, int dirPort) {
+ DirectoryNodeImpl(NetworkImpl network, String nodeName, int controlPort,
+ int socksPort, int orPort, int dirPort) {
+
+ // create superclass instance; parameter checking is done in super
+ // constructor
super(network, nodeName, controlPort, socksPort, orPort, dirPort);
+ // log entering
+ this.logger.entering(this.getClass().getName(), "DirectoryNodeImpl",
+ new Object[] { 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?
+ // location of tor executable?
this.configuration
.add("RecommendedVersions 0.1.2.12-rc,0.1.2.7-alpha-dev,0.2.0.0-alpha-dev");
@@ -36,19 +71,61 @@
this.configuration.add("NamingAuthoritativeDirectory 1");
- this.configuration.add("V2AuthoritativeDirectory 1");
+ // TODO this requires version 0.2.x
+ //this.configuration.add("V2AuthoritativeDirectory 1");
this.configuration.add("V1AuthoritativeDirectory 1");
// TODO this only works since Tor 0.1.2.x!!!
this.configuration.add("HSAuthoritativeDir 1");
+ // TODO only in v0.2.x
+ //this.configuration.add("HSAuthorityRecordStats 1");
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "DirectoryNodeImpl");
}
- public void writeApprovedRouters(Set<String> approvedRouters)
+ public synchronized String determineDirServerString()
throws TorProcessException {
-
- // check param!
-
+
+ // log entering
+ this.logger.entering(this.getClass().getName(),
+ "determineDirServerString");
+
+ // determine fingerprint
+ String fingerprint = determineFingerprint();
+
+ // cut off router nickname
+ fingerprint = fingerprint.substring(fingerprint.indexOf(" ") + 1);
+
+ // put everything together
+ String dirServerString = "DirServer " + this.nodeName + " hs orport="
+ + this.orPort + " " + localIpAddress + ":" + this.dirPort + " "
+ + fingerprint;
+
+ // log exiting and return dir server string
+ this.logger.exiting(this.getClass().getName(),
+ "determineDirServerString", dirServerString);
+ return dirServerString;
+ }
+
+ public synchronized void writeApprovedRouters(Set<String> approvedRouters)
+ throws TorProcessException {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "writeApprovedRouters",
+ approvedRouters);
+
+ // check parameter
+ if (approvedRouters == null) {
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(),
+ "writeApprovedRouters", e);
+ throw e;
+ }
+
+ // sort the given approved router strings alphabetically and store them
+ // to file
try {
File approvedRoutersFile = new File(this.workingDir
.getAbsolutePath()
@@ -62,22 +139,13 @@
}
bw.close();
} catch (IOException e) {
- throw new TorProcessException(e);
+ TorProcessException ex = new TorProcessException(e);
+ this.logger.throwing(this.getClass().getName(),
+ "writeApprovedRouters", ex);
+ throw ex;
}
- }
- 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 " + localIpAddress + ":" + this.dirPort + " " + fingerprint;
- return dirServerString;
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "writeApprovedRouters");
}
}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -1,8 +1,3 @@
-/*
- * 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;
@@ -19,23 +14,61 @@
import de.uniba.wiai.lspi.puppetor.EventManager;
import de.uniba.wiai.lspi.puppetor.EventSource;
+/**
+ * Implementation of <code>EventManager</code>.
+ */
public class EventManagerImpl implements EventManager {
+ /**
+ * Registered event handlers.
+ */
+ private Map<EventSource, Set<EventListener>> eventHandlers;
+
+ /**
+ * Logger for this <code>EventManagerImpl</code> instance which is called
+ * "event." plus the name of the network.
+ */
private Logger logger;
+ /**
+ * Events observed so far.
+ */
+ private Map<EventSource, List<Event>> observedEvents;
+
+ /**
+ * Creates a new <code>EventManagerImpl</code> for the network with name
+ * <code>networkName</code> and initializes it.
+ *
+ * @param networkName
+ * Name of this event manager that is used as part of the logger
+ * name.
+ * @throws IllegalArgumentException
+ * Thrown if the given <code>networkName</code> is either
+ * <code>null</code> or a zero-length string.
+ */
EventManagerImpl(String networkName) {
-
-// TODO make sure that name is loggable!!
-
+
+ // check if networkName can be used as logger name
+ if (networkName == null || networkName.length() == 0) {
+ throw new IllegalArgumentException("Invalid networkName: "
+ + networkName);
+ }
+
+ // create logger
this.logger = Logger.getLogger("event." + networkName);
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "EventManagerImpl",
+ networkName);
+
+ // create data structures
this.observedEvents = new HashMap<EventSource, List<Event>>();
this.eventHandlers = new HashMap<EventSource, Set<EventListener>>();
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "EventManagerImpl");
}
- private Map<EventSource, List<Event>> observedEvents;
-
- private Map<EventSource, Set<EventListener>> eventHandlers;
-
public synchronized List<Event> addEventListener(EventSource source,
EventListener listener) {
@@ -45,7 +78,10 @@
// check parameters
if (source == null || listener == null) {
- throw new IllegalArgumentException();
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(), "addEventListener",
+ e);
+ throw e;
}
// if necessary, create new event listener set for source
@@ -70,12 +106,15 @@
public synchronized List<Event> getEventHistory(EventSource source) {
// log entering
- this.logger.entering(this.getClass().getName(), "getNodeEventHistory",
+ this.logger.entering(this.getClass().getName(), "getEventHistory",
source);
// check parameter
if (source == null) {
- throw new IllegalArgumentException();
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(), "getEventHistory",
+ e);
+ throw e;
}
// prepare result
@@ -88,10 +127,9 @@
}
// log exiting and return result
- this.logger.exiting(this.getClass().getName(), "getNodeEventHistory",
+ this.logger.exiting(this.getClass().getName(), "getEventHistory",
result);
return result;
-
}
public synchronized boolean hasEventOccured(EventSource source, Event event) {
@@ -102,7 +140,10 @@
// check parameters
if (source == null || event == null) {
- throw new IllegalArgumentException();
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(), "hasEventOccured",
+ e);
+ throw e;
}
// determine result
@@ -115,6 +156,57 @@
return result;
}
+ /**
+ * Stores the given <code>event</code> from <code>source</code> to the
+ * event history and propagates its occurrence to all registered event
+ * handlers.
+ *
+ * @param source
+ * The source of the given event.
+ * @param event
+ * The event type.
+ */
+ 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 themselves 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");
+ }
+
public synchronized void removeEventListener(EventListener eventListener) {
// log entering
@@ -123,7 +215,10 @@
// check parameters
if (eventListener == null) {
- throw new IllegalArgumentException();
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(),
+ "removeEventListener", e);
+ throw e;
}
// don't know to which source this listener has been added (may to more
@@ -146,7 +241,10 @@
// check parameters
if (source == null || event == null) {
- throw new IllegalArgumentException();
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(),
+ "waitForAnyOccurence", e);
+ throw e;
}
// invoke overloaded method with maximumTimeToWaitInMillis of -1L which
@@ -167,15 +265,16 @@
// check parameters
if (source == null || event == null) {
- throw new IllegalArgumentException();
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(),
+ "waitForAnyOccurence", e);
+ throw e;
}
// 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;
@@ -189,7 +288,6 @@
this.logger.exiting(this.getClass().getName(), "waitForAnyOccurence",
result);
return result;
-
}
public synchronized void waitForNextOccurence(EventSource source,
@@ -201,7 +299,10 @@
// check parameters
if (source == null || event == null) {
- throw new IllegalArgumentException();
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(),
+ "waitForNextOccurence", e);
+ throw e;
}
// invoke overloaded method with maximumTimeToWaitInMillis of -1L which
@@ -210,7 +311,6 @@
// log exiting
this.logger.exiting(this.getClass().getName(), "waitForNextOccurence");
-
}
public synchronized boolean waitForNextOccurence(EventSource source,
@@ -222,7 +322,10 @@
// check parameters
if (source == null || event == null) {
- throw new IllegalArgumentException();
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(),
+ "waitForNextOccurence", e);
+ throw e;
}
// distinguish between negative waiting time (wait forever) and zero or
@@ -299,50 +402,4 @@
}
}
- /**
- * 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");
- }
-
-}
+}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -1,8 +1,3 @@
-/*
- * 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;
@@ -24,92 +19,526 @@
import de.uniba.wiai.lspi.puppetor.ServerApplication;
import de.uniba.wiai.lspi.puppetor.TorProcessException;
+/**
+ * Implementation of <code>Network</code>.
+ *
+ * @author kloesing
+ */
public class NetworkImpl implements Network {
- private EventManagerImpl eventManager;
+ /**
+ * Internal thread class that is used to determine fingerprints in parallel.
+ */
+ private class FingerprintThread extends Thread {
- @Override
- public String toString() {
- return this.getClass().getSimpleName() + ": networkName=\""
- + this.networkName + "\", networkState="
- + this.networkState.toString() + ", nodes.size()="
- + this.nodes.size();
+ /**
+ * The exception, if one is caught while trying to determine the
+ * fingerprint of the node.
+ */
+ private TorProcessException caughtException = null;
+
+ /**
+ * The node of which the fingerprint shall be determined.
+ */
+ private RouterNode node = null;
+
+ /**
+ * Creates a new thread to determine the fingerprint of
+ * <code>node</code>.
+ *
+ * @param node
+ * The node of which the fingerprint shall be determined.
+ */
+ FingerprintThread(RouterNode node) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "FingerprintThread");
+
+ // remember node reference
+ this.node = node;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "FingerprintThread");
+ }
+
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ // determine fingerprint
+ try {
+ node.determineFingerprint();
+ } catch (TorProcessException e) {
+ logger.log(Level.SEVERE,
+ "Caught an exception while determining fingerprint for "
+ + "node " + node.toString() + "!");
+ this.caughtException = e;
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
}
+ /**
+ * Internal thread class that is used to start Tor processes in parallel.
+ */
+ private class NodeStarter extends Thread {
+
+ /**
+ * The exception, if one is caught while trying to start the node.
+ */
+ TorProcessException caughtException;
+
+ /**
+ * The maximum time to wait for the Tor process to start in
+ * milliseconds.
+ */
+ private long maximumTimeToWaitInMillis;
+
+ /**
+ * The node for which the Tor process shall be started.
+ */
+ private ProxyNode node;
+
+ /**
+ * Flag that denotes whether starting the Tor process was successful.
+ */
+ boolean success = false;
+
+ /**
+ * Creates a new <code>NodeStarter</code> for node <code>node</code>
+ * that will wait for <code>maximumTimeToWaitInMillis</code>
+ * milliseconds to start a Tor process, but that is not started, yet.
+ *
+ * @param node
+ * The node for which the Tor process shall be started.
+ * @param maximumTimeToWaitInMillis
+ * The maximum time to wait for the Tor process to start in
+ * milliseconds.
+ */
+ NodeStarter(ProxyNode node, long maximumTimeToWaitInMillis) {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "NodeStarter",
+ new Object[] { node, maximumTimeToWaitInMillis });
+
+ // store parameters
+ this.node = node;
+ this.maximumTimeToWaitInMillis = maximumTimeToWaitInMillis;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "NodeStarter");
+ }
+
+ @Override
+ public void run() {
+
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
+
+ try {
+ // try to start node
+ this.success = this.node
+ .startNode(this.maximumTimeToWaitInMillis);
+ } catch (TorProcessException e) {
+ // if an exception is caught, store it, but don't throw it (the
+ // thread wouldn't care)
+ this.caughtException = e;
+ }
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ }
+ }
+
+ /**
+ * The fingerprints of all approved routers in the network configuration.
+ */
+ private HashSet<String> approvedRoutersFingerprints;
+
+ /**
+ * The fingerprints of all authoritative directories in the network
+ * configuration.
+ */
+ private Set<String> authorizedDirectoriesFingerprints;
+
+ /**
+ * Event manager to which all events concerning this network are notified.
+ */
+ private EventManagerImpl eventManager;
+
+ /**
+ * Logger for this <code>NetworkImpl</code> instance which is called
+ * "network." plus the name of this network.
+ */
private Logger logger;
/**
- * Contains the name of this node configuration which is the String
- * conversion of System.currentTimeMillis().
+ * Contains the name of this network configuration which is the String
+ * conversion of System.currentTimeMillis() of the network creation time.
*/
- protected String networkName;
+ private String networkName;
/**
+ * The state of this network.
+ */
+ private NetworkState networkState = NetworkState.CONFIGURING_NODES;
+
+ /**
+ * All nodes contained in this network. It is important that we store and
+ * work only with interface types to assure that all operations could also
+ * be performed by the application itself.
+ */
+ private Set<ProxyNode> nodes = new HashSet<ProxyNode>();
+
+ /**
+ * Directory that contains status information of all nodes contained in this
+ * network.
+ */
+ private File workingDir;
+
+ /**
* 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.
+ * Name of this network configuration. May neither be
+ * <code>null</code> or a zero-length string.
+ * @throws IllegalArgumentException
+ * Thrown if the given <code>networkName</code> is either
+ * <code>null</code> or a zero-length string.
*/
public NetworkImpl(String networkName) {
-
-// TODO make sure that name is loggable!!
-
+
+ // check if networkName can be used as logger name
+ if (networkName == null || networkName.length() == 0) {
+ throw new IllegalArgumentException("Invalid networkName: "
+ + networkName);
+ }
+
+ // create logger
this.logger = Logger.getLogger("network." + networkName);
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "NetworkImpl",
+ networkName);
+
+ // TODO is this necessary?!
this.logger.setLevel(Level.ALL);
+
+ // remember parameter
this.networkName = networkName;
+
+ // create working directory
this.workingDir = new File("test-env/" + System.currentTimeMillis());
this.workingDir.mkdirs();
+ this.logger.log(Level.FINE, "Created working directory \""
+ + this.workingDir.getAbsolutePath() + "\"");
// TODO if we want to log to file, set this... somehow...
// this.logFile = new File(this.workingDir.getAbsolutePath()
// + "\\events.log");
+ // create event manager
this.eventManager = new EventManagerImpl(this.networkName);
- }
- public NetworkState getNetworkState() {
- // TODO Auto-generated method stub
- throw new UnsupportedOperationException(
- "Auto-generated method stub in NetworkImpl.getNetworkState");
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "NetworkImpl");
}
+ /**
+ * Returns whether all nodes in this network are up, or not.
+ *
+ * @return <code>true</code> if all nodes are up, <code>false</code> if
+ * at least one node is not up.
+ */
private boolean allNodesUp() {
- // check if all nodes are up
+ // log entering
+ this.logger.entering(this.getClass().getName(), "allNodesUp");
+
+ // nodes can only be up, when network is in state
+ // NetworkState.NODES_STARTED.
+ if (this.networkState != NetworkState.NODES_STARTED) {
+
+ // log exiting and return false
+ this.logger.exiting(this.getClass().getName(), "allNodesUp");
+ return false;
+ }
+
+ // fail on first node that is not up
for (ProxyNode node : this.nodes) {
if (!eventManager.hasEventOccured(node, Event.NODE_CIRCUIT_OPENED)) {
+
+ // log exiting and return false
+ this.logger.exiting(this.getClass().getName(), "allNodesUp");
return false;
}
}
+
+ // log exiting and return true
+ this.logger.exiting(this.getClass().getName(), "allNodesUp");
return true;
}
+ public void configureAsPrivateNetwork() throws TorProcessException {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(),
+ "configureAsPrivateNetwork");
+
+ // start threads to determine fingerprints for all directories and
+ // routers 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();
+ }
+ }
+
+ // wait for all fingerprints have been determined
+ for (FingerprintThread fingerprintThread : fingerprintThreads) {
+
+ // join fingerprint determination one after the other
+ try {
+ fingerprintThread.join();
+ } catch (InterruptedException e) {
+ // ignore; TODO really?!
+ logger.log(Level.WARNING,
+ "Joining fingerprint thread was interrupted.");
+ }
+
+ // if any thread has caught an exception, throw that exception now
+ if (fingerprintThread.caughtException != null) {
+ this.logger.throwing(this.getClass().getName(),
+ "configureAsPrivateNetwork",
+ fingerprintThread.caughtException);
+ 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
+ this.authorizedDirectoriesFingerprints = new HashSet<String>();
+ 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; 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());
+ }
+ }
+
+ // write fingerprints for all directories and routers to
+ // approved-routers file
+ for (ProxyNode node : this.nodes) {
+ if (node instanceof DirectoryNode) {
+ DirectoryNode dirNode = (DirectoryNode) node;
+ dirNode.writeApprovedRouters(this.approvedRoutersFingerprints);
+ }
+ }
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(),
+ "configureAsPrivateNetwork");
+ }
+
+ public ClientApplication createClient(String clientApplicationName,
+ String targetAddress, int targetPort, int socksPort) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "createClient",
+ new Object[] { clientApplicationName, targetAddress,
+ targetPort, socksPort });
+
+ // create client; parameter checking is done in constructor
+ ClientApplicationImpl client = new ClientApplicationImpl(this,
+ clientApplicationName, targetAddress, targetPort, socksPort);
+
+ // log exiting and return client
+ this.logger.exiting(this.getClass().getName(), "createClient", client);
+ return client;
+ }
+
+ public DirectoryNode createDirectory(String nodeName, int controlPort,
+ int socksPort, int orPort, int dirPort) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "createDirectory",
+ new Object[] { nodeName, controlPort, socksPort, orPort,
+ dirPort });
+
+ // check state
+ if (this.networkState != NetworkState.CONFIGURING_NODES) {
+ IllegalStateException e = new IllegalStateException();
+ this.logger.throwing(this.getClass().getName(), "createDirectory",
+ e);
+ throw e;
+ }
+
+ // create directory node; parameter checking is done in constructor
+ DirectoryNode dir = new DirectoryNodeImpl(this, nodeName, controlPort,
+ socksPort, orPort, dirPort);
+
+ // add new directory node to nodes collection
+ this.nodes.add(dir);
+
+ // log exiting and return directory node
+ this.logger.exiting(this.getClass().getName(), "createDirectory", dir);
+ return dir;
+ }
+
+ public ProxyNode createProxy(String nodeName, int controlPort, int socksPort) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "createProxy",
+ new Object[] { nodeName, controlPort, socksPort });
+
+ // check state
+ if (this.networkState != NetworkState.CONFIGURING_NODES) {
+ IllegalStateException e = new IllegalStateException();
+ this.logger.throwing(this.getClass().getName(), "createProxy", e);
+ throw e;
+ }
+
+ // create proxy node; parameter checking is done in constructor
+ ProxyNodeImpl proxy = new ProxyNodeImpl(this, nodeName, controlPort,
+ socksPort);
+
+ // add new proxy node to nodes collection
+ this.nodes.add(proxy);
+
+ // log exiting and return proxy node
+ this.logger.exiting(this.getClass().getName(), "createProxy", proxy);
+ return proxy;
+ }
+
+ public RouterNode createRouter(String nodeName, int controlPort,
+ int socksPort, int orPort, int dirPort) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "createRouter",
+ new Object[] { nodeName, controlPort, socksPort, orPort,
+ dirPort });
+
+ // check state
+ if (this.networkState != NetworkState.CONFIGURING_NODES) {
+ IllegalStateException e = new IllegalStateException();
+ this.logger.throwing(this.getClass().getName(), "createRouter", e);
+ throw e;
+ }
+
+ // create router node; parameter checking is done in constructor
+ RouterNode router = new RouterNodeImpl(this, nodeName, controlPort,
+ socksPort, orPort, dirPort);
+
+ // add new router node to nodes collection
+ this.nodes.add(router);
+
+ // log exiting and return router node
+ this.logger.exiting(this.getClass().getName(), "createRouter", router);
+ return router;
+ }
+
+ public ServerApplication createServer(String serverApplicationName,
+ int serverPort) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "createServer",
+ new Object[] { serverApplicationName, serverPort });
+
+ // create server; parameter checking is done in constructor
+ ServerApplicationImpl server = new ServerApplicationImpl(this,
+ serverApplicationName, serverPort);
+
+ // log exiting and return server
+ this.logger.exiting(this.getClass().getName(), "createServer", server);
+ return server;
+ }
+
+ public EventManager getEventManager() {
+ return this.eventManager;
+ }
+
/**
- * check, (wait, check, hup)*
+ * Returns the implementation instance of the event manager of this network.
*
- * if all nodes get up during sleeping, we will wake up and return
- * immediately
+ * @return The implementation instance of the event manager of this network.
+ */
+ EventManagerImpl getEventManagerImpl() {
+ return this.eventManager;
+ }
+
+ public NetworkState getNetworkState() {
+ return this.networkState;
+ }
+
+ /**
+ * Returns the working directory of this network configuration which is in
+ * test-env/networkName/.
*
- * @param retries
- * @param hupInterval
- * @throws Exception
+ * @return Working directory of this network.
*/
- public boolean hupUntilUp(int retries, long hupInterval)
+ File getWorkingDir() {
+ return workingDir;
+ }
+
+ public boolean hupUntilUp(int tries, long hupInterval)
throws TorProcessException {
+ // log entering
+ this.logger.entering(this.getClass().getName(), "hupUntilUp",
+ new Object[] { tries, hupInterval });
+
+ // check state
+ if (this.networkState != NetworkState.NODES_STARTED) {
+ IllegalStateException e = new IllegalStateException();
+ this.logger.throwing(this.getClass().getName(), "hupUntilUp", e);
+ throw e;
+ }
+
+ // check if nodes are already up; if so, return immediately
if (allNodesUp()) {
+
+ // log exiting and return true
+ this.logger.exiting(this.getClass().getName(), "hupUntilUp", true);
return true;
}
- // register event handlers
+ // create and register a new event handler for each node
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();
@@ -119,22 +548,30 @@
});
}
- for (int i = 0; i < retries; i++) {
+ // walk through wait-check-hup loop until there are no tries left
+ for (int i = 0; i < tries; i++) {
// determine how long to sleep
long endOfSleeping = System.currentTimeMillis() + hupInterval;
long now;
+ // unless all nodes have reported to be up, wait for the given
+ // maximum time
while ((now = System.currentTimeMillis()) < endOfSleeping) {
// sleep
try {
Thread.sleep(endOfSleeping - now);
} catch (InterruptedException e) {
+ // do nothing about it
}
- // check
+ // check if nodes are up now
if (allNodesUp()) {
+
+ // log exiting and return true
+ this.logger.exiting(this.getClass().getName(),
+ "hupUntilUp", true);
return true;
}
}
@@ -147,15 +584,27 @@
// continue in loop
}
- // no retries left and not all nodes are up; return failure
+ // no retries left and not all nodes are up; log exiting and return
+ // failure
+ this.logger.exiting(this.getClass().getName(), "hupUntilUp", false);
return false;
-
}
public void shutdownNodes() throws TorProcessException {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "shutdownNodes");
+
+ // check state
if (this.networkState != NetworkState.NODES_STARTED) {
- throw new IllegalStateException();
+ IllegalStateException e = new IllegalStateException();
+ this.logger.throwing(this.getClass().getName(), "shutdownNodes", e);
+ throw e;
}
+
+ // iteratively shut down all nodes; if an exception is caught, continue
+ // shutting down the other nodes and throw the first exception
+ // subsequently
TorProcessException firstCaughtException = null;
for (ProxyNode node : this.nodes) {
try {
@@ -166,46 +615,41 @@
}
}
}
+
+ // change network state
this.networkState = NetworkState.NODES_SHUT_DOWN;
+
+ // if an exception was caught during shutting down nodes, throw the
+ // first caught exception
if (firstCaughtException != null) {
+ this.logger.throwing(this.getClass().getName(), "shutdownNodes",
+ firstCaughtException);
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;
- }
- }
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "shutdownNodes");
}
public boolean startNodes(long maximumTimeToWaitInMillis)
throws TorProcessException {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "startNodes",
+ maximumTimeToWaitInMillis);
+
+ // check state
if (this.networkState != NetworkState.CONFIGURATIONS_WRITTEN) {
- throw new IllegalStateException();
+ IllegalStateException e = new IllegalStateException();
+ this.logger.throwing(this.getClass().getName(), "startNodes", e);
+ throw e;
}
+ // check parameter
if (maximumTimeToWaitInMillis < 0) {
- throw new IllegalArgumentException();
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(), "startNodes", e);
+ throw e;
}
// remember time when we begin starting the nodes
@@ -220,27 +664,45 @@
nodeStarter.start();
}
+ // wait for all node starts to complete
for (NodeStarter nodeStarter : allNodeStarters) {
+
+ // join node starts one after the other
try {
nodeStarter.join();
} catch (InterruptedException e) {
- // we have some kind of problem here!
+ // this happens?! we have some kind of problem here!
+ this.logger.log(Level.WARNING,
+ "Interrupt while joining node starter!");
+
+ // log exiting and return false
+ this.logger.exiting(this.getClass().getName(), "startNodes",
+ false);
return false;
}
+
+ // if node start threw an exception, throw that exception now
if (nodeStarter.caughtException != null) {
- this.networkState = NetworkState.NODES_SHUT_DOWN;
+ this.logger.throwing(this.getClass().getName(), "startNodes",
+ nodeStarter.caughtException);
throw nodeStarter.caughtException;
}
+
+ // if node start did not succeed in the given time, fail
if (!nodeStarter.success) {
this.logger.log(Level.WARNING,
"Starting nodes was not successful in "
+ (maximumTimeToWaitInMillis / 1000)
+ " seconds.", this.networkName);
+
+ // log exiting and return false
+ this.logger.exiting(this.getClass().getName(), "startNodes",
+ false);
return false;
}
}
- // check how long we took to start all nodes
+ // determine 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);
@@ -248,88 +710,33 @@
// change state
this.networkState = NetworkState.NODES_STARTED;
+ // log exiting and return true
+ this.logger.exiting(this.getClass().getName(), "startNodes", true);
return true;
}
- public ClientApplication createClient(String clientApplicationName,
- String targetAddress, int targetPort, int socksPort) {
- return new ClientApplicationImpl(this, clientApplicationName,
- targetAddress, targetPort, socksPort);
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ": networkName=\""
+ + this.networkName + "\", networkState="
+ + this.networkState.toString() + ", nodes.size()="
+ + this.nodes.size();
}
- public ProxyNode createProxy(String nodeName, int controlPort, int socksPort) {
+ public void writeConfigurations() throws TorProcessException {
- // TODO check parms
+ // log entering
+ this.logger.entering(this.getClass().getName(), "writeConfigurations");
- 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();
+ IllegalStateException e = new IllegalStateException();
+ this.logger.throwing(this.getClass().getName(),
+ "writeConfigurations", e);
+ throw e;
}
- // TODO don't we need to configure the nodes as private nodes, if we
- // have a directory node here?!
-
+ // write configurations for all nodes
for (ProxyNode node : this.nodes) {
if (node.getNodeState() == NodeState.CONFIGURING) {
node.writeConfiguration();
@@ -338,124 +745,8 @@
// 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;
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "writeConfigurations");
}
-
- /**
- * 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);
- }
- }
- }
}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -1,19 +1,16 @@
-/*
- * 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.FileOutputStream;
+import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
@@ -29,21 +26,23 @@
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.
+ * Implementation of <code>ProxyNode</code>.
*
* @author kloesing
*/
public class ProxyNodeImpl implements ProxyNode {
- // TODO make this configurable
+ /**
+ * Executable file containing Tor.
+ *
+ * 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.
+ * The <code>torrc</code> configuration file of this Tor node.
*/
- protected Logger logger;
+ protected File configFile;
/**
* Collects all configuration strings for this node during configuration
@@ -51,51 +50,86 @@
*/
protected List<String> configuration;
- public synchronized NodeState getNodeState() {
- return this.nodeState;
- }
+ /**
+ * Connection via Tor controller.
+ */
+ protected TorControlConnection conn;
- @Override
- public String toString() {
- return this.getClass().getSimpleName() + ": nodeName=\""
- + this.nodeName + "\", controlPort=" + this.controlPort
- + ", socksPort=" + this.socksPort;
- }
+ /**
+ * Port on which the Tor node will be listening for us as its controller.
+ */
+ protected int controlPort;
/**
- * Port on which node will be listening for SOCKS connections.
+ * Event manager to which all events concerning this node are notified.
*/
+ private EventManagerImpl eventManager;
+
+ /**
+ * Logger for this <code>ProxyNodeImpl</code> instance which is called
+ * "node." plus the name of this node.
+ */
+ protected Logger logger;
+
+ /**
+ * Network to which this node belongs.
+ */
+ protected NetworkImpl network;
+
+ /**
+ * Name of this node that is used as part of the working directory and as
+ * logger name of this node.
+ */
+ protected String nodeName;
+
+ /**
+ * The state of this node.
+ */
+ protected NodeState nodeState = NodeState.CONFIGURING;
+
+ /**
+ * Port on which the Tor node will be listening for SOCKS connection
+ * requests.
+ */
protected int socksPort;
- private EventManagerImpl eventManager;
+ /**
+ * Handle on the running Tor process that belongs to this node.
+ */
+ protected Process torProcess;
/**
- * LOGGING OK
+ * Directory in which all information concerning this node is stored.
+ */
+ protected File workingDir;
+
+ /**
+ * Creates a new <code>ProxyNodeImpl</code> and adds it to the given
+ * <code>network</code>, but does not yet write its configuration to disk
+ * or start the corresponding Tor process.
*
- * 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.
+ * The name of the new node 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.
- *
+ * Port on which the Tor node will be listening for us as its
+ * controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * Port on which the Tor node will be listening for SOCKS
+ * connection requests. May not be negative or greater than
+ * 65535.
* @throws IllegalArgumentException
- * If at least one of the parameters is null or has an invalid
- * value.
+ * If at least one of the parameters is <code>null</code> or
+ * has an invalid value.
*/
- public ProxyNodeImpl(NetworkImpl network, String nodeName, int controlPort,
+ ProxyNodeImpl(NetworkImpl network, String nodeName, int controlPort,
int socksPort) {
- // make sure that name is a valid logger name
+ // make sure that nodeName 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;
@@ -112,13 +146,7 @@
// 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);
+ IllegalArgumentException e = new IllegalArgumentException();
this.logger.throwing(this.getClass().getName(), "ProxyNodeImpl", e);
throw e;
}
@@ -132,23 +160,19 @@
// obtain reference on event manager from network
this.eventManager = network.getEventManagerImpl();
- // determine working directory
+ // create 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 + "\"!");
+ + this.workingDir.getAbsolutePath() + "\"");
// create reference on config file
this.configFile = new File(this.workingDir.getAbsolutePath()
+ File.separator + "torrc");
- // initialize configuration with general-purpose configurations
+ // initialize configuration with general-purpose configuration entries
this.configuration = new ArrayList<String>();
this.configuration.add("DataDirectory .");
this.configuration.add("SafeLogging 0");
@@ -176,6 +200,10 @@
this.configuration
.add("AllowInvalidNodes middle,rendezvous,exit,entry,introduction");
+ // tunnel dir connections
+ // this.configuration.add("TunnelDirConns 1");
+ // this.configuration.add("PreferTunneledDirConns 1");
+
// initialize state
this.nodeState = NodeState.CONFIGURING;
@@ -184,11 +212,180 @@
}
+ public void addConfiguration(String configurationString) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "addConfiguration");
+
+ // check state
+ if (this.nodeState != NodeState.CONFIGURING) {
+ IllegalStateException e = new IllegalStateException();
+ this.logger.throwing(this.getClass().getName(), "addConfiguration",
+ e);
+ throw e;
+ }
+
+ // check parameter
+ if (configurationString == null || configurationString.length() < 1) {
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(), "addConfiguration",
+ e);
+ throw e;
+ }
+
+ // add configuration string
+ this.configuration.add(configurationString);
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "addConfiguration");
+ }
+
+ public synchronized void addHiddenService(String serviceName,
+ int servicePort, int virtualPort) {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "addHiddenService",
+ new Object[] { serviceName, servicePort, 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!");
+ IllegalStateException e = new IllegalStateException(
+ "Hidden service can only be added when node is in state "
+ + "CONFIGURING!");
+ this.logger.throwing(this.getClass().getName(), "addHiddenService",
+ e);
+ throw e;
+ }
+
+ // check parameters
+ 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!");
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(), "addHiddenService",
+ e);
+ throw e;
+ }
+
+ // add hidden service using Tor controller
+ this.configuration.add("HiddenServiceDir "
+ + workingDir.getAbsolutePath() + File.separator + serviceName
+ + "\nHiddenServicePort " + virtualPort + " 127.0.0.1:"
+ + servicePort);
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "addHiddenService");
+ }
+
/**
- * LOGGING OK
+ * Adds a hidden service configuration to this node while the node is
+ * running.
*
- * @see de.uniba.wiai.lspi.puppetor.ProxyNode#configureDirServers(java.util.Set)
+ * TODO not fully implemented yet, but this should be possible both, during
+ * configuration and when running a network...
+ *
+ * @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, but need not be different from 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.
+ * @return The onion address string consisting of 16 base32 chars plus
+ * ".onion" for hidden service versions 0 and 1 or 16 base32 chars
+ * plus "." plus 24 base32 chars plus ".onion" for hidden service
+ * version 2.
*/
+ synchronized String addHiddenServiceUsingController(String serviceName,
+ int servicePort, int virtualPort) {
+
+ // TODO this method is not supported yet!
+ if (1 != 2) {
+ UnsupportedOperationException e = new UnsupportedOperationException();
+ this.logger.throwing(this.getClass().getName(),
+ "addHiddenServiceUsingController", e);
+ throw e;
+ }
+
+ // log entering
+ this.logger.entering(this.getClass().getName(),
+ "addHiddenServiceUsingController", new Object[] { serviceName,
+ servicePort, virtualPort });
+
+ // TODO check state
+
+ // check parameters
+ if (serviceName == null || serviceName.length() == 0 || servicePort < 0
+ || servicePort > 65535 || virtualPort < 0
+ || virtualPort > 65535) {
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(),
+ "addHiddenServiceUsingController", e);
+ throw e;
+ }
+
+ // 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) {
+ RuntimeException ex = new RuntimeException(
+ "IOException when trying to register a new "
+ + "hidden service", e);
+ this.logger.throwing(this.getClass().getName(),
+ "addHiddenServiceUsingController", ex);
+ throw ex;
+ }
+ this.logger.log(Level.FINE,
+ "Hidden service successfully registered at Tor proxy.");
+
+ //
+ File hiddenServiceFile = new File(workingDir.getAbsolutePath() + "\\"
+ + serviceName + "\\hostname");
+ if (!hiddenServiceFile.exists()) {
+ RuntimeException ex = new RuntimeException();
+ this.logger.throwing(this.getClass().getName(),
+ "addHiddenServiceUsingController", ex);
+ throw ex;
+ }
+ // read hostname from file
+ String address = null;
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(
+ hiddenServiceFile));
+ address = br.readLine();
+ br.close();
+ } catch (IOException e) {
+ RuntimeException ex = new RuntimeException(e);
+ this.logger.throwing(this.getClass().getName(),
+ "addHiddenServiceUsingController", ex);
+ throw ex;
+ }
+
+ return address;
+ }
+
public synchronized void configureDirServers(
Set<String> authorizedDirServerStrings) {
@@ -198,55 +395,194 @@
// 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);
+ IllegalArgumentException e = new IllegalArgumentException(
+ "Parameter may not be null!");
this.logger.throwing(this.getClass().getName(),
"configureDirServers", e);
throw e;
}
// add to configuration
+ int entriesBefore = this.configuration.size();
this.configuration.addAll(authorizedDirServerStrings);
this.logger.log(Level.FINE, "Added "
+ authorizedDirServerStrings.size()
- + " DirServer entries to configuration!");
+ + " DirServer entries to configuration, having "
+ + entriesBefore + " entries before and "
+ + this.configuration.size()
+ + " entries afterwards (duplicates possible)!");
// log exiting
this.logger.exiting(this.getClass().getName(), "configureDirServers");
+ }
+ public synchronized void copyDescriptorTo(ProxyNode otherNode)
+ throws TorProcessException {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "copyDescriptorTo",
+ otherNode);
+
+ // copy files
+ try {
+ File otherWorkingDir = ((ProxyNodeImpl) otherNode).workingDir;
+ for (File f : workingDir.listFiles()) {
+ if (f.getName().contains(".rsd")) {
+ File dst = new File(otherWorkingDir.getAbsolutePath()
+ + File.separator + f.getName());
+ this.logger.log(Level.FINE, "Copying '"
+ + f.getAbsolutePath() + "' to '"
+ + dst.getAbsolutePath() + "'...");
+ InputStream in = new FileInputStream(f);
+ OutputStream out = new FileOutputStream(dst);
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+ in.close();
+ out.close();
+ }
+ }
+ } catch (IOException e) {
+ TorProcessException ex = new TorProcessException(
+ "Error copying descriptors");
+ this.logger.throwing(this.getClass().getName(), "copyDescriptorTo",
+ ex);
+ throw ex;
+ }
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "copyDescriptorTo");
}
- /**
- * Network to which this node belongs.
- */
- protected NetworkImpl network;
+ public synchronized String getNodeName() {
+ return this.nodeName;
+ }
- /**
- * Name of this node that is used as part of the node's working directory
- * and logger.
- */
- protected String nodeName;
+ public synchronized NodeState getNodeState() {
+ return this.nodeState;
+ }
- /**
- * Port on which the process will be listening for us as its controller.
- */
- protected int controlPort;
+ public synchronized String getOnionAddress(String serviceName, int version)
+ throws TorProcessException {
- /**
- * Directory in which all information concerning this node is stored.
- */
- protected File workingDir;
+ // log entering
+ this.logger.entering(this.getClass().getName(), "getOnionAddress",
+ new Object[] { serviceName, version });
- /**
- * LOGGING OK
- *
- * @param maximumTimeToWaitInMillis <
- * 0 bedeutet unendlich lange
- *
- */
+ // check parameter
+ if (serviceName == null || serviceName.length() == 0) {
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(), "getOnionAddress",
+ e);
+ throw e;
+ }
+
+ // check if hidden service directory exists
+ File hiddenServiceFile = new File(workingDir.getAbsolutePath()
+ + File.separator + serviceName + File.separator + "hostname"
+ + ((version <= 1) ? "" : "2"));
+ if (!hiddenServiceFile.exists()) {
+ this.logger.log(Level.SEVERE,
+ "Hidden service directory or hostname file does not exist: "
+ + hiddenServiceFile.getAbsolutePath());
+
+ TorProcessException e = new TorProcessException(
+ "Hidden service directory or hostname file does not exist: "
+ + hiddenServiceFile.getAbsolutePath());
+ this.logger.throwing(this.getClass().getName(), "getOnionAddress",
+ e);
+ throw e;
+ }
+
+ // read hostname from file
+ String address = null;
+ try {
+ BufferedReader br = new BufferedReader(new FileReader(
+ hiddenServiceFile));
+ address = br.readLine();
+ br.close();
+ } catch (IOException e) {
+ TorProcessException ex = new TorProcessException(
+ "Could not read hostname file!", e);
+ this.logger.throwing(this.getClass().getName(), "getOnionAddress",
+ ex);
+ throw ex;
+ }
+
+ // log exiting and return address
+ this.logger.exiting(this.getClass().getName(), "getOnionAddress",
+ address);
+ return address;
+ }
+
+ public synchronized void hup() throws TorProcessException {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "hup");
+
+ // check state
+ if (this.nodeState != NodeState.RUNNING) {
+ IllegalStateException e = new IllegalStateException(
+ "Cannot hup a process when it's not running!");
+ this.logger.throwing(this.getClass().getName(), "hup", e);
+ throw e;
+ }
+
+ // send HUP signal to Tor process
+ try {
+ this.conn.signal("HUP");
+ } catch (IOException e) {
+ TorProcessException ex = new TorProcessException(
+ "Could not send the command HUP to the Tor process!", e);
+ this.logger.throwing(this.getClass().getName(), "hup", ex);
+ throw ex;
+ } catch (NullPointerException e) {
+ // TODO sometimes, this throws a NullPointerException...
+ this.logger.log(Level.SEVERE,
+ "NullPointerException while sending HUP signal to node "
+ + this.toString());
+ throw e;
+ }
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "hup");
+ }
+
+ public synchronized void shutdown() throws TorProcessException {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "shutdown");
+
+ // check state
+ if (this.nodeState != NodeState.RUNNING) {
+ IllegalStateException e = new IllegalStateException();
+ this.logger.throwing(this.getClass().getName(), "shutdown", e);
+ throw e;
+ }
+
+ // we cannot simply kill the Tor 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.shutdownTor("SHUTDOWN");
+ this.conn.shutdownTor("SHUTDOWN");
+ } catch (IOException e) {
+ TorProcessException ex = new TorProcessException(
+ "Could not send shutdown command to Tor process!", e);
+ this.logger.throwing(this.getClass().getName(), "shutdown", ex);
+ throw ex;
+ }
+
+ // change state
+ this.nodeState = NodeState.SHUT_DOWN;
+
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "shutdown");
+ }
+
public synchronized boolean startNode(long maximumTimeToWaitInMillis)
throws TorProcessException {
@@ -256,12 +592,9 @@
// 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;
}
@@ -275,13 +608,10 @@
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;
+ TorProcessException ex = new TorProcessException(reason, e);
+ this.logger.throwing(this.getClass().getName(), "startNode", ex);
+ throw ex;
}
// start thread to parse output
@@ -294,10 +624,9 @@
// log entering
logger.entering(this.getClass().getName(), "run");
+ // read output from Tor to parse it
String line = null;
boolean waitingForControlPort = true;
-
- // read output from Tor
try {
while ((line = br.readLine()) != null) {
@@ -319,7 +648,7 @@
// don't throw this exception, nobody will catch it!
String reason = "IOException when reading output from Tor "
+ "process!";
- logger.log(Level.FINE, reason, e);
+ logger.log(Level.WARNING, reason, e);
}
// log exiting
@@ -372,11 +701,14 @@
// be sure that Tor is ready, especially if computer is very busy and
// many nodes are created
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e2) {
- }
-
+ // TODO don't wait, because we could miss log messages while waiting
+ // TODO should we better only parse the process output instead of
+ // parsing log messages from the controller?!
+ // try {
+ // Thread.sleep(1000);
+ // } catch (InterruptedException e2) {
+ // }
+
// connect to the controller
this.logger.log(Level.FINER, "Connecting to control port...");
try {
@@ -384,13 +716,11 @@
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 " + controlPort + "!";
- TorProcessException e1 = new TorProcessException(reason, e);
- this.logger.log(Level.SEVERE, reason, e1);
- this.logger.throwing(this.getClass().getName(), "startNode", e1);
- throw e1;
+ String reason = "Could not connect to control port " + controlPort
+ + "!";
+ TorProcessException ex = new TorProcessException(reason, e);
+ this.logger.throwing(this.getClass().getName(), "startNode", ex);
+ throw ex;
}
this.logger.log(Level.FINE, "Connected to control port successfully!");
@@ -398,14 +728,16 @@
EventHandler handler = new EventHandler() {
public void bandwidthUsed(long read, long written) {
- logger.log(Level.FINEST, "bandwidthUsed(read, written) = "
- + read + ", " + written);
+ // TODO do we need this information?
+ // 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);
+ // TODO do we need this information?
+ // logger.log(Level.FINEST,
+ // "circuitStatus(status, circID, path) = " + status
+ // + ", " + circID + ", " + path);
}
public void message(String severity, String msg) {
@@ -429,51 +761,50 @@
}
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());
+ // TODO do we need this information?
+ // 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);
+ // TODO do we need this information?
+ // 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);
+ // TODO do we need this information?
+ // 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);
+ // TODO do we need this information?
+ // logger.log(Level.FINEST, "unrecognized(type, msg) = " + type
+ // + ", " + msg);
}
};
- conn.setEventHandler(handler);
-
- // register for logging events of level INFO and higher
+ this.conn.setEventHandler(handler);
+
+ // register for logging events of level INFO and higher using controller
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;
+ TorProcessException ex = new TorProcessException(reason, e);
+ this.logger.throwing(this.getClass().getName(), "startNode", ex);
+ throw ex;
}
this.logger.log(Level.FINE, "Registered event handler at Tor process!");
@@ -485,92 +816,24 @@
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() + File.separator + serviceName + "\nHiddenServicePort " + virtualPort
- + " 127.0.0.1:" + servicePort);
-
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ": nodeName=\""
+ + this.nodeName + "\", controlPort=" + this.controlPort
+ + ", socksPort=" + this.socksPort;
}
- public synchronized String getOnionAddress(String serviceName)
- throws TorProcessException {
+ public synchronized void writeConfiguration() throws TorProcessException {
- // check parameter
- if (serviceName == null || serviceName.length() == 0) {
- this.logger.log(Level.SEVERE, "Illegal argument!");
- throw new IllegalArgumentException();
- }
+ // log entering
+ this.logger.entering(this.getClass().getName(), "writeConfiguration");
- // 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();
+ IllegalStateException e = new IllegalStateException();
+ this.logger.throwing(this.getClass().getName(),
+ "writeConfiguration", e);
+ throw e;
}
// write config file
@@ -581,159 +844,17 @@
}
bw.close();
} catch (IOException e) {
- throw new TorProcessException("Could not write configuration!", e);
+ TorProcessException ex = new TorProcessException(
+ "Could not write configuration!", e);
+ this.logger.throwing(this.getClass().getName(),
+ "writeConfiguration", ex);
+ throw ex;
}
// 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.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;
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "writeConfiguration");
}
-
- // /**
- // * 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) {
- this.logger.log(Level.WARNING, "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;
- }
-
}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -1,8 +1,3 @@
-/*
- * 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;
@@ -19,34 +14,72 @@
import de.uniba.wiai.lspi.puppetor.RouterNode;
import de.uniba.wiai.lspi.puppetor.TorProcessException;
+/**
+ * Implementation of <code>RouterNode</code>.
+ *
+ * @author kloesing
+ *
+ */
public class RouterNodeImpl extends ProxyNodeImpl implements RouterNode {
- protected final String localIpAddress = "127.0.0.1";
-
/**
- * OR port of this router.
+ * Port on which the Tor node will be listening for directory requests from
+ * other Tor nodes.
*/
- protected int orPort;
+ protected int dirPort;
/**
- * Directory port of this router.
+ * The fingerprint of this node that is determined as hash value of its
+ * onion key.
*/
- protected int dirPort;
+ protected String fingerprint;
/**
- * This node's fingerprint that is determined as hash value of this node's
- * onion routing key.
+ * The IP address of local nodes (typically <code>localhost</code> or
+ * <code>127.0.0.1</code>).
*/
- protected String fingerprint;
+ protected final String localIpAddress = "127.0.0.1";
- @Override
- public String toString() {
- return super.toString() + ", orPort=" + this.orPort + ", dirPort="
- + this.dirPort;
- }
+ /**
+ * Port on which the Tor node will be listening for onion requests by other
+ * Tor nodes.
+ */
+ protected int orPort;
- public RouterNodeImpl(NetworkImpl network, String nodeName,
- int controlPort, int socksPort, int orPort, int dirPort) {
+ /**
+ * Creates a new <code>RouterNodeImpl</code> and adds it to the given
+ * <code>network</code>, but does not yet write its configuration to disk
+ * or start the corresponding Tor process.
+ *
+ * @param network
+ * Network configuration to which this node belongs.
+ * @param nodeName
+ * The name of the new node which may only consist of between 1
+ * and 19 alpha-numeric characters.
+ * @param controlPort
+ * Port on which the Tor node will be listening for us as its
+ * controller. May not be negative or greater than 65535.
+ * @param socksPort
+ * Port on which the Tor node will be listening for SOCKS
+ * connection requests. May not be negative or greater than
+ * 65535.
+ * @param orPort
+ * Port on which the Tor node will be listening for onion
+ * requests by other Tor nodes. May not be negative or greater
+ * than 65535.
+ * @param dirPort
+ * Port on which the Tor node will be listening for directory
+ * requests from other Tor nodes. May not be negative or greater
+ * than 65535.
+ * @throws IllegalArgumentException
+ * If at least one of the parameters is <code>null</code> or
+ * has an invalid value.
+ */
+ RouterNodeImpl(NetworkImpl network, String nodeName, int controlPort,
+ int socksPort, int orPort, int dirPort) {
+
+ // create superclass instance; parameter checking is done in super
+ // constructor
super(network, nodeName, controlPort, socksPort);
// log entering
@@ -56,7 +89,10 @@
// check parameters
if (orPort < 0 || orPort > 65535 || dirPort < 0 || dirPort > 65535) {
- throw new IllegalArgumentException();
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger
+ .throwing(this.getClass().getName(), "RouterNodeImpl", e);
+ throw e;
}
// remember parameters
@@ -71,7 +107,7 @@
// all routers mirror the directory
this.configuration.add("DirPort " + dirPort);
- // this node's address should be localhost and not guessed by Tor
+ // the address of this node should be localhost and not guessed by Tor
this.configuration.add("Address " + localIpAddress);
// the OR port may only be contacted locally
@@ -88,24 +124,26 @@
// 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 {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "determineFingerprint");
+
+ // check if we can answer the request immediately
if (this.fingerprint != null) {
- // we can answer the request immediately
+ // log exiting and return fingerprint
+ this.logger.exiting(this.getClass().getName(),
+ "determineFingerprint", this.fingerprint);
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
@@ -125,8 +163,11 @@
}
bw.close();
} catch (IOException e) {
- throw new TorProcessException(
+ TorProcessException ex = new TorProcessException(
"Could not write temporary config file!", e);
+ this.logger.throwing(this.getClass().getName(),
+ "determineFingerprint", ex);
+ throw ex;
}
// start process with option --list-fingerprint
@@ -135,13 +176,15 @@
processBuilder.directory(this.workingDir);
processBuilder.redirectErrorStream(true);
Process tmpProcess = null;
-
try {
tmpProcess = processBuilder.start();
} catch (IOException e) {
- throw new TorProcessException(
+ TorProcessException ex = new TorProcessException(
"Could not start Tor process temporarily with "
+ "--list-fingerprint option!", e);
+ this.logger.throwing(this.getClass().getName(),
+ "determineFingerprint", ex);
+ throw ex;
}
// start thread to parse output
@@ -150,14 +193,17 @@
Thread outputThread = new Thread(new Runnable() {
public void run() {
try {
- // Read output from tor
+ // 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?
+ // TODO how to handle?
+ logger.log(Level.SEVERE,
+ "I/O exception while parsing output of temporary "
+ + "Tor process!");
}
}
});
@@ -169,11 +215,12 @@
try {
tmpProcess.waitFor();
} catch (InterruptedException e) {
- // TODO handle?
+ // TODO how to handle?
+ this.logger.log(Level.WARNING,
+ "Temporary Tor process was interrupted!", e);
}
// read fingerprint from file
-
File fingerprintFile = new File(this.workingDir.getAbsolutePath()
+ File.separator + "fingerprint");
try {
@@ -182,10 +229,22 @@
this.fingerprint = br2.readLine();
br2.close();
} catch (IOException e) {
- throw new TorProcessException(
+ TorProcessException ex = new TorProcessException(
"Could not read fingerprint from file!", e);
+ this.logger.throwing(this.getClass().getName(),
+ "determineFingerprint", ex);
+ throw ex;
}
-
+
+ // log exiting and return fingerprint
+ this.logger.exiting(this.getClass().getName(), "determineFingerprint",
+ this.fingerprint);
return this.fingerprint;
}
+
+ @Override
+ public String toString() {
+ return super.toString() + ", orPort=" + this.orPort + ", dirPort="
+ + this.dirPort;
+ }
}
Modified: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java 2007-06-27 20:52:42 UTC (rev 10686)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java 2007-06-27 22:09:47 UTC (rev 10687)
@@ -1,8 +1,3 @@
-/*
- * 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;
@@ -17,100 +12,109 @@
import de.uniba.wiai.lspi.puppetor.Event;
import de.uniba.wiai.lspi.puppetor.ServerApplication;
+/**
+ * Implementation of <code>ServerApplication</code>.
+ *
+ * @author kloesing
+ */
public class ServerApplicationImpl implements ServerApplication {
/**
- * logger für diesen server
+ * Internal thread class that is used to process an incoming request.
*/
- private Logger logger;
+ private class HandlerThread extends Thread {
- private EventManagerImpl eventManager;
+ /**
+ * Accepted socket on which the request came in.
+ */
+ private Socket handleSocket = null;
- private int serverPort;
+ /**
+ * Creates a new thread to handle the request coming in on
+ * <code>handleSocket</code>, but does not start handling it.
+ *
+ * @param handleSocket
+ * Accepted socket on which the request came in.
+ */
+ public HandlerThread(Socket handleSocket) {
- /**
- * erzeugt neue server app innerhalb der jvm, fängt aber noch nicht an mit
- * listening.
- *
- */
- ServerApplicationImpl(NetworkImpl network, String serverApplicationName,
- int serverPort) {
+ // log entering
+ logger.entering(this.getClass().getName(), "HandlerThread",
+ handleSocket);
- // TODO make sure that name is loggable!!
+ // remember parameter
+ this.handleSocket = handleSocket;
- // 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();
+ // log exiting
+ logger.exiting(this.getClass().getName(), "HandlerThread");
}
- // remember parameters
- this.serverPort = serverPort;
+ @Override
+ public void run() {
- // obtain reference on event manager
- this.eventManager = network.getEventManagerImpl();
+ // log entering
+ logger.entering(this.getClass().getName(), "run");
- // log exiting
- this.logger.exiting(this.getClass().getName(), "ClientApplicationImpl");
- }
+ try {
- private Thread serverThread;
+ // wait for request (don't mind the content)
+ BufferedReader in = new BufferedReader(new InputStreamReader(
+ handleSocket.getInputStream()));
+ in.read();
- public void stopListening() {
+ // send event to event manager
+ eventManager.observeEvent(ServerApplicationImpl.this,
+ Event.APPLICATION_REQUEST_RECEIVED);
- // log entering
- this.logger.entering(this.getClass().getName(), "stopListening");
+ // write response
+ PrintStream out = new PrintStream(handleSocket
+ .getOutputStream());
+ out.print("HTTP/1.0 200 OK\r\n");
- // check if a request is running
- if (this.serverThread == null) {
- throw new IllegalStateException(
- "We did not start listening before!");
- }
+ } catch (IOException e) {
+ logger.log(Level.SEVERE,
+ "I/O exception while handling incoming request!");
+ // we can't do more, because nobody takes notice of this thread.
- // log this event
- this.logger.log(Level.FINE, "Shutting down server");
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ return;
+ // TODO do we need more?
+ }
- // interrupt thread
- this.serverThread.interrupt();
+ // close socket
+ try {
+ handleSocket.close();
+ } catch (IOException e) {
+ logger
+ .log(Level.WARNING,
+ "I/O exception while closing socket!");
- // log exiting
- this.logger.exiting(this.getClass().getName(), "stopListening");
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ return;
+ }
- }
-
- 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!");
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
}
-
- // 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");
}
+ /**
+ * Internal thread class that is used to listen for requests.
+ */
private class ListenThread extends Thread {
+ /**
+ * Flag to remember whether this thread listens for requests at the
+ * moment (<code>true</code>), or has been stopped (<code>false</code>).
+ */
private boolean connected;
+ /**
+ * Creates a new thread to listen for requests, but does not start
+ * listening, yet.
+ */
ListenThread() {
// log entering
@@ -123,23 +127,6 @@
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() {
@@ -148,75 +135,196 @@
try {
+ // create server socket
ServerSocket serverSocket = null;
-
try {
serverSocket = new ServerSocket(serverPort);
} catch (IOException ioe) {
- logger.log(Level.WARNING, "Can't open server socket on port "
- + serverPort + "!");
+ logger.log(Level.SEVERE,
+ "Can't open server socket on port " + serverPort
+ + "!");
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
+ return;
}
- logger.log(Level.FINE, "Listening on port " + serverPort + "...");
+
+ // as long as we are connected, accept incoming requests
+ logger.log(Level.FINE, "Listening on port " + serverPort
+ + "...");
while (connected) {
+ Socket incomingConnection = null;
try {
- Socket incomingConnection = serverSocket.accept();
- HandlerThread handler = new HandlerThread(
- incomingConnection);
- new Thread(handler).start();
+ incomingConnection = serverSocket.accept();
} catch (Exception e) {
+ logger
+ .log(
+ Level.SEVERE,
+ "Exception while accepting socket requests! Stopping listening!",
+ e);
+ break;
}
+ new HandlerThread(incomingConnection).start();
}
} 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");
}
+ // mark as disconnected
+ this.connected = false;
+
+ // log exiting
+ logger.exiting(this.getClass().getName(), "run");
}
+ /**
+ * Stops listening on server socket.
+ */
+ 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");
+ }
}
- private class HandlerThread implements Runnable {
+ /**
+ * Event manager to which all events concerning this server application are
+ * notified.
+ */
+ private EventManagerImpl eventManager;
- private Socket handleSocket = null;
+ /**
+ * Logger for this server.
+ */
+ private Logger logger;
- public HandlerThread(Socket requestSocket) {
- handleSocket = requestSocket;
+ /**
+ * Name of this server application that is used as logger name of this node.
+ */
+ private String serverApplicationName;
+
+ /**
+ * Port on which this server will listen for incoming requests.
+ */
+ private int serverPort;
+
+ /**
+ * Thread that listens for requests in the background.
+ */
+ private Thread serverThread;
+
+ /**
+ * Creates a new HTTP server application within this JVM, but does not yet
+ * listen for incoming requests.
+ *
+ * @param network
+ * Network to which this HTTP server belongs; at the moment this
+ * is only used to determine the event manager instance.
+ * @param serverApplicationName
+ * Name of this server that is used as part of the logger name.
+ * @param serverPort
+ * Port on which this server will listen for incoming requests.
+ * @throws IllegalArgumentException
+ * If at least one of the parameters is <code>null</code> or
+ * has an invalid value.
+ */
+ ServerApplicationImpl(NetworkImpl network, String serverApplicationName,
+ int serverPort) {
+
+ // check if serverApplicationName can be used as logger name
+ if (serverApplicationName == null
+ || serverApplicationName.length() == 0) {
+ throw new IllegalArgumentException(
+ "Invalid serverApplicationName: " + serverApplicationName);
}
- public void run() {
+ // create logger
+ this.logger = Logger.getLogger("application." + serverApplicationName);
- try {
+ // log entering
+ this.logger.entering(this.getClass().getName(),
+ "ServerApplicationImpl", new Object[] { network,
+ serverApplicationName, serverPort });
- // wait for request (don't mind content)
- BufferedReader in = new BufferedReader(new InputStreamReader(
- handleSocket.getInputStream()));
- in.read();
+ // check parameters
+ if (network == null || serverPort < 0 || serverPort > 65535) {
+ IllegalArgumentException e = new IllegalArgumentException();
+ this.logger.throwing(this.getClass().getName(),
+ "ServerApplicationImpl", e);
+ throw e;
+ }
- // send event to event manager
- eventManager.observeEvent(ServerApplicationImpl.this,
- Event.APPLICATION_REQUEST_RECEIVED);
+ // remember parameters
+ this.serverApplicationName = serverApplicationName;
+ this.serverPort = serverPort;
- // write response
- PrintStream out = new PrintStream(handleSocket
- .getOutputStream());
- out.print("HTTP/1.0 200 OK\r\n");
+ // obtain reference on event manager
+ this.eventManager = network.getEventManagerImpl();
- } catch (Exception e) {
- e.printStackTrace();
- }
+ // log exiting
+ this.logger.exiting(this.getClass().getName(), "ServerApplicationImpl");
+ }
- try {
- // handleSocket.getInputStream().close();
- // handleSocket.getOutputStream().flush();
- // handleSocket.getOutputStream().close();
- handleSocket.close();
- } catch (Exception e) {
- }
+ public synchronized void listen() {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "listen");
+
+ // check if we are already listening
+ if (this.serverThread != null) {
+ IllegalStateException e = new IllegalStateException(
+ "We are already listening!");
+ this.logger.throwing(this.getClass().getName(), "listen", e);
+ throw e;
}
+
+ // 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");
}
+
+ public synchronized void stopListening() {
+
+ // log entering
+ this.logger.entering(this.getClass().getName(), "stopListening");
+
+ // check if a request is running
+ if (this.serverThread == null) {
+ IllegalStateException e = new IllegalStateException(
+ "We did not start listening before!");
+ this.logger.throwing(this.getClass().getName(), "stopListening", e);
+ throw e;
+ }
+
+ // 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");
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ": serverApplicationName=\""
+ + this.serverApplicationName + "\", serverPort="
+ + this.serverPort;
+ }
}