pax_global_header00006660000000000000000000000064124156140020014505gustar00rootroot0000000000000052 comment=597f8378f6f4f3de570b8e1064c2e4cb8d67fbd0 fteproxy-0.2.19/000077500000000000000000000000001241561400200134565ustar00rootroot00000000000000fteproxy-0.2.19/.gitignore000066400000000000000000000001621241561400200154450ustar00rootroot00000000000000build dist fteproxy/*.so thirdparty/re2 fteproxy/*.pyc fteproxy/tests/*.pyc fteproxy/defs/*.pyc fteproxy.egg-info fteproxy-0.2.19/.travis.yml000066400000000000000000000003731241561400200155720ustar00rootroot00000000000000language: - python - cpp compiler: - gcc - clang before_install: - sudo apt-get update -qq - sudo apt-get install -y libgmp-dev python: - "2.6" - "2.7" install: - "pip install -r requirements.txt" script: - "python setup.py test" fteproxy-0.2.19/BUILDING.md000066400000000000000000000031571241561400200152030ustar00rootroot00000000000000fteproxy Build Instructions =========================== Ubuntu/Debian ------------- Install the following packages. ``` sudo apt-get install python-dev python-pip upx git sudo pip install --upgrade fte pyptlib obfsproxy twisted pyinstaller ``` Note: If you are on Ubuntu 13.10, there is a bug with pyinstaller/pycrypto, such that binary packages do not build properly. Please install development version 52fa29ce of pyinstaller [3], instead of using pip. See [4] for more details. Then, clone and build fteproxy. ``` git clone https://github.com/kpdyer/fteproxy.git cd fteproxy make make test make dist ``` OSX --- Install homebrew [1], if you don't have it already. ``` ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)" ``` Install the following packages. ``` brew install --build-from-source python gmp git upx sudo pip install --upgrade fte pyptlib obfsproxy twisted pyinstaller ``` Then, clone and build fteproxy. ``` git clone https://github.com/kpdyer/fteproxy.git cd fteproxy make make test make dist ``` Note: if on OSX 10.9, you may experience a ```clang: warning: argument unused during compilation: '-mno-fused-madd'``` error. This can be resolved by setting the following evnironmental variables: ``` export CFLAGS=-Qunused-arguments export CPPFLAGS=-Qunused-arguments ``` Windows ------- If you must build fteproxy on Windows, please see [2] for guidance. ### References * [1] http://brew.sh/ * [2] https://github.com/kpdyer/fteproxy-builder/blob/master/build/windows-i386/build_fteproxy.sh * [3] https://github.com/pyinstaller/pyinstaller * [4] https://github.com/kpdyer/fteproxy/issues/124 fteproxy-0.2.19/COPYING000066400000000000000000000260731241561400200145210ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.fteproxy-0.2.19/LICENSE000066400000000000000000000260731241561400200144730ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.fteproxy-0.2.19/MANIFEST.in000066400000000000000000000001011241561400200152040ustar00rootroot00000000000000include LICENSE include fteproxy/VERSION include fteproxy/defs/* fteproxy-0.2.19/README000066400000000000000000000020051241561400200143330ustar00rootroot00000000000000Requires: Twisted (https://twistedmatrix.com/), obfsproxy (https://pypi.python.org/pypi/obfsproxy), pyptlib (https://pypi.python.org/pypi/pyptlib) and fte (https://pypi.python.org/pypi/fte). fteproxy provides transport-layer protection to resist keyword filtering, censorship and discriminatory routing policies. Its job is to relay datastreams, such as web browsing traffic, by encoding streams as messages that match a user-specified regular expression. See https://fteproxy.org/ for more information. Format-Transforming Encryption (FTE) is a cryptographic primitive explored in the paper *Protocol Misidentification Made Easy with Format-Transforming Encryption* [1]. FTE allows a user to specify the format of their ouput ciphertexts using regular expressions. The libfte library implements the primitive presented in [1]. [1] Protocol Misidentification Made Easy with Format-Transforming Encryption, Kevin P. Dyer, Scott E. Coull, Thomas Ristenpart and Thomas Shrimpton, https://kpdyer.com/publications/ccs2013-fte.pdf fteproxy-0.2.19/README.md000066400000000000000000000035241241561400200147410ustar00rootroot00000000000000fteproxy ======== [![Build Status](https://travis-ci.org/kpdyer/fteproxy.svg?branch=master)](https://travis-ci.org/kpdyer/fteproxy) * homepage: https://fteproxy.org * source code: https://github.com/kpdyer/fteproxy * publication: https://kpdyer.com/publications/ccs2013-fte.pdf Overview -------- fteproxy provides transport-layer protection to resist keyword filtering, censorship and discrimantory routing policies. Its job is to relay datastreams, such as web browsing traffic, by encoding the stream into messages that satisfy a user-specified regular expression. fteproxy is powered by Format-Transforming Encryption [1] and was presented at CCS 2013. [1] [Protocol Misidentification Made Easy with Format-Transforming Encryption](https://kpdyer.com/publications/ccs2013-fte.pdf), Kevin P. Dyer, Scott E. Coull, Thomas Ristenpart and Thomas Shrimpton Quick Start ----------- On Linux/OSX, use pip to install fteproxy. ```console pip install fteproxy ``` On Windows, download pre-compiled binaries, located at: https://fteproxy.org/download Dependencies -------- Dependencies for building from source: * Python 2.7.x: https://python.org/ * fte 0.0.x: https://pypi.python.org/pypi/fte * pyptlib 0.0.x: https://gitweb.torproject.org/pluggable-transports/pyptlib.git * obfsproxy 0.2.x: https://gitweb.torproject.org/pluggable-transports/obfsproxy.git * Twisted 13.2.x: https://twistedmatrix.com/ Running ------- For platform-specific examples of how to install dependencies see BUILDING.md. There is nothing to build for fteproxy --- it is Python-only project. To run fteproxy, you need to do only the following. ``` git clone https://github.com/kpdyer/fteproxy.git cd fteproxy ./bin/fteproxy ``` Documentation ------------- See: https://fteproxy.org/documentation Author ------ Please contact Kevin P. Dyer (kpdyer@gmail.com), if you have any questions. fteproxy-0.2.19/bin/000077500000000000000000000000001241561400200142265ustar00rootroot00000000000000fteproxy-0.2.19/bin/fteproxy000077500000000000000000000005511241561400200160350ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import os if hasattr(sys, "frozen"): sys.path.append( os.path.abspath(os.path.join(os.path.dirname(sys.executable)))) else: sys.path.append( os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) import fteproxy.cli if __name__ == '__main__': fteproxy.cli.main() fteproxy-0.2.19/doc/000077500000000000000000000000001241561400200142235ustar00rootroot00000000000000fteproxy-0.2.19/doc/Makefile000066400000000000000000000130671241561400200156720ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Format-TransformingEncryptionfteproxy.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Format-TransformingEncryptionfteproxy.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Format-TransformingEncryptionFTE" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Format-TransformingEncryptionFTE" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." fteproxy-0.2.19/doc/make.bat000066400000000000000000000120571241561400200156350ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Format-TransformingEncryptionfteproxy.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Format-TransformingEncryptionfteproxy.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end fteproxy-0.2.19/doc/source/000077500000000000000000000000001241561400200155235ustar00rootroot00000000000000fteproxy-0.2.19/doc/source/__init__.rst000066400000000000000000000043001241561400200200110ustar00rootroot00000000000000``fteproxy.wrap_socket`` ************************ Overview -------- The ``fteproxy.wrap_socket`` function is useful for rapidly integrating FTE into existing applications. The interface is inspired by ``ssl.wrap_socket`` [1]. .. [1] http://docs.python.org/2/library/ssl.html Interface --------- .. autofunction:: fteproxy.wrap_socket Example ------- An example FTE-powered client-server chat application. The highlighted lines are the only lines introduced to the example chat application at for Python's socket module [2]. .. [2] http://docs.python.org/2/library/socket.html .. code-block:: python :emphasize-lines: 3,5,6,11,12,13,14,15 # FTE-Powered echo server program import socket import fteproxy client_server_regex = '^(0|1)+$' server_client_regex = '^(A|B)+$' HOST = '' # Symbolic name meaning all available interfaces PORT = 50007 # Arbitrary non-privileged port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = fteproxy.wrap_socket(s, outgoing_regex=server_client_regex, outgoing_fixed_slice=256, incoming_regex=client_server_regex, incoming_fixed_slice=256) s.bind((HOST, PORT)) s.listen(1) conn, addr = s.accept() print 'Connected by', addr while 1: data = conn.recv(1024) if not data: break conn.sendall(data) conn.close() .. code-block:: python :emphasize-lines: 3,5,6,11,12,13,14,15 # FTE-Powered echo client program import socket import fteproxy client_server_regex = '^(0|1)+$' server_client_regex = '^(A|B)+$' HOST = '127.0.0.1' # The remote host PORT = 50007 # The same port as used by the server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = fteproxy.wrap_socket(s, outgoing_regex=client_server_regex, outgoing_fixed_slice=256, incoming_regex=server_client_regex, incoming_fixed_slice=256) s.connect((HOST, PORT)) s.sendall('Hello, world') data = s.recv(1024) s.close() print 'Received', repr(data) fteproxy-0.2.19/doc/source/_static000066400000000000000000000000001241561400200170620ustar00rootroot00000000000000fteproxy-0.2.19/doc/source/client.rst000066400000000000000000000036131241561400200175360ustar00rootroot00000000000000:mod:`fteproxy.client` Module ***************************** Overview -------- It's the responsibility of the client listener to broker incoming/ outgoing connections on the client-side of an fteproxy setup. Incoming connections are not encapsulated by FTE, as they are from a client-side application, such as Firefox. Outgoing connections will be destined to an fteproxy server. They will be encapsulated by fteproxy. The ``fteproxy.client.listener`` class extends ``fteproxy.relay.listener``. See ``fteproxy.relay.listener`` for more information, which is also the base class for ``fteproxy.server.listener``. The ``fteproxy.relay.listener`` class extends ``threading.Thread``, hence we invoke the fteproxy client via fteproxy.listener.client.start(). Interface --------- .. automodule:: fteproxy.client :members: :undoc-members: :show-inheritance: Examples -------- Start the fteproxy client with default configuration parameters. .. code-block:: python import fteproxy.client.listener client = fteproxy.client.listener() client.start() client.join(10) # run for 10 seconds client.stop() Start the fteproxy client listening on client-side port ``127.0.0.1:8888``. .. code-block:: python import fteproxy.conf import fteproxy.client.listener fteproxy.conf.setValue('runtime.client.ip', '127.0.0.1') fteproxy.conf.setValue('runtime.client.port', 8888) client = fteproxy.client.listener() client.start() client.join(10) # run for 10 seconds client.stop() Start the fteproxy client and connect to remote server ``myfteserver:80``. .. code-block:: python import fteproxy.conf import fteproxy.client.listener fteproxy.conf.setValue('runtime.server.ip', 'myfteserver') fteproxy.conf.setValue('runtime.server.port', 80) client = fteproxy.client.listener() client.start() client.join(10) # run for 10 seconds client.stop() fteproxy-0.2.19/doc/source/conf.py000066400000000000000000000201161241561400200170220ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Format-Transforming Encryption (FTE) documentation build configuration file, created by # sphinx-quickstart on Wed Oct 23 04:52:22 2013. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.append('..') sys.path.append('../..') sys.path.append('../../..') # -- General configuration ----------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.pngmath'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'fteproxy' copyright = u'2013, Kevin P. Dyer' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. with open('../../fteproxy/VERSION') as fh: FTEPROXY_RELEASE = fh.read().strip() version = FTEPROXY_RELEASE # The full version, including alpha/beta/rc tags. release = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Format-TransformingEncryptionFTEdoc' # -- Options for LaTeX output -------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Format-TransformingEncryptionfteproxy.tex', u'fteproxy Documentation', u'Kevin P. Dyer', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'format-transformingencryptionfte', u'fteproxy Documentation', [u'Kevin P. Dyer'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Format-TransformingEncryptionFTE', u'fteproxy Documentation', u'Kevin P. Dyer', 'Format-TransformingEncryptionFTE', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} fteproxy-0.2.19/doc/source/fteproxy.rst000066400000000000000000000065361241561400200201470ustar00rootroot00000000000000``fteproxy`` ************ The ``fteproxy`` command-line application is used to start an fteproxy client or server. Parameters ---------- The ``fteproxy`` command-line application usage: .. code-block:: none usage: fteproxy [-h] [--version] [--mode (client|server|test)] [--stop] [--upstream-format UPSTREAM_FORMAT] [--downstream-format DOWNSTREAM_FORMAT] [--client_ip CLIENT_IP] [--client_port CLIENT_PORT] [--server_ip SERVER_IP] [--server_port SERVER_PORT] [--proxy_ip PROXY_IP] [--proxy_port PROXY_PORT] [--quiet] [--release RELEASE] [--managed] [--key KEY] optional arguments: -h, --help show this help message and exit --version Output the version of fteproxy, then quit. (default: False) --mode (client|server|test) Relay mode: client or server (default: client) --stop Shutdown daemon process (default: False) --upstream-format UPSTREAM_FORMAT Client-to-server language format (default: manual- http-request) --downstream-format DOWNSTREAM_FORMAT Server-to-client language format (default: manual- http-response) --client_ip CLIENT_IP Client-side listening IP (default: 127.0.0.1) --client_port CLIENT_PORT Client-side listening port (default: 8079) --server_ip SERVER_IP Server-side listening IP (default: 127.0.0.1) --server_port SERVER_PORT Server-side listening port (default: 8080) --proxy_ip PROXY_IP Forwarding-proxy (SOCKS) listening IP (default: 127.0.0.1) --proxy_port PROXY_PORT Forwarding-proxy (SOCKS) listening port (default: 8081) --quiet Be completely silent. Print nothing. (default: False) --release RELEASE Definitions file to use, specified as YYYYMMDD (default: 20131224) --managed Start in pluggable transport managed mode, for use with Tor. (default: False) --key KEY Cryptographic key, hex, must be exactly 64 characters (default: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000 00000000000000000000) Example Usage ------------- Starting the ``fteproxy`` client with the default configuration parameters. .. code-block:: none fteproxy --mode client Starting the ``fteproxy`` client, binding to ``127.0.0.1:8888``. .. code-block:: none fteproxy --mode client --client_ip 127.0.0.1 --client_port 8888 Starting the ``fteproxy`` client, using ``XXX`` for upstream communications and ``YYY`` for downstream communications. .. code-block:: none fteproxy --mode client --upstream-format XXX --downstream-format YYY Starting the ``fteproxy`` server, binding to port ``8888`` on all interfaces. .. code-block:: none fteproxy --mode server --client_ip 0.0.0.0 --client_port 8888 Start ``fteproxy``, execute all unit tests, then exit. .. code-block:: none fteproxy --mode test fteproxy-0.2.19/doc/source/index.rst000066400000000000000000000020241241561400200173620ustar00rootroot00000000000000fteproxy Documentation ======================= Format-Transforming Encryption (FTE) is a strategy for communicating using strings defined by compact regular expressions. fteproxy is well-positioned to circumvent regex-based DPI systems. ``fteproxy`` Command Line Application ------------------------------------- The ``fteproxy`` command-line application can be used to run and fteproxy proxy client or server. .. toctree:: :maxdepth: 2 fteproxy.rst fteproxy Wrapper for Socket Objects ----------------------------------- The fteproxy socket wrapper is useful for rapidly integrating fteproxy into existing socket-powered applications. .. toctree:: :maxdepth: 2 __init__.rst fteproxy API ----------------------------------- .. toctree:: :maxdepth: 2 client.rst server.rst network_io.rst relay.rst record_layer.rst Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` fteproxy-0.2.19/doc/source/network_io.rst000066400000000000000000000006071241561400200204400ustar00rootroot00000000000000:mod:`fteproxy.network_io` Module ********************************* Overview -------- A class that aids in socket I/O. The primary purpose of this class is to encapsulate the different error conditions that can occur with a socket read: exceptions and a return value of ''. Interface --------- .. automodule:: fteproxy.network_io :members: :undoc-members: :show-inheritance: fteproxy-0.2.19/doc/source/record_layer.rst000066400000000000000000000004411241561400200207260ustar00rootroot00000000000000:mod:`fteproxy.record_layer` Module *********************************** Overview -------- This is a class used internally by fteproxy and should not be invoked directly. Interface --------- .. automodule:: fteproxy.record_layer :members: :undoc-members: :show-inheritance: fteproxy-0.2.19/doc/source/relay.rst000066400000000000000000000007121241561400200173710ustar00rootroot00000000000000:mod:`fteproxy.relay` Module **************************** Overview -------- The classes ``fteproxy.relay.worker`` and ``fteproxy.relay.listener`` are never invoked directly and are helper classes used internally within fteproxy. ``fteproxy.relay.worker`` contains logic that is common to ``fteproxy.relay.client`` and ``fteproxy.relay.server``. Interface --------- .. automodule:: fteproxy.relay :members: :undoc-members: :show-inheritance: fteproxy-0.2.19/doc/source/server.rst000066400000000000000000000027531241561400200175720ustar00rootroot00000000000000:mod:`fteproxy.server` Module ***************************** Overview -------- It's the responsibility of the server listener to broker incoming/ outgoing connections on the server-side of an fteproxy setup. Incoming connections are encapsulated by FTE, as they are from an fteproxy client application. Outgoing connections will be destined to some sort of proxy, such as a SOCKS server or Tor bridge. The ``fteproxy.server.listener`` class extends ``fteproxy.relay.listener``. See ``fteproxy.relay.listener`` for more information, which is also the base class for ``fteproxy.client.listener``. The ``fteproxy.relay.listener`` class extends ``threading.Thread``, hence we invoke the fteproxy server via fteproxy.listener.server.start(). Interface --------- .. automodule:: fteproxy.server :members: :undoc-members: :show-inheritance: Examples -------- Start the fteproxy server with default configuration parameters. .. code-block:: python import fteproxy.server.listener server = fteproxy.server.listener() server.start() server.join(10) # run for 10 seconds server.stop() Start the fteproxy server listening on server-side port ``127.0.0.1:8888``. .. code-block:: python import fteproxy.conf import fteproxy.server.listener fteproxy.conf.setValue('runtime.server.ip', '127.0.0.1') fteproxy.conf.setValue('runtime.server.port', 8888) server = fteproxy.server.listener() server.start() server.join(10) # run for 10 seconds server.stop() fteproxy-0.2.19/examples/000077500000000000000000000000001241561400200152745ustar00rootroot00000000000000fteproxy-0.2.19/examples/chat/000077500000000000000000000000001241561400200162135ustar00rootroot00000000000000fteproxy-0.2.19/examples/chat/README000066400000000000000000000001011241561400200170630ustar00rootroot00000000000000Based on code from: http://docs.python.org/2/library/socket.html fteproxy-0.2.19/examples/chat/client.py000066400000000000000000000011671241561400200200500ustar00rootroot00000000000000# FTE-Powered echo client program import socket import fteproxy client_server_regex = '^(0|1)+$' server_client_regex = '^(A|B)+$' HOST = '127.0.0.1' # The remote host PORT = 50007 # The same port as used by the server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = fteproxy.wrap_socket(s, outgoing_regex=client_server_regex, outgoing_fixed_slice=256, incoming_regex=server_client_regex, incoming_fixed_slice=256) s.connect((HOST, PORT)) s.sendall('Hello, world') data = s.recv(1024) s.close() print 'Received', repr(data) fteproxy-0.2.19/examples/chat/server.py000066400000000000000000000013421241561400200200730ustar00rootroot00000000000000# FTE-Powered echo server program import socket import fteproxy client_server_regex = '^(0|1)+$' server_client_regex = '^(A|B)+$' HOST = '' # Symbolic name meaning all available interfaces PORT = 50007 # Arbitrary non-privileged port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = fteproxy.wrap_socket(s, outgoing_regex=server_client_regex, outgoing_fixed_slice=256, incoming_regex=client_server_regex, incoming_fixed_slice=256) s.bind((HOST, PORT)) s.listen(1) conn, addr = s.accept() print 'Connected by', addr while 1: data = conn.recv(1024) if not data: break conn.sendall(data) conn.close() fteproxy-0.2.19/examples/netcat/000077500000000000000000000000001241561400200165525ustar00rootroot00000000000000fteproxy-0.2.19/examples/netcat/netcat_simple.sh000077500000000000000000000003771241561400200217470ustar00rootroot00000000000000#!/bin/sh # start fteproxy client ./bin/fteproxy --quiet & # start fteproxy server ./bin/fteproxy --mode server --quiet & # start server-side netcat listener netcat -k -l -p 8081 # start client-side netcat pusher in another window # nc localhost 8079 fteproxy-0.2.19/examples/netcat/netcat_verbose.sh000077500000000000000000000015571241561400200221240ustar00rootroot00000000000000#!/bin/sh # the IP:port our fteproxy client listens on CLIENT_IP=127.0.0.1 CLIENT_PORT=8079 # the IP:port our fteproxy server listens on SERVER_IP=127.0.0.1 SERVER_PORT=8080 # the IP:port where our fteproxy forwards all connections # in this test, it's the IP:port the server-side netcat will bind to PROXY_IP=127.0.0.1 PROXY_PORT=8081 # start fteproxy client ./bin/fteproxy --mode client --quiet \ --client_ip $CLIENT_IP --client_port $CLIENT_PORT \ --server_ip $SERVER_IP --server_port $SERVER_PORT & # start fteproxy server ./bin/fteproxy --mode server --quiet \ --server_ip $SERVER_IP --server_port $SERVER_PORT \ --proxy_ip $PROXY_IP --proxy_port $PROXY_PORT & # start server-side netcat listener netcat -k -l -p $PROXY_PORT # start client-side netcat pusher in another window # nc $CLIENT_IP $CLIENT_PORT fteproxy-0.2.19/fteproxy.spec000066400000000000000000000011261241561400200162120ustar00rootroot00000000000000# -*- mode: python -*- a = Analysis(['./bin/fteproxy'], pathex=['/Users/kpdyer/sandbox/kpdyer/fteproxy'], hiddenimports=[], hookspath=None, runtime_hooks=None) pyz = PYZ(a.pure) exe = EXE(pyz, a.scripts, exclude_binaries=True, name='fteproxy', debug=False, strip=None, upx=True, console=True ) coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=None, upx=True, name='fteproxy') fteproxy-0.2.19/fteproxy/000077500000000000000000000000001241561400200153365ustar00rootroot00000000000000fteproxy-0.2.19/fteproxy/VERSION000066400000000000000000000000071241561400200164030ustar00rootroot000000000000000.2.19 fteproxy-0.2.19/fteproxy/__init__.py000066400000000000000000000425321241561400200174550ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import socket import string import traceback import fteproxy.conf import fteproxy.defs import fteproxy.record_layer import fteproxy.regex2dfa import fte.encoder class InvalidRoleException(Exception): pass class NegotiationFailedException(Exception): pass class ChannelNotReadyException(Exception): pass class NegotiateTimeoutException(Exception): """Raised when negotiation fails to complete after """ + str(fteproxy.conf.getValue('runtime.fteproxy.negotiate.timeout')) + """ seconds. """ pass def fatal_error(msg): if fteproxy.conf.getValue('runtime.loglevel') in [1,2,3]: print 'ERROR:', msg sys.exit(1) def warn(msg): if fteproxy.conf.getValue('runtime.loglevel') in [2,3]: print 'WARN:', msg def info(msg): if fteproxy.conf.getValue('runtime.loglevel') in [3]: print 'INFO:', msg class NegotiateCell(object): _CELL_SIZE = 64 _PADDING_LEN = 32 _PADDING_CHAR = '\x00' _DATE_FORMAT = 'YYYYMMDD' def __init__(self): self._def_file = "" self._language = "" def setDefFile(self, def_file): self._def_file = def_file def getDefFile(self): return self._def_file def setLanguage(self, language): self._language = language def getLanguage(self): return self._language def toString(self): retval = '' retval += self._def_file retval += self._language retval = string.rjust( retval, NegotiateCell._CELL_SIZE, NegotiateCell._PADDING_CHAR) assert retval[:NegotiateCell._PADDING_LEN] == NegotiateCell._PADDING_CHAR * \ NegotiateCell._PADDING_LEN return retval def fromString(self, negotiate_cell_str): assert len(negotiate_cell_str) == NegotiateCell._CELL_SIZE assert negotiate_cell_str[ :NegotiateCell._PADDING_LEN] == NegotiateCell._PADDING_CHAR * NegotiateCell._PADDING_LEN negotiate_cell_str = negotiate_cell_str.strip( NegotiateCell._PADDING_CHAR) # 8==len(YYYYMMDD) def_file = negotiate_cell_str[:len(NegotiateCell._DATE_FORMAT)] language = negotiate_cell_str[len(NegotiateCell._DATE_FORMAT):] negotiate_cell = NegotiateCell() negotiate_cell.setDefFile(def_file) negotiate_cell.setLanguage(language) return negotiate_cell class NegotiationManager(object): def __init__(self): self._negotiationComplete = False def getNegotiationComplete(self): return self._negotiationComplete def _acceptNegotiation(self, data): languages = fteproxy.defs.load_definitions() for incoming_language in languages.keys(): try: if incoming_language.endswith('response'): continue incoming_regex = fteproxy.defs.getRegex(incoming_language) incoming_fixed_slice = fteproxy.defs.getFixedSlice( incoming_language) incoming_decoder = fte.encoder.DfaEncoder(fteproxy.regex2dfa.regex2dfa(incoming_regex), incoming_fixed_slice) decoder = fteproxy.record_layer.Decoder(decoder=incoming_decoder) decoder.push(data) negotiate_cell = decoder.pop(oneCell=True) NegotiateCell().fromString(negotiate_cell) return [negotiate_cell, decoder._buffer] except Exception as e: fteproxy.info('Failed to decode first message as '+incoming_language+': '+str(e)) raise NegotiationFailedException() def _init_encoders(self, outgoing_regex, outgoing_fixed_slice, incoming_regex, incoming_fixed_slice): encoder = None decoder = None if outgoing_regex != None and outgoing_fixed_slice != -1: outgoing_encoder = fte.encoder.DfaEncoder(fteproxy.regex2dfa.regex2dfa(outgoing_regex), outgoing_fixed_slice) encoder = fteproxy.record_layer.Encoder(encoder=outgoing_encoder) if incoming_regex != None and incoming_fixed_slice != -1: incoming_decoder = fte.encoder.DfaEncoder(fteproxy.regex2dfa.regex2dfa(incoming_regex), incoming_fixed_slice) decoder = fteproxy.record_layer.Decoder(decoder=incoming_decoder) return [encoder, decoder] def _makeNegotiationCell(self, encoder): negotiate_cell = NegotiateCell() def_file = fteproxy.conf.getValue('fteproxy.defs.release') negotiate_cell.setDefFile(def_file) language = fteproxy.conf.getValue('runtime.state.upstream_language') language = language[:-len('-request')] negotiate_cell.setLanguage(language) encoder.push(negotiate_cell.toString()) data = encoder.pop() return data def makeClientNegotiationCell(self, outgoing_regex, outgoing_fixed_slice, incoming_regex, incoming_fixed_slice): [encoder, decoder] = self._init_encoders( outgoing_regex, outgoing_fixed_slice, incoming_regex, incoming_fixed_slice) return self._makeNegotiationCell(encoder) def doServerSideNegotiation(self, data): [negotiate_cell, remaining_buffer] = self._acceptNegotiation(data) negotiate = NegotiateCell().fromString(negotiate_cell) outgoing_language = negotiate.getLanguage() + '-response' incoming_language = negotiate.getLanguage() + '-request' outgoing_regex = fteproxy.defs.getRegex(outgoing_language) outgoing_fixed_slice = fteproxy.defs.getFixedSlice(outgoing_language) incoming_regex = fteproxy.defs.getRegex(incoming_language) incoming_fixed_slice = fteproxy.defs.getFixedSlice(incoming_language) [encoder, decoder] = self._init_encoders( outgoing_regex, outgoing_fixed_slice, incoming_regex, incoming_fixed_slice) decoder.push(remaining_buffer) return [encoder, decoder] class FTEHelper(object): def _processRecv(self, data): retval = data if self._isServer and not self._negotiationComplete: try: self._preNegotiationBuffer_incoming += data [encoder, decoder] = self._negotiation_manager.doServerSideNegotiation( self._preNegotiationBuffer_incoming) self._encoder = encoder self._decoder = decoder self._preNegotiationBuffer_incoming = '' self._negotiationComplete = True retval = '' except Exception as e: raise ChannelNotReadyException() return retval def _processSend(self): retval = '' if self._isClient and not self._negotiationComplete: [encoder, decoder] = self._negotiation_manager._init_encoders( self._outgoing_regex, self._outgoing_fixed_slice, self._incoming_regex, self._incoming_fixed_slice) self._encoder = encoder self._decoder = decoder negotiation_cell = self._negotiation_manager.makeClientNegotiationCell( self._outgoing_regex, self._outgoing_fixed_slice, self._incoming_regex, self._incoming_fixed_slice) retval = negotiation_cell self._negotiationComplete = True return retval class _FTESocketWrapper(FTEHelper, object): def __init__(self, _socket, outgoing_regex=None, outgoing_fixed_slice=-1, incoming_regex=None, incoming_fixed_slice=-1, K1=None, K2=None): self._socket = _socket self._outgoing_regex = outgoing_regex self._outgoing_fixed_slice = outgoing_fixed_slice self._incoming_regex = incoming_regex self._incoming_fixed_slice = incoming_fixed_slice self._K1 = K1 self._K2 = K2 self._negotiation_manager = NegotiationManager() self._negotiationComplete = False self._isServer = (outgoing_regex is None and incoming_regex is None) self._isClient = ( outgoing_regex is not None and incoming_regex is not None) self._incoming_buffer = '' self._preNegotiationBuffer_outgoing = '' self._preNegotiationBuffer_incoming = '' def fileno(self): return self._socket.fileno() def recv(self, bufsize): # # Required to deal with case when client attempts to recv # before sending. This checks to ensure that a negotiate # cell is sent no matter what the client does first. to_send = self._processSend() if to_send: numbytes = self._socket.send(to_send) assert numbytes == len(to_send) # try: while True: data = self._socket.recv(bufsize) noData = (data == '') data = self._processRecv(data) if noData and not self._incoming_buffer and not self._decoder._buffer: return '' self._decoder.push(data) while True: frag = self._decoder.pop() if not frag: break self._incoming_buffer += frag if self._incoming_buffer: break retval = self._incoming_buffer self._incoming_buffer = '' except ChannelNotReadyException: raise socket.timeout return retval def send(self, data): to_send = self._processSend() if to_send: self._socket.sendall(to_send) self._encoder.push(data) while True: to_send = self._encoder.pop() if not to_send: break self._socket.sendall(to_send) return len(data) def sendall(self, data): return self.send(data) def gettimeout(self): return self._socket.gettimeout() def settimeout(self, val): return self._socket.settimeout(val) def shutdown(self, flags): return self._socket.shutdown(flags) def close(self): return self._socket.close() def connect(self, addr): return self._socket.connect(addr) def accept(self): conn, addr = self._socket.accept() conn = _FTESocketWrapper(conn, self._outgoing_regex, self._outgoing_fixed_slice, self._incoming_regex, self._incoming_fixed_slice, self._K1, self._K2) return conn, addr def bind(self, addr): return self._socket.bind(addr) def listen(self, N): return self._socket.listen(N) def wrap_socket(sock, outgoing_regex=None, outgoing_fixed_slice=-1, incoming_regex=None, incoming_fixed_slice=-1, K1=None, K2=None): """``fteproxy.wrap_socket`` turns an existing socket into an fteproxy socket. The input parameter ``sock`` is the socket to wrap. The parameter ``outgoing_regex`` specifies the format of the messages to send via the socket. The ``outgoing_fixed_slice`` parameter specifies the maximum length of the strings in ``outgoing_regex``. The parameters ``incoming_regex`` and ``incoming_fixed_slice`` are defined similarly. The optional parameters ``K1`` and ``K2`` specify 128-bit keys to be used in FTE's underlying AE scheme. If specified, these values must be 16-byte hex strings. """ assert K1 == None or len(K1) == 16 assert K2 == None or len(K2) == 16 socket_wrapped = _FTESocketWrapper( sock, outgoing_regex, outgoing_fixed_slice, incoming_regex, incoming_fixed_slice, K1, K2) return socket_wrapped import obfsproxy.network.network as network import obfsproxy.network.socks as socks import obfsproxy.network.extended_orport as extended_orport import obfsproxy.transports.base import twisted.internet class FTETransport(FTEHelper, obfsproxy.transports.base.BaseTransport): def __init__(self, pt_config=None): self.circuit = None self._isClient = (fteproxy.conf.getValue('runtime.mode') == 'client') self._isServer = not self._isClient if self._isClient: outgoing_language = fteproxy.conf.getValue( 'runtime.state.upstream_language') incoming_language = fteproxy.conf.getValue( 'runtime.state.downstream_language') self._outgoing_regex = fteproxy.defs.getRegex(outgoing_language) self._outgoing_fixed_slice = fteproxy.defs.getFixedSlice( outgoing_language) self._incoming_regex = fteproxy.defs.getRegex(incoming_language) self._incoming_fixed_slice = fteproxy.defs.getFixedSlice( incoming_language) else: self._outgoing_regex = None self._outgoing_fixed_slice = -1 self._incoming_regex = None self._incoming_fixed_slice = -1 self._encoder = None self._decoder = None self._negotiation_manager = NegotiationManager() self._negotiationComplete = False self._incoming_buffer = '' self._preNegotiationBuffer_outgoing = '' self._preNegotiationBuffer_incoming = '' def receivedDownstream(self, data, circuit=None): """decode fteproxy stream""" circuit = self.circuit if circuit == None else circuit try: data = data.read() data = self._processRecv(data) if self._decoder: self._decoder.push(data) while True: frag = self._decoder.pop() if not frag: break circuit.upstream.write(frag) except ChannelNotReadyException: pass def receivedUpstream(self, data, circuit=None): """encode fteproxy stream""" circuit = self.circuit if circuit == None else circuit to_send = self._processSend() if to_send: circuit.downstream.write(to_send) data = data.read() if self._encoder: self._encoder.push(data) while True: to_send = self._encoder.pop() if not to_send: break circuit.downstream.write(to_send) class FTETransportClient(FTETransport): pass class FTETransportServer(FTETransport): pass def launch_transport_listener(transport, bindaddr, role, remote_addrport, pt_config, ext_or_cookie_file=None): """ Launch a listener for 'transport' in role 'role' (socks/client/server/ext_server). If 'bindaddr' is set, then listen on bindaddr. Otherwise, listen on an ephemeral port on localhost. 'remote_addrport' is the TCP/IP address of the other end of the circuit. It's not used if we are in 'socks' role. 'pt_config' contains configuration options (such as the state location) which are of interest to the pluggable transport. 'ext_or_cookie_file' is the filesystem path where the Extended ORPort Authentication cookie is stored. It's only used in 'ext_server' mode. Return a tuple (addr, port) representing where we managed to bind. Throws obfsproxy.transports.transports.TransportNotFound if the transport could not be found. Throws twisted.internet.error.CannotListenError if the listener could not be set up. """ listen_host = bindaddr[0] if bindaddr else 'localhost' listen_port = int(bindaddr[1]) if bindaddr else 0 if role == 'socks': transport_class = FTETransportClient if hasattr(socks, "OBFSSOCKSv5Factory"): # obfsproxy >= 0.2.7 provides SOCKS5. factory = socks.OBFSSOCKSv5Factory(transport_class, pt_config) pt_config.fte_client_socks_version = 5 elif hasattr(socks, "SOCKSv4Factory"): # obfsproxy < 0.2.7 provides SOCKS4. factory = socks.SOCKSv4Factory(transport_class, pt_config) pt_config.fte_client_socks_version = 4 else: # This will only happen if the obfsproxy people change the socks # code again. This really is a dependency issue, so raise an # ImportError. raise ImportError("Failed to setup an obfsproxy SOCKS server factory") elif role == 'ext_server': assert(remote_addrport and ext_or_cookie_file) transport_class = FTETransportServer factory = extended_orport.ExtORPortServerFactory( remote_addrport, ext_or_cookie_file, transport, transport_class, pt_config) elif role == 'client': assert(remote_addrport) transport_class = FTETransportClient factory = network.StaticDestinationServerFactory( remote_addrport, role, transport_class, pt_config) elif role == 'server': assert(remote_addrport) transport_class = FTETransportServer factory = network.StaticDestinationServerFactory( remote_addrport, role, transport_class, pt_config) else: raise InvalidRoleException() addrport = twisted.internet.reactor.listenTCP( listen_port, factory, interface=listen_host) return (addrport.getHost().host, addrport.getHost().port) fteproxy-0.2.19/fteproxy/cli.py000077500000000000000000000467571241561400200165050ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import os import signal import glob import argparse import threading import fte.encoder import fteproxy.conf import fteproxy.server import fteproxy.client import fteproxy.regex2dfa # do_managed_* from twisted.internet import reactor, error import obfsproxy.network.network as network import obfsproxy.common.transport_config as transport_config import obfsproxy.transports.transports as transports import obfsproxy.common.log as logging from pyptlib.client import ClientTransportPlugin from pyptlib.server import ServerTransportPlugin from pyptlib.config import EnvError import pprint # unit tests import unittest import fteproxy.tests.test_record_layer import fteproxy.tests.test_relay FTE_PT_NAME = 'fte' VERSION_FILE = os.path.join( fteproxy.conf.getValue('general.base_dir'), 'fteproxy', 'VERSION') with open(VERSION_FILE) as fh: FTEPROXY_VERSION = fh.read().strip() class FTEMain(threading.Thread): def __init__(self, args): threading.Thread.__init__(self) self._args = args def run(self): try: self._client = None self._server = None if self._args.version: print FTEPROXY_VERSION sys.exit(0) if self._args.quiet: fteproxy.conf.setValue('runtime.loglevel', 0) if self._args.managed: fteproxy.conf.setValue('runtime.loglevel', 0) if not self._args.quiet and not self._args.managed: print """fteproxy Copyright (C) 2012-2014 Kevin P. Dyer This program comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions. """ if self._args.mode == 'test': test() if self._args.stop: if not self._args.mode: print '--mode keyword is required with --stop' sys.exit(1) if self._args.mode in ['client', 'server']: pid_files_path = \ os.path.join(fteproxy.conf.getValue('general.pid_dir'), '.' + self._args.mode + '-*.pid') pid_files = glob.glob(pid_files_path) for pid_file in pid_files: with open(pid_file) as f: pid = int(f.read()) try: os.kill(pid, signal.SIGINT) except OSError: fteproxy.warn('failed to remove PID file: '+pid_file) os.unlink(pid_file) sys.exit(0) if self._args.mode == 'client': fteproxy.conf.setValue('runtime.mode', 'client') elif self._args.mode == 'server': fteproxy.conf.setValue('runtime.mode', 'server') else: fteproxy.conf.setValue('runtime.mode', 'client') if self._args.client_ip: fteproxy.conf.setValue('runtime.client.ip', self._args.client_ip) if self._args.client_port: fteproxy.conf.setValue('runtime.client.port', int(self._args.client_port)) if self._args.server_ip: fteproxy.conf.setValue('runtime.server.ip', self._args.server_ip) if self._args.server_port: fteproxy.conf.setValue('runtime.server.port', int(self._args.server_port)) if self._args.proxy_ip: fteproxy.conf.setValue('runtime.proxy.ip', self._args.proxy_ip) if self._args.proxy_port: fteproxy.conf.setValue('runtime.proxy.port', int(self._args.proxy_port)) if self._args.downstream_format: fteproxy.conf.setValue('runtime.state.downstream_language', self._args.downstream_format) if self._args.upstream_format: fteproxy.conf.setValue('runtime.state.upstream_language', self._args.upstream_format) if self._args.release: fteproxy.conf.setValue('fteproxy.defs.release', self._args.release) if self._args.key: if len(self._args.key) != 64: fteproxy.warn('Invalid key length: ' + str(len(self._args.key)) + ', should be 64') sys.exit(1) try: binary_key = self._args.key.decode('hex') except: fteproxy.warn('Invalid key format, must contain only 0-9a-fA-F') sys.exit(1) fteproxy.conf.setValue('runtime.fteproxy.encrypter.key', binary_key) try: pid_file = os.path.join(fteproxy.conf.getValue('general.pid_dir'), '.' + fteproxy.conf.getValue('runtime.mode') + '-' + str(os.getpid()) + '.pid') with open(pid_file, 'w') as f: f.write(str(os.getpid())) except IOError: fteproxy.warn('Failed to write PID file to disk: '+pid_file) if fteproxy.conf.getValue('runtime.mode') == 'client': try: incoming_regex = fteproxy.defs.getRegex(self._args.downstream_format) except fteproxy.defs.InvalidRegexName: fteproxy.fatal_error('Invalid format name '+self._args.downstream_format) incoming_fixed_slice = fteproxy.defs.getFixedSlice( self._args.downstream_format) fte.encoder.DfaEncoder(fteproxy.regex2dfa.regex2dfa(incoming_regex), incoming_fixed_slice) try: outgoing_regex = fteproxy.defs.getRegex(self._args.upstream_format) except InvalidRegexName: fteproxy.fatal_error('Invalid format name '+self._args.upstream_format) outgoing_fixed_slice = fteproxy.defs.getFixedSlice( self._args.upstream_format) fte.encoder.DfaEncoder(fteproxy.regex2dfa.regex2dfa(outgoing_regex), outgoing_fixed_slice) if self._args.managed: do_managed_client() else: if not self._args.quiet: print 'Client ready!' local_ip = fteproxy.conf.getValue('runtime.client.ip') local_port = fteproxy.conf.getValue('runtime.client.port') remote_ip = fteproxy.conf.getValue('runtime.server.ip') remote_port = fteproxy.conf.getValue('runtime.server.port') self._client = fteproxy.client.listener(local_ip, local_port, remote_ip, remote_port) self._client.daemon = True self._client.start() self._client.join() elif fteproxy.conf.getValue('runtime.mode') == 'server': languages = fteproxy.defs.load_definitions() for language in languages.keys(): regex = fteproxy.defs.getRegex(language) fixed_slice = fteproxy.defs.getFixedSlice(language) fte.encoder.DfaEncoder(fteproxy.regex2dfa.regex2dfa(regex), fixed_slice) if self._args.managed: do_managed_server() else: local_ip = fteproxy.conf.getValue('runtime.server.ip') local_port = fteproxy.conf.getValue('runtime.server.port') remote_ip = fteproxy.conf.getValue('runtime.proxy.ip') remote_port = fteproxy.conf.getValue('runtime.proxy.port') self._server = fteproxy.server.listener(local_ip, local_port, remote_ip, remote_port) self._server.daemon = True self._server.start() if not self._args.quiet: print 'Server ready!' self._server.join() except Exception as e: import traceback traceback.print_exc(e) fteproxy.fatal_error("FTEMain terminated unexpectedly: "+str(e)) def stop(self): if self._client is not None: self._client.stop() if self._server is not None: self._server.stop() def do_managed_client(): log = logging.get_obfslogger() """Start the managed-proxy protocol as a client.""" should_start_event_loop = False ptclient = ClientTransportPlugin() try: ptclient.init([FTE_PT_NAME]) except EnvError, err: log.warning("Client managed-proxy protocol failed (%s)." % err) return log.debug("pyptlib gave us the following data:\n'%s'", pprint.pformat(ptclient.getDebugData())) # Apply the proxy settings if any proxy = ptclient.config.getProxy() if proxy: # Ensure that we have all the neccecary dependencies try: network.ensure_outgoing_proxy_dependencies() except network.OutgoingProxyDepFailure, err: ptclient.reportProxyError(str(err)) return ptclient.reportProxySuccess() for transport in ptclient.getTransports(): # Will hold configuration parameters for the pluggable transport # module. pt_config = transport_config.TransportConfig() pt_config.setStateLocation(ptclient.config.getStateLocation()) pt_config.fte_client_socks_version = -1 pt_config.setProxy(proxy) try: addrport = fteproxy.launch_transport_listener( transport, None, 'socks', None, pt_config) except transports.TransportNotFound: log.warning("Could not find transport '%s'" % transport) ptclient.reportMethodError(transport, "Could not find transport.") continue except error.CannotListenError: log.warning("Could not set up listener for '%s'." % transport) ptclient.reportMethodError(transport, "Could not set up listener.") continue should_start_event_loop = True log.debug("Successfully launched '%s' at '%s'" % (transport, log.safe_addr_str(str(addrport)))) if pt_config.fte_client_socks_version == 5: ptclient.reportMethodSuccess(transport, "socks5", addrport, None, None) elif pt_config.fte_client_socks_version == 4: ptclient.reportMethodSuccess(transport, "socks4", addrport, None, None) else: # This should *never* happen unless launch_transport_listener() # decides to report a SOCKS version from the future. log.warning("Listener SOCKS version unknown." ) ptclient.reportMethodError(transport, "Listener SOCKS version unknown.") should_start_event_loop = False ptclient.reportMethodsEnd() if should_start_event_loop: log.info("Starting up the event loop.") reactor.run() else: log.info("No transports launched. Nothing to do.") def do_managed_server(): log = logging.get_obfslogger() """Start the managed-proxy protocol as a server.""" should_start_event_loop = False ptserver = ServerTransportPlugin() try: ptserver.init([FTE_PT_NAME]) except EnvError, err: log.warning("Server managed-proxy protocol failed (%s)." % err) return log.debug("pyptlib gave us the following data:\n'%s'", pprint.pformat(ptserver.getDebugData())) ext_orport = ptserver.config.getExtendedORPort() authcookie = ptserver.config.getAuthCookieFile() orport = ptserver.config.getORPort() server_transport_options = ptserver.config.getServerTransportOptions() for transport, transport_bindaddr in ptserver.getBindAddresses().items(): # Will hold configuration parameters for the pluggable transport # module. pt_config = transport_config.TransportConfig() pt_config.setStateLocation(ptserver.config.getStateLocation()) transport_options = "" if server_transport_options and transport in server_transport_options: transport_options = server_transport_options[transport] pt_config.setServerTransportOptions(transport_options) try: if ext_orport: addrport = fteproxy.launch_transport_listener(transport, transport_bindaddr, 'ext_server', ext_orport, pt_config, ext_or_cookie_file=authcookie) else: addrport = fteproxy.launch_transport_listener(transport, transport_bindaddr, 'server', orport, pt_config) except transports.TransportNotFound: log.warning("Could not find transport '%s'" % transport) ptserver.reportMethodError(transport, "Could not find transport.") continue except error.CannotListenError: log.warning("Could not set up listener for '%s'." % transport) ptserver.reportMethodError(transport, "Could not set up listener.") continue should_start_event_loop = True # Include server transport options in the log message if we got 'em extra_log = "" if transport_options: extra_log = " (server transport options: '%s')" % str( transport_options) log.debug("Successfully launched '%s' at '%s'%s" % (transport, log.safe_addr_str(str(addrport)), extra_log)) # Report success for this transport. # (We leave the 'options' as None and let pyptlib handle the # SMETHOD argument sending.) ptserver.reportMethodSuccess(transport, addrport, None) ptserver.reportMethodsEnd() if should_start_event_loop: log.info("Starting up the event loop.") reactor.run() else: log.info("No transports launched. Nothing to do.") def test(): try: suite_record_layer = unittest.TestLoader().loadTestsFromTestCase( fteproxy.tests.test_record_layer.Tests) suite_relay = unittest.TestLoader().loadTestsFromTestCase( fteproxy.tests.test_relay.Tests) suites = [ suite_relay, suite_record_layer, ] alltests = unittest.TestSuite(suites) unittest.TextTestRunner(verbosity=2).run(alltests) sys.exit(0) except Exception as e: fteproxy.warn("Unit tests failed: "+str(e)) def get_args(): parser = argparse.ArgumentParser(prog='fteproxy', formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('--version', action='store_true', default=False, help='Output the version of fteproxy, then quit.') parser.add_argument('--mode', default='client', metavar='(client|server|test)', help='Relay mode: client or server') parser.add_argument('--stop', action='store_true', help='Shutdown daemon process') parser.add_argument('--upstream-format', help='Client-to-server language format', default=fteproxy.conf.getValue('runtime.state.upstream_language' )) parser.add_argument('--downstream-format', help='Server-to-client language format', default=fteproxy.conf.getValue('runtime.state.downstream_language' )) parser.add_argument('--client_ip', help='Client-side listening IP', default=fteproxy.conf.getValue('runtime.client.ip' )) parser.add_argument('--client_port', help='Client-side listening port', default=fteproxy.conf.getValue('runtime.client.port' )) parser.add_argument('--server_ip', help='Server-side listening IP', default=fteproxy.conf.getValue('runtime.server.ip' )) parser.add_argument('--server_port', help='Server-side listening port', default=fteproxy.conf.getValue('runtime.server.port' )) parser.add_argument('--proxy_ip', help='Forwarding-proxy (SOCKS) listening IP', default=fteproxy.conf.getValue('runtime.proxy.ip' )) parser.add_argument('--proxy_port', help='Forwarding-proxy (SOCKS) listening port', default=fteproxy.conf.getValue('runtime.proxy.port' )) parser.add_argument('--quiet', action='store_true', default=False, help='Be completely silent. Print nothing.') parser.add_argument('--release', help='Definitions file to use, specified as YYYYMMDD', default=fteproxy.conf.getValue('fteproxy.defs.release')) parser.add_argument('--managed', help="Start in pluggable transport managed mode, for use with Tor.", action='store_true', default=False) parser.add_argument('--key', help='Cryptographic key, hex, must be exactly 64 characters', default=fteproxy.conf.getValue('runtime.fteproxy.encrypter.key' )) args = parser.parse_args(sys.argv[1:]) return args def main(): global running running = True def signal_handler(signal, frame): global running running = False signal.signal(signal.SIGINT, signal_handler) try: args = get_args() main = FTEMain(args) if args.managed: main.run() else: main.daemon = True main.start() while running and main.is_alive(): main.join(timeout=0.5) main.stop() except KeyboardInterrupt: pass except Exception as e: fteproxy.fatal_error("Main thread terminated unexpectedly: "+str(e)) finally: if fteproxy.conf.getValue('runtime.mode'): pid_file = os.path.join(fteproxy.conf.getValue('general.pid_dir' ), '.' + fteproxy.conf.getValue('runtime.mode') + '-' + str(os.getpid()) + '.pid') if pid_file and os.path.exists(pid_file): os.unlink(pid_file) fteproxy-0.2.19/fteproxy/client.py000066400000000000000000000021331241561400200171650ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import fteproxy.relay class listener(fteproxy.relay.listener): def onNewOutgoingConnection(self, socket): """On an outgoing data stream we wrap it with ``fteproxy.wrap_socket``, with the languages specified in the ``runtime.state.upstream_language`` and ``runtime.state.downstream_language`` configuration parameters. """ outgoing_language = fteproxy.conf.getValue( 'runtime.state.upstream_language') incoming_language = fteproxy.conf.getValue( 'runtime.state.downstream_language') outgoing_regex = fteproxy.defs.getRegex(outgoing_language) outgoing_fixed_slice = fteproxy.defs.getFixedSlice(outgoing_language) incoming_regex = fteproxy.defs.getRegex(incoming_language) incoming_fixed_slice = fteproxy.defs.getFixedSlice(incoming_language) socket = fteproxy.wrap_socket(socket, outgoing_regex, outgoing_fixed_slice, incoming_regex, incoming_fixed_slice) return socket fteproxy-0.2.19/fteproxy/conf.py000066400000000000000000000057401241561400200166430ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import tempfile def getValue(key): return conf[key] def setValue(key, value): conf[key] = value def we_are_frozen(): # All of the modules are built-in to the interpreter, e.g., by py2exe return hasattr(sys, "frozen") def module_path(): if we_are_frozen(): return os.path.dirname(sys.executable) else: return os.path.dirname(__file__) conf = {} """The base path for the location of the fteproxy.* modules.""" if we_are_frozen(): conf['general.base_dir'] = module_path() else: conf['general.base_dir'] = os.path.join(module_path(), '..') """Directory containing binary executables""" if we_are_frozen(): conf['general.bin_dir'] = os.path.join(module_path()) else: conf['general.bin_dir'] = os.path.join(module_path(), '..', 'bin') """The path for fte *.json definition files.""" if we_are_frozen(): conf['general.defs_dir'] = os.path.join(module_path(), 'fteproxy', 'defs') else: conf['general.defs_dir'] = os.path.join(module_path(), '..', 'fteproxy', 'defs') """The location that we store *.pid files, such that we can kill fteproxy from the command line.""" conf['general.pid_dir'] = tempfile.gettempdir() """Our runtime mode: client|server|test""" conf['runtime.mode'] = None """Our loglevel = 0|1|2|3""" conf['runtime.loglevel'] = 1 """The maximum number of queued connections for sockets""" conf['runtime.fteproxy.relay.backlog'] = 100 """Our client-side ip:port to listen for incoming connections""" conf['runtime.client.ip'] = '127.0.0.1' conf['runtime.client.port'] = 8079 """Our server-side ip:port to listen for connections from fteproxy clients""" conf['runtime.server.ip'] = '127.0.0.1' conf['runtime.server.port'] = 8080 """Our proxy server, where the fteproxy server forwards outgoing connections.""" conf['runtime.proxy.ip'] = '127.0.0.1' conf['runtime.proxy.port'] = 8081 """The default socket timeout.""" conf['runtime.fteproxy.relay.socket_timeout'] = 30 """The default socket accept timeout.""" conf['runtime.fteproxy.relay.accept_timeout'] = 0.1 """The default penalty after polling for network data, and not recieving anything.""" conf['runtime.fteproxy.relay.throttle'] = 0.01 """The default timeout when establishing a new fteproxy socket.""" conf['runtime.fteproxy.negotiate.timeout'] = 5 """The maximum number of bytes to segment for an outgoing message.""" conf['runtime.fteproxy.record_layer.max_cell_size'] = 2 ** 15 """The default client-to-server language.""" conf['runtime.state.upstream_language'] = 'manual-http-request' """The default server-to-client language.""" conf['runtime.state.downstream_language'] = 'manual-http-response' """The default AE scheme key.""" conf['runtime.fteproxy.encrypter.key'] = 'FF' * 16 + '00' * 16 """The default fixed_slice parameter to use for buildTable.""" conf['fteproxy.default_fixed_slice'] = 2 ** 8 """The default definitions file to use.""" conf['fteproxy.defs.release'] = '20131224' fteproxy-0.2.19/fteproxy/defs/000077500000000000000000000000001241561400200162575ustar00rootroot00000000000000fteproxy-0.2.19/fteproxy/defs/20131224.json000066400000000000000000000007651241561400200200600ustar00rootroot00000000000000{ "manual-http-request": { "regex": "^GET\\ \\/([a-zA-Z0-9\\.\\/]*) HTTP/1\\.1\\r\\n\\r\\n$" }, "manual-http-response": { "regex": "^HTTP/1\\.1\\ 200 OK\\r\\nContent-Type:\\ ([a-zA-Z0-9]+)\\r\\n\\r\\n\\C*$" }, "manual-ssh-request": { "regex": "^SSH\\-2\\.0\\C*$" }, "manual-ssh-response": { "regex": "^SSH\\-2\\.0\\C*$" }, "dummy-request": { "regex": "^\\C+$" }, "dummy-response": { "regex": "^\\C+$" } } fteproxy-0.2.19/fteproxy/defs/__init__.py000066400000000000000000000017771241561400200204040ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import os import json import fteproxy.conf class InvalidRegexName(Exception): pass _definitions = None def load_definitions(): global _definitions if _definitions == None: def_dir = os.path.join(fteproxy.conf.getValue('general.defs_dir')) def_file = fteproxy.conf.getValue('fteproxy.defs.release') + '.json' def_abspath = os.path.join(def_dir, def_file) with open(def_abspath) as fh: _definitions = json.load(fh) return _definitions def getRegex(format_name): definitions = load_definitions() try: regex = definitions[format_name]['regex'] except KeyError: raise InvalidRegexName(format_name) return regex def getFixedSlice(format_name): definitions = load_definitions() try: fixed_slice = definitions[format_name]['fixed_slice'] except KeyError: fixed_slice = fteproxy.conf.getValue('fteproxy.default_fixed_slice') return fixed_slice fteproxy-0.2.19/fteproxy/network_io.py000066400000000000000000000033301241561400200200670ustar00rootroot00000000000000import select import socket def sendall_to_socket(sock, data): """Given a socket ``sock`` and ``msg`` does a best effort to send ``msg`` on ``sock`` as quickly as possible. """ return sock.sendall(data) def recvall_from_socket(sock, bufsize=2 ** 18, select_timeout=0.1): """Give ``sock``, does a best effort to pull data from ``sock``. By default, fails quickly if ``sock`` is closed or has no data ready. The return value ``is_alive`` reports if ``sock`` is still alive. The return value ``retval`` is the data extracted from the socket. Unlike normal raw sockets, it may be the case that ``retval`` is '', and ``is_alive`` is ``true``. """ retval = '' is_alive = False try: ready = select.select([sock], [], [sock], select_timeout) if ready[0]: _data = sock.recv(bufsize) if _data: retval += _data is_alive = True else: is_alive = False else: # select.timeout is_alive = True except socket.timeout: is_alive = True except socket.error: is_alive = (len(retval) > 0) except select.error: is_alive = (len(retval) > 0) finally: if retval: is_alive = True return [is_alive, retval] def close_socket(sock, lock=None): """Given socket ``sock`` closes the socket for reading and writing. If the optional ``lock`` parameter is provided, protects all accesses to ``sock`` with ``lock``. """ try: if lock is not None: with lock: sock.close() else: sock.close() except: pass fteproxy-0.2.19/fteproxy/record_layer.py000066400000000000000000000044541241561400200203710ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import fte.encoder import fteproxy.conf MAX_CELL_SIZE = fteproxy.conf.getValue('runtime.fteproxy.record_layer.max_cell_size') class Encoder: def __init__( self, encoder, ): self._encoder = encoder self._buffer = '' def push(self, data): """Push data onto the FIFO buffer.""" self._buffer += data def pop(self): """Pop data off the FIFO buffer. We pop at most ``runtime.fteproxy.record_layer.max_cell_size`` bytes. The returned value is encrypted and encoded with ``encoder`` specified in ``__init__``. """ retval = '' while len(self._buffer) > 0: plaintext = self._buffer[:MAX_CELL_SIZE] covertext = self._encoder.encode(plaintext) self._buffer = self._buffer[MAX_CELL_SIZE:] retval += covertext return retval class Decoder: def __init__( self, decoder, ): self._decoder = decoder self._buffer = '' def push(self, data): """Push data onto the FIFO buffer.""" self._buffer += data def pop(self, oneCell=False): """Pop data off the FIFO buffer. The returned value is decoded with ``_decoder`` then decrypted with ``_decrypter`` specified in ``__init__``. """ retval = '' while len(self._buffer) > 0: try: msg, buffer = self._decoder.decode(self._buffer) retval += msg self._buffer = buffer except fte.encoder.DecodeFailureError as e: fteproxy.info("fteproxy.encoder.DecodeFailure: "+str(e)) break except fte.encrypter.RecoverableDecryptionError as e: fteproxy.info("fteproxy.encrypter.RecoverableDecryptionError: "+str(e)) break except fte.encrypter.UnrecoverableDecryptionError as e: fteproxy.fatal_error("fteproxy.encrypter.UnrecoverableDecryptionError: "+str(e)) # exit except Exception as e: fteproxy.warn("fteproxy.record_layer exception: "+str(e)) break finally: if oneCell: break return retval fteproxy-0.2.19/fteproxy/regex2dfa.py000066400000000000000000000615601241561400200175670ustar00rootroot00000000000000 _regex2dfa = {} _regex2dfa["^\\C+$"] = "0 1 0 0\n" + \ "0 1 1 1\n" + \ "0 1 2 2\n" + \ "0 1 3 3\n" + \ "0 1 4 4\n" + \ "0 1 5 5\n" + \ "0 1 6 6\n" + \ "0 1 7 7\n" + \ "0 1 8 8\n" + \ "0 1 9 9\n" + \ "0 1 10 10\n" + \ "0 1 11 11\n" + \ "0 1 12 12\n" + \ "0 1 13 13\n" + \ "0 1 14 14\n" + \ "0 1 15 15\n" + \ "0 1 16 16\n" + \ "0 1 17 17\n" + \ "0 1 18 18\n" + \ "0 1 19 19\n" + \ "0 1 20 20\n" + \ "0 1 21 21\n" + \ "0 1 22 22\n" + \ "0 1 23 23\n" + \ "0 1 24 24\n" + \ "0 1 25 25\n" + \ "0 1 26 26\n" + \ "0 1 27 27\n" + \ "0 1 28 28\n" + \ "0 1 29 29\n" + \ "0 1 30 30\n" + \ "0 1 31 31\n" + \ "0 1 32 32\n" + \ "0 1 33 33\n" + \ "0 1 34 34\n" + \ "0 1 35 35\n" + \ "0 1 36 36\n" + \ "0 1 37 37\n" + \ "0 1 38 38\n" + \ "0 1 39 39\n" + \ "0 1 40 40\n" + \ "0 1 41 41\n" + \ "0 1 42 42\n" + \ "0 1 43 43\n" + \ "0 1 44 44\n" + \ "0 1 45 45\n" + \ "0 1 46 46\n" + \ "0 1 47 47\n" + \ "0 1 48 48\n" + \ "0 1 49 49\n" + \ "0 1 50 50\n" + \ "0 1 51 51\n" + \ "0 1 52 52\n" + \ "0 1 53 53\n" + \ "0 1 54 54\n" + \ "0 1 55 55\n" + \ "0 1 56 56\n" + \ "0 1 57 57\n" + \ "0 1 58 58\n" + \ "0 1 59 59\n" + \ "0 1 60 60\n" + \ "0 1 61 61\n" + \ "0 1 62 62\n" + \ "0 1 63 63\n" + \ "0 1 64 64\n" + \ "0 1 65 65\n" + \ "0 1 66 66\n" + \ "0 1 67 67\n" + \ "0 1 68 68\n" + \ "0 1 69 69\n" + \ "0 1 70 70\n" + \ "0 1 71 71\n" + \ "0 1 72 72\n" + \ "0 1 73 73\n" + \ "0 1 74 74\n" + \ "0 1 75 75\n" + \ "0 1 76 76\n" + \ "0 1 77 77\n" + \ "0 1 78 78\n" + \ "0 1 79 79\n" + \ "0 1 80 80\n" + \ "0 1 81 81\n" + \ "0 1 82 82\n" + \ "0 1 83 83\n" + \ "0 1 84 84\n" + \ "0 1 85 85\n" + \ "0 1 86 86\n" + \ "0 1 87 87\n" + \ "0 1 88 88\n" + \ "0 1 89 89\n" + \ "0 1 90 90\n" + \ "0 1 91 91\n" + \ "0 1 92 92\n" + \ "0 1 93 93\n" + \ "0 1 94 94\n" + \ "0 1 95 95\n" + \ "0 1 96 96\n" + \ "0 1 97 97\n" + \ "0 1 98 98\n" + \ "0 1 99 99\n" + \ "0 1 100 100\n" + \ "0 1 101 101\n" + \ "0 1 102 102\n" + \ "0 1 103 103\n" + \ "0 1 104 104\n" + \ "0 1 105 105\n" + \ "0 1 106 106\n" + \ "0 1 107 107\n" + \ "0 1 108 108\n" + \ "0 1 109 109\n" + \ "0 1 110 110\n" + \ "0 1 111 111\n" + \ "0 1 112 112\n" + \ "0 1 113 113\n" + \ "0 1 114 114\n" + \ "0 1 115 115\n" + \ "0 1 116 116\n" + \ "0 1 117 117\n" + \ "0 1 118 118\n" + \ "0 1 119 119\n" + \ "0 1 120 120\n" + \ "0 1 121 121\n" + \ "0 1 122 122\n" + \ "0 1 123 123\n" + \ "0 1 124 124\n" + \ "0 1 125 125\n" + \ "0 1 126 126\n" + \ "0 1 127 127\n" + \ "0 1 128 128\n" + \ "0 1 129 129\n" + \ "0 1 130 130\n" + \ "0 1 131 131\n" + \ "0 1 132 132\n" + \ "0 1 133 133\n" + \ "0 1 134 134\n" + \ "0 1 135 135\n" + \ "0 1 136 136\n" + \ "0 1 137 137\n" + \ "0 1 138 138\n" + \ "0 1 139 139\n" + \ "0 1 140 140\n" + \ "0 1 141 141\n" + \ "0 1 142 142\n" + \ "0 1 143 143\n" + \ "0 1 144 144\n" + \ "0 1 145 145\n" + \ "0 1 146 146\n" + \ "0 1 147 147\n" + \ "0 1 148 148\n" + \ "0 1 149 149\n" + \ "0 1 150 150\n" + \ "0 1 151 151\n" + \ "0 1 152 152\n" + \ "0 1 153 153\n" + \ "0 1 154 154\n" + \ "0 1 155 155\n" + \ "0 1 156 156\n" + \ "0 1 157 157\n" + \ "0 1 158 158\n" + \ "0 1 159 159\n" + \ "0 1 160 160\n" + \ "0 1 161 161\n" + \ "0 1 162 162\n" + \ "0 1 163 163\n" + \ "0 1 164 164\n" + \ "0 1 165 165\n" + \ "0 1 166 166\n" + \ "0 1 167 167\n" + \ "0 1 168 168\n" + \ "0 1 169 169\n" + \ "0 1 170 170\n" + \ "0 1 171 171\n" + \ "0 1 172 172\n" + \ "0 1 173 173\n" + \ "0 1 174 174\n" + \ "0 1 175 175\n" + \ "0 1 176 176\n" + \ "0 1 177 177\n" + \ "0 1 178 178\n" + \ "0 1 179 179\n" + \ "0 1 180 180\n" + \ "0 1 181 181\n" + \ "0 1 182 182\n" + \ "0 1 183 183\n" + \ "0 1 184 184\n" + \ "0 1 185 185\n" + \ "0 1 186 186\n" + \ "0 1 187 187\n" + \ "0 1 188 188\n" + \ "0 1 189 189\n" + \ "0 1 190 190\n" + \ "0 1 191 191\n" + \ "0 1 192 192\n" + \ "0 1 193 193\n" + \ "0 1 194 194\n" + \ "0 1 195 195\n" + \ "0 1 196 196\n" + \ "0 1 197 197\n" + \ "0 1 198 198\n" + \ "0 1 199 199\n" + \ "0 1 200 200\n" + \ "0 1 201 201\n" + \ "0 1 202 202\n" + \ "0 1 203 203\n" + \ "0 1 204 204\n" + \ "0 1 205 205\n" + \ "0 1 206 206\n" + \ "0 1 207 207\n" + \ "0 1 208 208\n" + \ "0 1 209 209\n" + \ "0 1 210 210\n" + \ "0 1 211 211\n" + \ "0 1 212 212\n" + \ "0 1 213 213\n" + \ "0 1 214 214\n" + \ "0 1 215 215\n" + \ "0 1 216 216\n" + \ "0 1 217 217\n" + \ "0 1 218 218\n" + \ "0 1 219 219\n" + \ "0 1 220 220\n" + \ "0 1 221 221\n" + \ "0 1 222 222\n" + \ "0 1 223 223\n" + \ "0 1 224 224\n" + \ "0 1 225 225\n" + \ "0 1 226 226\n" + \ "0 1 227 227\n" + \ "0 1 228 228\n" + \ "0 1 229 229\n" + \ "0 1 230 230\n" + \ "0 1 231 231\n" + \ "0 1 232 232\n" + \ "0 1 233 233\n" + \ "0 1 234 234\n" + \ "0 1 235 235\n" + \ "0 1 236 236\n" + \ "0 1 237 237\n" + \ "0 1 238 238\n" + \ "0 1 239 239\n" + \ "0 1 240 240\n" + \ "0 1 241 241\n" + \ "0 1 242 242\n" + \ "0 1 243 243\n" + \ "0 1 244 244\n" + \ "0 1 245 245\n" + \ "0 1 246 246\n" + \ "0 1 247 247\n" + \ "0 1 248 248\n" + \ "0 1 249 249\n" + \ "0 1 250 250\n" + \ "0 1 251 251\n" + \ "0 1 252 252\n" + \ "0 1 253 253\n" + \ "0 1 254 254\n" + \ "0 1 255 255\n" + \ "1 1 0 0\n" + \ "1 1 1 1\n" + \ "1 1 2 2\n" + \ "1 1 3 3\n" + \ "1 1 4 4\n" + \ "1 1 5 5\n" + \ "1 1 6 6\n" + \ "1 1 7 7\n" + \ "1 1 8 8\n" + \ "1 1 9 9\n" + \ "1 1 10 10\n" + \ "1 1 11 11\n" + \ "1 1 12 12\n" + \ "1 1 13 13\n" + \ "1 1 14 14\n" + \ "1 1 15 15\n" + \ "1 1 16 16\n" + \ "1 1 17 17\n" + \ "1 1 18 18\n" + \ "1 1 19 19\n" + \ "1 1 20 20\n" + \ "1 1 21 21\n" + \ "1 1 22 22\n" + \ "1 1 23 23\n" + \ "1 1 24 24\n" + \ "1 1 25 25\n" + \ "1 1 26 26\n" + \ "1 1 27 27\n" + \ "1 1 28 28\n" + \ "1 1 29 29\n" + \ "1 1 30 30\n" + \ "1 1 31 31\n" + \ "1 1 32 32\n" + \ "1 1 33 33\n" + \ "1 1 34 34\n" + \ "1 1 35 35\n" + \ "1 1 36 36\n" + \ "1 1 37 37\n" + \ "1 1 38 38\n" + \ "1 1 39 39\n" + \ "1 1 40 40\n" + \ "1 1 41 41\n" + \ "1 1 42 42\n" + \ "1 1 43 43\n" + \ "1 1 44 44\n" + \ "1 1 45 45\n" + \ "1 1 46 46\n" + \ "1 1 47 47\n" + \ "1 1 48 48\n" + \ "1 1 49 49\n" + \ "1 1 50 50\n" + \ "1 1 51 51\n" + \ "1 1 52 52\n" + \ "1 1 53 53\n" + \ "1 1 54 54\n" + \ "1 1 55 55\n" + \ "1 1 56 56\n" + \ "1 1 57 57\n" + \ "1 1 58 58\n" + \ "1 1 59 59\n" + \ "1 1 60 60\n" + \ "1 1 61 61\n" + \ "1 1 62 62\n" + \ "1 1 63 63\n" + \ "1 1 64 64\n" + \ "1 1 65 65\n" + \ "1 1 66 66\n" + \ "1 1 67 67\n" + \ "1 1 68 68\n" + \ "1 1 69 69\n" + \ "1 1 70 70\n" + \ "1 1 71 71\n" + \ "1 1 72 72\n" + \ "1 1 73 73\n" + \ "1 1 74 74\n" + \ "1 1 75 75\n" + \ "1 1 76 76\n" + \ "1 1 77 77\n" + \ "1 1 78 78\n" + \ "1 1 79 79\n" + \ "1 1 80 80\n" + \ "1 1 81 81\n" + \ "1 1 82 82\n" + \ "1 1 83 83\n" + \ "1 1 84 84\n" + \ "1 1 85 85\n" + \ "1 1 86 86\n" + \ "1 1 87 87\n" + \ "1 1 88 88\n" + \ "1 1 89 89\n" + \ "1 1 90 90\n" + \ "1 1 91 91\n" + \ "1 1 92 92\n" + \ "1 1 93 93\n" + \ "1 1 94 94\n" + \ "1 1 95 95\n" + \ "1 1 96 96\n" + \ "1 1 97 97\n" + \ "1 1 98 98\n" + \ "1 1 99 99\n" + \ "1 1 100 100\n" + \ "1 1 101 101\n" + \ "1 1 102 102\n" + \ "1 1 103 103\n" + \ "1 1 104 104\n" + \ "1 1 105 105\n" + \ "1 1 106 106\n" + \ "1 1 107 107\n" + \ "1 1 108 108\n" + \ "1 1 109 109\n" + \ "1 1 110 110\n" + \ "1 1 111 111\n" + \ "1 1 112 112\n" + \ "1 1 113 113\n" + \ "1 1 114 114\n" + \ "1 1 115 115\n" + \ "1 1 116 116\n" + \ "1 1 117 117\n" + \ "1 1 118 118\n" + \ "1 1 119 119\n" + \ "1 1 120 120\n" + \ "1 1 121 121\n" + \ "1 1 122 122\n" + \ "1 1 123 123\n" + \ "1 1 124 124\n" + \ "1 1 125 125\n" + \ "1 1 126 126\n" + \ "1 1 127 127\n" + \ "1 1 128 128\n" + \ "1 1 129 129\n" + \ "1 1 130 130\n" + \ "1 1 131 131\n" + \ "1 1 132 132\n" + \ "1 1 133 133\n" + \ "1 1 134 134\n" + \ "1 1 135 135\n" + \ "1 1 136 136\n" + \ "1 1 137 137\n" + \ "1 1 138 138\n" + \ "1 1 139 139\n" + \ "1 1 140 140\n" + \ "1 1 141 141\n" + \ "1 1 142 142\n" + \ "1 1 143 143\n" + \ "1 1 144 144\n" + \ "1 1 145 145\n" + \ "1 1 146 146\n" + \ "1 1 147 147\n" + \ "1 1 148 148\n" + \ "1 1 149 149\n" + \ "1 1 150 150\n" + \ "1 1 151 151\n" + \ "1 1 152 152\n" + \ "1 1 153 153\n" + \ "1 1 154 154\n" + \ "1 1 155 155\n" + \ "1 1 156 156\n" + \ "1 1 157 157\n" + \ "1 1 158 158\n" + \ "1 1 159 159\n" + \ "1 1 160 160\n" + \ "1 1 161 161\n" + \ "1 1 162 162\n" + \ "1 1 163 163\n" + \ "1 1 164 164\n" + \ "1 1 165 165\n" + \ "1 1 166 166\n" + \ "1 1 167 167\n" + \ "1 1 168 168\n" + \ "1 1 169 169\n" + \ "1 1 170 170\n" + \ "1 1 171 171\n" + \ "1 1 172 172\n" + \ "1 1 173 173\n" + \ "1 1 174 174\n" + \ "1 1 175 175\n" + \ "1 1 176 176\n" + \ "1 1 177 177\n" + \ "1 1 178 178\n" + \ "1 1 179 179\n" + \ "1 1 180 180\n" + \ "1 1 181 181\n" + \ "1 1 182 182\n" + \ "1 1 183 183\n" + \ "1 1 184 184\n" + \ "1 1 185 185\n" + \ "1 1 186 186\n" + \ "1 1 187 187\n" + \ "1 1 188 188\n" + \ "1 1 189 189\n" + \ "1 1 190 190\n" + \ "1 1 191 191\n" + \ "1 1 192 192\n" + \ "1 1 193 193\n" + \ "1 1 194 194\n" + \ "1 1 195 195\n" + \ "1 1 196 196\n" + \ "1 1 197 197\n" + \ "1 1 198 198\n" + \ "1 1 199 199\n" + \ "1 1 200 200\n" + \ "1 1 201 201\n" + \ "1 1 202 202\n" + \ "1 1 203 203\n" + \ "1 1 204 204\n" + \ "1 1 205 205\n" + \ "1 1 206 206\n" + \ "1 1 207 207\n" + \ "1 1 208 208\n" + \ "1 1 209 209\n" + \ "1 1 210 210\n" + \ "1 1 211 211\n" + \ "1 1 212 212\n" + \ "1 1 213 213\n" + \ "1 1 214 214\n" + \ "1 1 215 215\n" + \ "1 1 216 216\n" + \ "1 1 217 217\n" + \ "1 1 218 218\n" + \ "1 1 219 219\n" + \ "1 1 220 220\n" + \ "1 1 221 221\n" + \ "1 1 222 222\n" + \ "1 1 223 223\n" + \ "1 1 224 224\n" + \ "1 1 225 225\n" + \ "1 1 226 226\n" + \ "1 1 227 227\n" + \ "1 1 228 228\n" + \ "1 1 229 229\n" + \ "1 1 230 230\n" + \ "1 1 231 231\n" + \ "1 1 232 232\n" + \ "1 1 233 233\n" + \ "1 1 234 234\n" + \ "1 1 235 235\n" + \ "1 1 236 236\n" + \ "1 1 237 237\n" + \ "1 1 238 238\n" + \ "1 1 239 239\n" + \ "1 1 240 240\n" + \ "1 1 241 241\n" + \ "1 1 242 242\n" + \ "1 1 243 243\n" + \ "1 1 244 244\n" + \ "1 1 245 245\n" + \ "1 1 246 246\n" + \ "1 1 247 247\n" + \ "1 1 248 248\n" + \ "1 1 249 249\n" + \ "1 1 250 250\n" + \ "1 1 251 251\n" + \ "1 1 252 252\n" + \ "1 1 253 253\n" + \ "1 1 254 254\n" + \ "1 1 255 255\n" + \ "1" _regex2dfa["^GET\\ \\/([a-zA-Z0-9\\.\\/]*) HTTP/1\\.1\\r\\n\\r\\n$"] = "0 1 71 71\n" + \ "1 2 69 69\n" + \ "2 3 84 84\n" + \ "3 4 32 32\n" + \ "4 5 47 47\n" + \ "5 6 32 32\n" + \ "5 5 46 46\n" + \ "5 5 47 47\n" + \ "5 5 48 48\n" + \ "5 5 49 49\n" + \ "5 5 50 50\n" + \ "5 5 51 51\n" + \ "5 5 52 52\n" + \ "5 5 53 53\n" + \ "5 5 54 54\n" + \ "5 5 55 55\n" + \ "5 5 56 56\n" + \ "5 5 57 57\n" + \ "5 5 65 65\n" + \ "5 5 66 66\n" + \ "5 5 67 67\n" + \ "5 5 68 68\n" + \ "5 5 69 69\n" + \ "5 5 70 70\n" + \ "5 5 71 71\n" + \ "5 5 72 72\n" + \ "5 5 73 73\n" + \ "5 5 74 74\n" + \ "5 5 75 75\n" + \ "5 5 76 76\n" + \ "5 5 77 77\n" + \ "5 5 78 78\n" + \ "5 5 79 79\n" + \ "5 5 80 80\n" + \ "5 5 81 81\n" + \ "5 5 82 82\n" + \ "5 5 83 83\n" + \ "5 5 84 84\n" + \ "5 5 85 85\n" + \ "5 5 86 86\n" + \ "5 5 87 87\n" + \ "5 5 88 88\n" + \ "5 5 89 89\n" + \ "5 5 90 90\n" + \ "5 5 97 97\n" + \ "5 5 98 98\n" + \ "5 5 99 99\n" + \ "5 5 100 100\n" + \ "5 5 101 101\n" + \ "5 5 102 102\n" + \ "5 5 103 103\n" + \ "5 5 104 104\n" + \ "5 5 105 105\n" + \ "5 5 106 106\n" + \ "5 5 107 107\n" + \ "5 5 108 108\n" + \ "5 5 109 109\n" + \ "5 5 110 110\n" + \ "5 5 111 111\n" + \ "5 5 112 112\n" + \ "5 5 113 113\n" + \ "5 5 114 114\n" + \ "5 5 115 115\n" + \ "5 5 116 116\n" + \ "5 5 117 117\n" + \ "5 5 118 118\n" + \ "5 5 119 119\n" + \ "5 5 120 120\n" + \ "5 5 121 121\n" + \ "5 5 122 122\n" + \ "6 7 72 72\n" + \ "7 8 84 84\n" + \ "8 9 84 84\n" + \ "9 10 80 80\n" + \ "10 11 47 47\n" + \ "11 12 49 49\n" + \ "12 13 46 46\n" + \ "13 14 49 49\n" + \ "14 15 13 13\n" + \ "15 16 10 10\n" + \ "16 17 13 13\n" + \ "17 18 10 10\n" + \ "18" _regex2dfa["^HTTP/1\\.1\\ 200 OK\\r\\nContent-Type:\\ ([a-zA-Z0-9]+)\\r\\n\\r\\n\\C*$"] = "0 1 72 72\n" + \ "1 2 84 84\n" + \ "2 3 84 84\n" + \ "3 4 80 80\n" + \ "4 5 47 47\n" + \ "5 6 49 49\n" + \ "6 7 46 46\n" + \ "7 8 49 49\n" + \ "8 9 32 32\n" + \ "9 10 50 50\n" + \ "10 11 48 48\n" + \ "11 12 48 48\n" + \ "12 13 32 32\n" + \ "13 14 79 79\n" + \ "14 15 75 75\n" + \ "15 16 13 13\n" + \ "16 17 10 10\n" + \ "17 18 67 67\n" + \ "18 19 111 111\n" + \ "19 20 110 110\n" + \ "20 21 116 116\n" + \ "21 22 101 101\n" + \ "22 23 110 110\n" + \ "23 24 116 116\n" + \ "24 25 45 45\n" + \ "25 26 84 84\n" + \ "26 27 121 121\n" + \ "27 28 112 112\n" + \ "28 29 101 101\n" + \ "29 30 58 58\n" + \ "30 31 32 32\n" + \ "31 32 48 48\n" + \ "31 32 49 49\n" + \ "31 32 50 50\n" + \ "31 32 51 51\n" + \ "31 32 52 52\n" + \ "31 32 53 53\n" + \ "31 32 54 54\n" + \ "31 32 55 55\n" + \ "31 32 56 56\n" + \ "31 32 57 57\n" + \ "31 32 65 65\n" + \ "31 32 66 66\n" + \ "31 32 67 67\n" + \ "31 32 68 68\n" + \ "31 32 69 69\n" + \ "31 32 70 70\n" + \ "31 32 71 71\n" + \ "31 32 72 72\n" + \ "31 32 73 73\n" + \ "31 32 74 74\n" + \ "31 32 75 75\n" + \ "31 32 76 76\n" + \ "31 32 77 77\n" + \ "31 32 78 78\n" + \ "31 32 79 79\n" + \ "31 32 80 80\n" + \ "31 32 81 81\n" + \ "31 32 82 82\n" + \ "31 32 83 83\n" + \ "31 32 84 84\n" + \ "31 32 85 85\n" + \ "31 32 86 86\n" + \ "31 32 87 87\n" + \ "31 32 88 88\n" + \ "31 32 89 89\n" + \ "31 32 90 90\n" + \ "31 32 97 97\n" + \ "31 32 98 98\n" + \ "31 32 99 99\n" + \ "31 32 100 100\n" + \ "31 32 101 101\n" + \ "31 32 102 102\n" + \ "31 32 103 103\n" + \ "31 32 104 104\n" + \ "31 32 105 105\n" + \ "31 32 106 106\n" + \ "31 32 107 107\n" + \ "31 32 108 108\n" + \ "31 32 109 109\n" + \ "31 32 110 110\n" + \ "31 32 111 111\n" + \ "31 32 112 112\n" + \ "31 32 113 113\n" + \ "31 32 114 114\n" + \ "31 32 115 115\n" + \ "31 32 116 116\n" + \ "31 32 117 117\n" + \ "31 32 118 118\n" + \ "31 32 119 119\n" + \ "31 32 120 120\n" + \ "31 32 121 121\n" + \ "31 32 122 122\n" + \ "32 33 13 13\n" + \ "32 32 48 48\n" + \ "32 32 49 49\n" + \ "32 32 50 50\n" + \ "32 32 51 51\n" + \ "32 32 52 52\n" + \ "32 32 53 53\n" + \ "32 32 54 54\n" + \ "32 32 55 55\n" + \ "32 32 56 56\n" + \ "32 32 57 57\n" + \ "32 32 65 65\n" + \ "32 32 66 66\n" + \ "32 32 67 67\n" + \ "32 32 68 68\n" + \ "32 32 69 69\n" + \ "32 32 70 70\n" + \ "32 32 71 71\n" + \ "32 32 72 72\n" + \ "32 32 73 73\n" + \ "32 32 74 74\n" + \ "32 32 75 75\n" + \ "32 32 76 76\n" + \ "32 32 77 77\n" + \ "32 32 78 78\n" + \ "32 32 79 79\n" + \ "32 32 80 80\n" + \ "32 32 81 81\n" + \ "32 32 82 82\n" + \ "32 32 83 83\n" + \ "32 32 84 84\n" + \ "32 32 85 85\n" + \ "32 32 86 86\n" + \ "32 32 87 87\n" + \ "32 32 88 88\n" + \ "32 32 89 89\n" + \ "32 32 90 90\n" + \ "32 32 97 97\n" + \ "32 32 98 98\n" + \ "32 32 99 99\n" + \ "32 32 100 100\n" + \ "32 32 101 101\n" + \ "32 32 102 102\n" + \ "32 32 103 103\n" + \ "32 32 104 104\n" + \ "32 32 105 105\n" + \ "32 32 106 106\n" + \ "32 32 107 107\n" + \ "32 32 108 108\n" + \ "32 32 109 109\n" + \ "32 32 110 110\n" + \ "32 32 111 111\n" + \ "32 32 112 112\n" + \ "32 32 113 113\n" + \ "32 32 114 114\n" + \ "32 32 115 115\n" + \ "32 32 116 116\n" + \ "32 32 117 117\n" + \ "32 32 118 118\n" + \ "32 32 119 119\n" + \ "32 32 120 120\n" + \ "32 32 121 121\n" + \ "32 32 122 122\n" + \ "33 34 10 10\n" + \ "34 35 13 13\n" + \ "35 36 10 10\n" + \ "36 36 0 0\n" + \ "36 36 1 1\n" + \ "36 36 2 2\n" + \ "36 36 3 3\n" + \ "36 36 4 4\n" + \ "36 36 5 5\n" + \ "36 36 6 6\n" + \ "36 36 7 7\n" + \ "36 36 8 8\n" + \ "36 36 9 9\n" + \ "36 36 10 10\n" + \ "36 36 11 11\n" + \ "36 36 12 12\n" + \ "36 36 13 13\n" + \ "36 36 14 14\n" + \ "36 36 15 15\n" + \ "36 36 16 16\n" + \ "36 36 17 17\n" + \ "36 36 18 18\n" + \ "36 36 19 19\n" + \ "36 36 20 20\n" + \ "36 36 21 21\n" + \ "36 36 22 22\n" + \ "36 36 23 23\n" + \ "36 36 24 24\n" + \ "36 36 25 25\n" + \ "36 36 26 26\n" + \ "36 36 27 27\n" + \ "36 36 28 28\n" + \ "36 36 29 29\n" + \ "36 36 30 30\n" + \ "36 36 31 31\n" + \ "36 36 32 32\n" + \ "36 36 33 33\n" + \ "36 36 34 34\n" + \ "36 36 35 35\n" + \ "36 36 36 36\n" + \ "36 36 37 37\n" + \ "36 36 38 38\n" + \ "36 36 39 39\n" + \ "36 36 40 40\n" + \ "36 36 41 41\n" + \ "36 36 42 42\n" + \ "36 36 43 43\n" + \ "36 36 44 44\n" + \ "36 36 45 45\n" + \ "36 36 46 46\n" + \ "36 36 47 47\n" + \ "36 36 48 48\n" + \ "36 36 49 49\n" + \ "36 36 50 50\n" + \ "36 36 51 51\n" + \ "36 36 52 52\n" + \ "36 36 53 53\n" + \ "36 36 54 54\n" + \ "36 36 55 55\n" + \ "36 36 56 56\n" + \ "36 36 57 57\n" + \ "36 36 58 58\n" + \ "36 36 59 59\n" + \ "36 36 60 60\n" + \ "36 36 61 61\n" + \ "36 36 62 62\n" + \ "36 36 63 63\n" + \ "36 36 64 64\n" + \ "36 36 65 65\n" + \ "36 36 66 66\n" + \ "36 36 67 67\n" + \ "36 36 68 68\n" + \ "36 36 69 69\n" + \ "36 36 70 70\n" + \ "36 36 71 71\n" + \ "36 36 72 72\n" + \ "36 36 73 73\n" + \ "36 36 74 74\n" + \ "36 36 75 75\n" + \ "36 36 76 76\n" + \ "36 36 77 77\n" + \ "36 36 78 78\n" + \ "36 36 79 79\n" + \ "36 36 80 80\n" + \ "36 36 81 81\n" + \ "36 36 82 82\n" + \ "36 36 83 83\n" + \ "36 36 84 84\n" + \ "36 36 85 85\n" + \ "36 36 86 86\n" + \ "36 36 87 87\n" + \ "36 36 88 88\n" + \ "36 36 89 89\n" + \ "36 36 90 90\n" + \ "36 36 91 91\n" + \ "36 36 92 92\n" + \ "36 36 93 93\n" + \ "36 36 94 94\n" + \ "36 36 95 95\n" + \ "36 36 96 96\n" + \ "36 36 97 97\n" + \ "36 36 98 98\n" + \ "36 36 99 99\n" + \ "36 36 100 100\n" + \ "36 36 101 101\n" + \ "36 36 102 102\n" + \ "36 36 103 103\n" + \ "36 36 104 104\n" + \ "36 36 105 105\n" + \ "36 36 106 106\n" + \ "36 36 107 107\n" + \ "36 36 108 108\n" + \ "36 36 109 109\n" + \ "36 36 110 110\n" + \ "36 36 111 111\n" + \ "36 36 112 112\n" + \ "36 36 113 113\n" + \ "36 36 114 114\n" + \ "36 36 115 115\n" + \ "36 36 116 116\n" + \ "36 36 117 117\n" + \ "36 36 118 118\n" + \ "36 36 119 119\n" + \ "36 36 120 120\n" + \ "36 36 121 121\n" + \ "36 36 122 122\n" + \ "36 36 123 123\n" + \ "36 36 124 124\n" + \ "36 36 125 125\n" + \ "36 36 126 126\n" + \ "36 36 127 127\n" + \ "36 36 128 128\n" + \ "36 36 129 129\n" + \ "36 36 130 130\n" + \ "36 36 131 131\n" + \ "36 36 132 132\n" + \ "36 36 133 133\n" + \ "36 36 134 134\n" + \ "36 36 135 135\n" + \ "36 36 136 136\n" + \ "36 36 137 137\n" + \ "36 36 138 138\n" + \ "36 36 139 139\n" + \ "36 36 140 140\n" + \ "36 36 141 141\n" + \ "36 36 142 142\n" + \ "36 36 143 143\n" + \ "36 36 144 144\n" + \ "36 36 145 145\n" + \ "36 36 146 146\n" + \ "36 36 147 147\n" + \ "36 36 148 148\n" + \ "36 36 149 149\n" + \ "36 36 150 150\n" + \ "36 36 151 151\n" + \ "36 36 152 152\n" + \ "36 36 153 153\n" + \ "36 36 154 154\n" + \ "36 36 155 155\n" + \ "36 36 156 156\n" + \ "36 36 157 157\n" + \ "36 36 158 158\n" + \ "36 36 159 159\n" + \ "36 36 160 160\n" + \ "36 36 161 161\n" + \ "36 36 162 162\n" + \ "36 36 163 163\n" + \ "36 36 164 164\n" + \ "36 36 165 165\n" + \ "36 36 166 166\n" + \ "36 36 167 167\n" + \ "36 36 168 168\n" + \ "36 36 169 169\n" + \ "36 36 170 170\n" + \ "36 36 171 171\n" + \ "36 36 172 172\n" + \ "36 36 173 173\n" + \ "36 36 174 174\n" + \ "36 36 175 175\n" + \ "36 36 176 176\n" + \ "36 36 177 177\n" + \ "36 36 178 178\n" + \ "36 36 179 179\n" + \ "36 36 180 180\n" + \ "36 36 181 181\n" + \ "36 36 182 182\n" + \ "36 36 183 183\n" + \ "36 36 184 184\n" + \ "36 36 185 185\n" + \ "36 36 186 186\n" + \ "36 36 187 187\n" + \ "36 36 188 188\n" + \ "36 36 189 189\n" + \ "36 36 190 190\n" + \ "36 36 191 191\n" + \ "36 36 192 192\n" + \ "36 36 193 193\n" + \ "36 36 194 194\n" + \ "36 36 195 195\n" + \ "36 36 196 196\n" + \ "36 36 197 197\n" + \ "36 36 198 198\n" + \ "36 36 199 199\n" + \ "36 36 200 200\n" + \ "36 36 201 201\n" + \ "36 36 202 202\n" + \ "36 36 203 203\n" + \ "36 36 204 204\n" + \ "36 36 205 205\n" + \ "36 36 206 206\n" + \ "36 36 207 207\n" + \ "36 36 208 208\n" + \ "36 36 209 209\n" + \ "36 36 210 210\n" + \ "36 36 211 211\n" + \ "36 36 212 212\n" + \ "36 36 213 213\n" + \ "36 36 214 214\n" + \ "36 36 215 215\n" + \ "36 36 216 216\n" + \ "36 36 217 217\n" + \ "36 36 218 218\n" + \ "36 36 219 219\n" + \ "36 36 220 220\n" + \ "36 36 221 221\n" + \ "36 36 222 222\n" + \ "36 36 223 223\n" + \ "36 36 224 224\n" + \ "36 36 225 225\n" + \ "36 36 226 226\n" + \ "36 36 227 227\n" + \ "36 36 228 228\n" + \ "36 36 229 229\n" + \ "36 36 230 230\n" + \ "36 36 231 231\n" + \ "36 36 232 232\n" + \ "36 36 233 233\n" + \ "36 36 234 234\n" + \ "36 36 235 235\n" + \ "36 36 236 236\n" + \ "36 36 237 237\n" + \ "36 36 238 238\n" + \ "36 36 239 239\n" + \ "36 36 240 240\n" + \ "36 36 241 241\n" + \ "36 36 242 242\n" + \ "36 36 243 243\n" + \ "36 36 244 244\n" + \ "36 36 245 245\n" + \ "36 36 246 246\n" + \ "36 36 247 247\n" + \ "36 36 248 248\n" + \ "36 36 249 249\n" + \ "36 36 250 250\n" + \ "36 36 251 251\n" + \ "36 36 252 252\n" + \ "36 36 253 253\n" + \ "36 36 254 254\n" + \ "36 36 255 255\n" + \ "36" _regex2dfa["^SSH\\-2\\.0\\C*$"] = "0 1 83 83\n" + \ "1 2 83 83\n" + \ "2 3 72 72\n" + \ "3 4 45 45\n" + \ "4 5 50 50\n" + \ "5 6 46 46\n" + \ "6 7 48 48\n" + \ "7 7 0 0\n" + \ "7 7 1 1\n" + \ "7 7 2 2\n" + \ "7 7 3 3\n" + \ "7 7 4 4\n" + \ "7 7 5 5\n" + \ "7 7 6 6\n" + \ "7 7 7 7\n" + \ "7 7 8 8\n" + \ "7 7 9 9\n" + \ "7 7 10 10\n" + \ "7 7 11 11\n" + \ "7 7 12 12\n" + \ "7 7 13 13\n" + \ "7 7 14 14\n" + \ "7 7 15 15\n" + \ "7 7 16 16\n" + \ "7 7 17 17\n" + \ "7 7 18 18\n" + \ "7 7 19 19\n" + \ "7 7 20 20\n" + \ "7 7 21 21\n" + \ "7 7 22 22\n" + \ "7 7 23 23\n" + \ "7 7 24 24\n" + \ "7 7 25 25\n" + \ "7 7 26 26\n" + \ "7 7 27 27\n" + \ "7 7 28 28\n" + \ "7 7 29 29\n" + \ "7 7 30 30\n" + \ "7 7 31 31\n" + \ "7 7 32 32\n" + \ "7 7 33 33\n" + \ "7 7 34 34\n" + \ "7 7 35 35\n" + \ "7 7 36 36\n" + \ "7 7 37 37\n" + \ "7 7 38 38\n" + \ "7 7 39 39\n" + \ "7 7 40 40\n" + \ "7 7 41 41\n" + \ "7 7 42 42\n" + \ "7 7 43 43\n" + \ "7 7 44 44\n" + \ "7 7 45 45\n" + \ "7 7 46 46\n" + \ "7 7 47 47\n" + \ "7 7 48 48\n" + \ "7 7 49 49\n" + \ "7 7 50 50\n" + \ "7 7 51 51\n" + \ "7 7 52 52\n" + \ "7 7 53 53\n" + \ "7 7 54 54\n" + \ "7 7 55 55\n" + \ "7 7 56 56\n" + \ "7 7 57 57\n" + \ "7 7 58 58\n" + \ "7 7 59 59\n" + \ "7 7 60 60\n" + \ "7 7 61 61\n" + \ "7 7 62 62\n" + \ "7 7 63 63\n" + \ "7 7 64 64\n" + \ "7 7 65 65\n" + \ "7 7 66 66\n" + \ "7 7 67 67\n" + \ "7 7 68 68\n" + \ "7 7 69 69\n" + \ "7 7 70 70\n" + \ "7 7 71 71\n" + \ "7 7 72 72\n" + \ "7 7 73 73\n" + \ "7 7 74 74\n" + \ "7 7 75 75\n" + \ "7 7 76 76\n" + \ "7 7 77 77\n" + \ "7 7 78 78\n" + \ "7 7 79 79\n" + \ "7 7 80 80\n" + \ "7 7 81 81\n" + \ "7 7 82 82\n" + \ "7 7 83 83\n" + \ "7 7 84 84\n" + \ "7 7 85 85\n" + \ "7 7 86 86\n" + \ "7 7 87 87\n" + \ "7 7 88 88\n" + \ "7 7 89 89\n" + \ "7 7 90 90\n" + \ "7 7 91 91\n" + \ "7 7 92 92\n" + \ "7 7 93 93\n" + \ "7 7 94 94\n" + \ "7 7 95 95\n" + \ "7 7 96 96\n" + \ "7 7 97 97\n" + \ "7 7 98 98\n" + \ "7 7 99 99\n" + \ "7 7 100 100\n" + \ "7 7 101 101\n" + \ "7 7 102 102\n" + \ "7 7 103 103\n" + \ "7 7 104 104\n" + \ "7 7 105 105\n" + \ "7 7 106 106\n" + \ "7 7 107 107\n" + \ "7 7 108 108\n" + \ "7 7 109 109\n" + \ "7 7 110 110\n" + \ "7 7 111 111\n" + \ "7 7 112 112\n" + \ "7 7 113 113\n" + \ "7 7 114 114\n" + \ "7 7 115 115\n" + \ "7 7 116 116\n" + \ "7 7 117 117\n" + \ "7 7 118 118\n" + \ "7 7 119 119\n" + \ "7 7 120 120\n" + \ "7 7 121 121\n" + \ "7 7 122 122\n" + \ "7 7 123 123\n" + \ "7 7 124 124\n" + \ "7 7 125 125\n" + \ "7 7 126 126\n" + \ "7 7 127 127\n" + \ "7 7 128 128\n" + \ "7 7 129 129\n" + \ "7 7 130 130\n" + \ "7 7 131 131\n" + \ "7 7 132 132\n" + \ "7 7 133 133\n" + \ "7 7 134 134\n" + \ "7 7 135 135\n" + \ "7 7 136 136\n" + \ "7 7 137 137\n" + \ "7 7 138 138\n" + \ "7 7 139 139\n" + \ "7 7 140 140\n" + \ "7 7 141 141\n" + \ "7 7 142 142\n" + \ "7 7 143 143\n" + \ "7 7 144 144\n" + \ "7 7 145 145\n" + \ "7 7 146 146\n" + \ "7 7 147 147\n" + \ "7 7 148 148\n" + \ "7 7 149 149\n" + \ "7 7 150 150\n" + \ "7 7 151 151\n" + \ "7 7 152 152\n" + \ "7 7 153 153\n" + \ "7 7 154 154\n" + \ "7 7 155 155\n" + \ "7 7 156 156\n" + \ "7 7 157 157\n" + \ "7 7 158 158\n" + \ "7 7 159 159\n" + \ "7 7 160 160\n" + \ "7 7 161 161\n" + \ "7 7 162 162\n" + \ "7 7 163 163\n" + \ "7 7 164 164\n" + \ "7 7 165 165\n" + \ "7 7 166 166\n" + \ "7 7 167 167\n" + \ "7 7 168 168\n" + \ "7 7 169 169\n" + \ "7 7 170 170\n" + \ "7 7 171 171\n" + \ "7 7 172 172\n" + \ "7 7 173 173\n" + \ "7 7 174 174\n" + \ "7 7 175 175\n" + \ "7 7 176 176\n" + \ "7 7 177 177\n" + \ "7 7 178 178\n" + \ "7 7 179 179\n" + \ "7 7 180 180\n" + \ "7 7 181 181\n" + \ "7 7 182 182\n" + \ "7 7 183 183\n" + \ "7 7 184 184\n" + \ "7 7 185 185\n" + \ "7 7 186 186\n" + \ "7 7 187 187\n" + \ "7 7 188 188\n" + \ "7 7 189 189\n" + \ "7 7 190 190\n" + \ "7 7 191 191\n" + \ "7 7 192 192\n" + \ "7 7 193 193\n" + \ "7 7 194 194\n" + \ "7 7 195 195\n" + \ "7 7 196 196\n" + \ "7 7 197 197\n" + \ "7 7 198 198\n" + \ "7 7 199 199\n" + \ "7 7 200 200\n" + \ "7 7 201 201\n" + \ "7 7 202 202\n" + \ "7 7 203 203\n" + \ "7 7 204 204\n" + \ "7 7 205 205\n" + \ "7 7 206 206\n" + \ "7 7 207 207\n" + \ "7 7 208 208\n" + \ "7 7 209 209\n" + \ "7 7 210 210\n" + \ "7 7 211 211\n" + \ "7 7 212 212\n" + \ "7 7 213 213\n" + \ "7 7 214 214\n" + \ "7 7 215 215\n" + \ "7 7 216 216\n" + \ "7 7 217 217\n" + \ "7 7 218 218\n" + \ "7 7 219 219\n" + \ "7 7 220 220\n" + \ "7 7 221 221\n" + \ "7 7 222 222\n" + \ "7 7 223 223\n" + \ "7 7 224 224\n" + \ "7 7 225 225\n" + \ "7 7 226 226\n" + \ "7 7 227 227\n" + \ "7 7 228 228\n" + \ "7 7 229 229\n" + \ "7 7 230 230\n" + \ "7 7 231 231\n" + \ "7 7 232 232\n" + \ "7 7 233 233\n" + \ "7 7 234 234\n" + \ "7 7 235 235\n" + \ "7 7 236 236\n" + \ "7 7 237 237\n" + \ "7 7 238 238\n" + \ "7 7 239 239\n" + \ "7 7 240 240\n" + \ "7 7 241 241\n" + \ "7 7 242 242\n" + \ "7 7 243 243\n" + \ "7 7 244 244\n" + \ "7 7 245 245\n" + \ "7 7 246 246\n" + \ "7 7 247 247\n" + \ "7 7 248 248\n" + \ "7 7 249 249\n" + \ "7 7 250 250\n" + \ "7 7 251 251\n" + \ "7 7 252 252\n" + \ "7 7 253 253\n" + \ "7 7 254 254\n" + \ "7 7 255 255\n" + \ "7" def regex2dfa(regex): if regex not in _regex2dfa: raise Exception("Regex not supported, please see fte/regex2dfa.py.") return _regex2dfa[regex] fteproxy-0.2.19/fteproxy/relay.py000066400000000000000000000125301241561400200170250ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import time import socket import threading import fteproxy.conf import fteproxy.network_io class worker(threading.Thread): """``fteproxy.relay.worker`` is responsible for relaying data between two sockets. Given ``socket1`` and ``socket2``, the worker will forward all data from ``socket1`` to ``socket2``, and ``socket2`` to ``socket1``. This class is a subclass of threading.Thread and does not start relaying until start() is called. The run method terminates when either ``socket1`` or ``socket2`` is detected to be closed. """ def __init__(self, socket1, socket2): threading.Thread.__init__(self) self._socket1 = socket1 self._socket2 = socket2 self._running = False def run(self): """It's the responsibility of run to forward data from ``socket1`` to ``socket2`` and from ``socket2`` to ``socket1``. The ``run()`` method terminates and closes both sockets if ``fteproxy.network_io.recvall_from_socket`` returns a negative result for ``success``. """ self._running = True try: throttle = fteproxy.conf.getValue('runtime.fteproxy.relay.throttle') while self._running: [success, _data] = fteproxy.network_io.recvall_from_socket( self._socket1) if not success: break if _data: fteproxy.network_io.sendall_to_socket(self._socket2, _data) else: time.sleep(throttle) except Exception as e: fteproxy.warn("fteproxy.worker terminated prematurely: " + str(e)) finally: fteproxy.network_io.close_socket(self._socket1) fteproxy.network_io.close_socket(self._socket2) def stop(self): """Terminate the thread and stop listening on ``local_ip:local_port``. """ self._running = False class listener(threading.Thread): """It's the responsibility of ``fteproxy.relay.listener`` to bind to ``local_ip:local_port``. Once bound it will then relay all incoming connections to ``remote_ip:remote_port``. All new incoming connections are wrapped with ``onNewIncomingConnection``. All new outgoing connections are wrapped with ``onNewOutgoingConnection``. By default the functions ``onNewIncomingConnection`` and ``onNewOutgoingConnection`` are the identity function. """ def __init__(self, local_ip, local_port, remote_ip, remote_port): threading.Thread.__init__(self) self._running = False self._local_ip = local_ip self._local_port = local_port self._remote_ip = remote_ip self._remote_port = remote_port def _instantiateSocket(self): try: self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._sock.bind((self._local_ip, self._local_port)) self._sock.listen(fteproxy.conf.getValue('runtime.fteproxy.relay.backlog')) self._sock.settimeout( fteproxy.conf.getValue('runtime.fteproxy.relay.accept_timeout')) except Exception as e: fteproxy.fatal_error('Failed to bind to ' + str((self._local_ip, self._local_port)) + ': ' + str(e)) def run(self): """Bind to ``local_ip:local_port`` and forward all connections to ``remote_ip:remote_port``. """ self._instantiateSocket() self._running = True while self._running: try: conn, addr = self._sock.accept() new_stream = socket.socket(socket.AF_INET, socket.SOCK_STREAM) new_stream.connect((self._remote_ip, self._remote_port)) conn = self.onNewIncomingConnection(conn) new_stream = self.onNewOutgoingConnection(new_stream) conn.settimeout( fteproxy.conf.getValue('runtime.fteproxy.relay.socket_timeout')) new_stream.settimeout( fteproxy.conf.getValue('runtime.fteproxy.relay.socket_timeout')) w1 = worker(conn, new_stream) w2 = worker(new_stream, conn) w1.start() w2.start() except socket.timeout: continue except socket.error as e: fteproxy.warn('socket.error in fteproxy.listener: ' + str(e)) continue except Exception as e: fteproxy.warn('exception in fteproxy.listener: ' + str(e)) break def stop(self): """Terminate the thread and stop listening on ``local_ip:local_port``. """ self._running = False fteproxy.network_io.close_socket(self._sock) def onNewIncomingConnection(self, socket): """``onNewIncomingConnection`` returns the socket unmodified, by default we do not need to perform any modifications to incoming data streams. """ return socket def onNewOutgoingConnection(self, socket): """``onNewOutgoingConnection`` returns the socket unmodified, by default we do not need to perform any modifications to incoming data streams. """ return socket fteproxy-0.2.19/fteproxy/server.py000066400000000000000000000006771241561400200172300ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import fteproxy.relay class listener(fteproxy.relay.listener): def onNewIncomingConnection(self, socket): """On an incoming data stream we wrap it with ``fteproxy.wrap_socket``, with no parameters. By default we want the regular expressions to be negotiated in-band, specified by the client. """ socket = fteproxy.wrap_socket(socket) return socket fteproxy-0.2.19/fteproxy/tests/000077500000000000000000000000001241561400200165005ustar00rootroot00000000000000fteproxy-0.2.19/fteproxy/tests/__init__.py000066400000000000000000000005241241561400200206120ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import unittest import test_record_layer import test_relay def suite(): suite = unittest.TestSuite() suite.addTests(test_record_layer.suite()) suite.addTests(test_relay.suite()) return suite if __name__ == '__main__': unittest.TextTestRunner(verbosity=2).run(suite()) fteproxy-0.2.19/fteproxy/tests/test_record_layer.py000077500000000000000000000057461241561400200226020ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import unittest import fteproxy.record_layer import fteproxy.regex2dfa import fte.encoder START = 0 ITERATIONS = 2048 STEP = 64 class Tests(unittest.TestCase): @classmethod def setUp(self): fteproxy.conf.setValue('runtime.mode', 'client') self.record_layers_info = [] self.record_layers_outgoing = [] self.record_layers_incoming = [] definitions = fteproxy.defs.load_definitions() for language in definitions.keys(): regex = fteproxy.defs.getRegex(language) fixed_slice = fteproxy.defs.getFixedSlice(language) regex_encoder = fte.encoder.DfaEncoder(fteproxy.regex2dfa.regex2dfa(regex), fixed_slice) encoder = fteproxy.record_layer.Encoder( encoder=regex_encoder) decoder = fteproxy.record_layer.Decoder( decoder=regex_encoder) self.record_layers_info.append(language) self.record_layers_outgoing.append(encoder) self.record_layers_incoming.append(decoder) def testReclayer_basic(self): for i in range(len(self.record_layers_outgoing)): record_layer_outgoing = self.record_layers_outgoing[i] record_layer_incoming = self.record_layers_incoming[i] for j in range(START, ITERATIONS, STEP): P = 'X' * j + 'Y' record_layer_outgoing.push(P) while True: data = record_layer_outgoing.pop() if not data: break record_layer_incoming.push(data) Y = '' while True: data = record_layer_incoming.pop() if not data: break Y += data self.assertEquals(P, Y, (self.record_layers_info[i], P, Y)) def testReclayer_concat(self): for i in range(len(self.record_layers_outgoing)): record_layer_outgoing = self.record_layers_outgoing[i] record_layer_incoming = self.record_layers_incoming[i] for j in range(START, ITERATIONS, STEP): ptxt = '' X = '' P = 'X' * j + 'Y' ptxt += P record_layer_outgoing.push(P) while True: data = record_layer_outgoing.pop() if not data: break X += data record_layer_incoming.push(X) Y = '' while True: data = record_layer_incoming.pop() if not data: break Y += data self.assertEquals(ptxt, Y, self.record_layers_info[i]) def suite(): loader = unittest.TestLoader() suite = unittest.TestSuite() suite.addTest(loader.loadTestsFromTestCase(Tests)) return suite fteproxy-0.2.19/fteproxy/tests/test_relay.py000066400000000000000000000063751241561400200212400ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import time import socket import random import unittest import fteproxy.network_io import fteproxy.relay import fteproxy.client import fteproxy.server LOCAL_INTERFACE = '127.0.0.1' class Tests(unittest.TestCase): def setUp(self): time.sleep(1) self._server = fteproxy.server.listener(LOCAL_INTERFACE, fteproxy.conf.getValue( 'runtime.server.port'), LOCAL_INTERFACE, fteproxy.conf.getValue('runtime.proxy.port')) self._client = fteproxy.client.listener(LOCAL_INTERFACE, fteproxy.conf.getValue( 'runtime.client.port'), LOCAL_INTERFACE, fteproxy.conf.getValue('runtime.server.port')) self._server.start() self._client.start() time.sleep(1) def tearDown(self): self._server.stop() self._client.stop() def testTenSerialStreams(self): for i in range(10): self._testStream() def _testStream(self): uniq_id = str(random.choice(range(2 ** 10))) expected_msg = 'Hello, world' * 100 + uniq_id actual_msg = '' proxy_socket = None client_socket = None server_conn = None try: proxy_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) proxy_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) proxy_socket.bind( (LOCAL_INTERFACE, fteproxy.conf.getValue('runtime.proxy.port'))) proxy_socket.listen(fteproxy.conf.getValue('runtime.fteproxy.relay.backlog')) client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect((LOCAL_INTERFACE, fteproxy.conf.getValue('runtime.client.port'))) server_conn, addr = proxy_socket.accept() server_conn.settimeout(1) client_socket.sendall(expected_msg) while True: try: data = server_conn.recv(1024) if not data: break actual_msg += data assert expected_msg.startswith(actual_msg) if actual_msg == expected_msg: break except socket.timeout: continue except socket.error: break except Exception as e: fteproxy.fatal_error("failed to transmit data: " + str(e)) finally: if proxy_socket: fteproxy.network_io.close_socket(proxy_socket) if server_conn: fteproxy.network_io.close_socket(server_conn) if client_socket: fteproxy.network_io.close_socket(client_socket) self.assertEquals(expected_msg, actual_msg) def suite(): loader = unittest.TestLoader() suite = unittest.TestSuite() suite.addTest(loader.loadTestsFromTestCase(Tests)) return suite fteproxy-0.2.19/requirements.txt000066400000000000000000000000361241561400200167410ustar00rootroot00000000000000fte pyptlib obfsproxy twisted fteproxy-0.2.19/setup.py000066400000000000000000000024431241561400200151730ustar00rootroot00000000000000#!/usr/bin/env python from setuptools import setup from setuptools import Extension import glob import sys import os if os.name == 'nt': import py2exe with open('fteproxy/VERSION') as fh: FTEPROXY_RELEASE = fh.read().strip() package_data_files = [] package_data_files += ['VERSION'] for filename in glob.glob('fteproxy/defs/*.json'): jsonFile = filename.split('/')[-1] package_data_files += ['defs/'+jsonFile] package_data = {'fteproxy': package_data_files} with open('README') as file: long_description = file.read() setup(name='fteproxy', long_description=long_description, console=['./bin/fteproxy'], test_suite='fteproxy.tests.suite', zipfile="fteproxy.zip", package_data=package_data, options={"py2exe": { "bundle_files": 2, "compressed": True, "dll_excludes": ["w9xpopen.exe"], } }, version=FTEPROXY_RELEASE, description='fteproxy', author='Kevin P. Dyer', author_email='kpdyer@gmail.com', url='https://fteproxy.org/', packages=['fteproxy', 'fteproxy.defs', 'fteproxy.tests'], install_requires=['fte','twisted','pyptlib','obfsproxy'], entry_points = { 'console_scripts': [ 'fteproxy = fteproxy.cli:main' ] }, ) fteproxy-0.2.19/setup_tbb.py000066400000000000000000000014761241561400200160270ustar00rootroot00000000000000#!/usr/bin/env python from setuptools import setup from setuptools import Extension import glob import sys import os if os.name == 'nt': import py2exe with open('fteproxy/VERSION') as fh: FTEPROXY_RELEASE = fh.read().strip() setup(name='fteproxy', console=['./bin/fteproxy'], zipfile="fteproxy.zip", options={"py2exe": { "dll_excludes": ["w9xpopen.exe"], "includes": ["twisted", "pyptlib", "Crypto", "txsocksx", "parsley", "obfsproxy", "fte"], } }, version=FTEPROXY_RELEASE, description='fteproxy', author='Kevin P. Dyer', author_email='kpdyer@gmail.com', url='https://github.com/kpdyer/fteproxy', packages=['fteproxy', 'fteproxy.defs', 'fteproxy.tests'], install_requires=['pyptlib', 'obfsproxy', 'twisted', 'fte'] ) fteproxy-0.2.19/systemtests000077500000000000000000000137461241561400200160260ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import os import time import socket import string import random import fteproxy.conf import fteproxy.defs import fteproxy.network_io BYTES_TO_SEND = 2 ** 18 TRIALS = 2 ** 8 BLOCK_SIZE = 2 ** 12 TIMEOUT = 120 BIND_IP = '127.0.0.1' CLIENT_PORT = 8079 SERVER_PORT = 8080 PROXY_PORT = 8081 def executeCmd(cmd): os.system(cmd) def main(): """Start an fteproxy server. Iterate through the FTE formats available to fteproxy. For each format, spawn a client using that format, connect to the fteproxy server and pump data through the connection. Verify the data successfully traversed the client and server, upon success output performance statistics. """ def_dir = os.path.join(fteproxy.conf.getValue('general.defs_dir')) def_file = fteproxy.conf.getValue('fteproxy.defs.release') + '.json' def_abspath = os.path.normpath(os.path.join(def_dir, def_file)) print 'Testing formats in', def_abspath start_server() languages = fteproxy.defs.load_definitions().keys() for language in languages: if language.endswith('request'): language_name = language[:-8] elapsed, bytesSent = doTest(language_name) throughput = ((1.0 * bytesSent / elapsed) / (2 ** 20)) * \ (2 ** 3) throughput = round(throughput, 2) upstream_regex = fteproxy.defs.getRegex(language_name + '-request') downstream_regex = fteproxy.defs.getRegex(language_name + '-response') print ' + SUCCESS, format_name="' + language_name + '"' print ' - client-server regex: "' + upstream_regex + '"' print ' - server-client regex: "' + downstream_regex + '"' print ' - test duration: ' + str(round(elapsed, 2)) + " seconds" print ' - goodput: ' + str(throughput) + 'Mbps' stop_server() print "****** ALL TESTS COMPLETE, SUCCESS!!" def random_string(size): """Return a random alphanumeric string of length ``size``.""" chars = string.ascii_uppercase + string.digits return ''.join(random.choice(chars) for x in range(size)) def doTest(language_name): """Given the ``language_name``, start the fteproxy client and pump ``BYTES_TO_SEND*TIRALS`` bytes via the fteproxy client and server. """ elapsed = -1 bytesSent = 0 try: start_client(language_name) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((BIND_IP, PROXY_PORT)) sock.listen(100) new_stream = socket.socket(socket.AF_INET, socket.SOCK_STREAM) new_stream.connect((BIND_IP, CLIENT_PORT)) conn, addr = sock.accept() conn.settimeout(0.1) startTimer = time.time() expected_msg = random_string(BYTES_TO_SEND) elapsed = -1 for i in range(TRIALS): bytesSent += BYTES_TO_SEND actual_msg = '' expected_buffer = expected_msg while True: if expected_buffer: bytessent = new_stream.send(expected_buffer[:BLOCK_SIZE]) expected_buffer = expected_buffer[bytessent:] else: break while True: time.sleep(0.00001) [is_alive, data] = fteproxy.network_io.recvall_from_socket(conn) actual_msg += data assert expected_msg.startswith(actual_msg) if (actual_msg == expected_msg): break if elapsed > TIMEOUT or not is_alive: raise Exception( "!!!!!!!!!!! System tests failed to execute properly") elapsed = time.time() - startTimer fteproxy.network_io.close_socket(conn) fteproxy.network_io.close_socket(new_stream) fteproxy.network_io.close_socket(sock) finally: stop_client() return elapsed, bytesSent def start_client(language_name): """Start our fteproxy client, block until it's ready.""" executeCmd("./bin/fteproxy --quiet --mode client" + " --upstream-format " + language_name + "-request" + " --downstream-format " + language_name + "-response" + " --client_ip " + BIND_IP + " --client_port " + str(CLIENT_PORT) + " --server_ip " + BIND_IP + " --server_port " + str(SERVER_PORT) + " &") waitForListener(BIND_IP, CLIENT_PORT) def start_server(): """Start our fteproxy server, block until it's ready.""" executeCmd("./bin/fteproxy --quiet --mode server" + " --server_ip " + BIND_IP + " --server_port " + str(SERVER_PORT) + " --proxy_ip " + BIND_IP + " --proxy_port " + str(PROXY_PORT) + " &") waitForListener(BIND_IP, SERVER_PORT) def stop_client(): """Stop our fteproxy client.""" executeCmd("./bin/fteproxy --quiet --mode client --stop") time.sleep(1) def stop_server(): """Stop our fteproxy server.""" executeCmd("./bin/fteproxy --quiet --mode server --stop") time.sleep(1) class ListenerNotReady(Exception): pass def waitForListener(ip, port, maxWait=60): """Block until a there's a process listening on ip:port. Return ``true`` on success, raise ``ListenerNotReady`` exception otherwise. """ time.sleep(5) startTime = time.time() success = False new_stream = None while not success and (time.time() - startTime) < maxWait: time.sleep(0.1) try: new_stream = socket.socket(socket.AF_INET, socket.SOCK_STREAM) new_stream.settimeout(0.1) new_stream.connect((ip, port)) success = True except: continue finally: if new_stream: new_stream.close() new_stream = None if not success: raise ListenerNotReady() time.sleep(1) return success if __name__ == '__main__': main() fteproxy-0.2.19/unittests000077500000000000000000000000561241561400200154470ustar00rootroot00000000000000#!/bin/sh ./bin/fteproxy --mode test --quiet