├── test ├── CMakeLists.txt ├── unit │ ├── regress.h │ ├── CMakeLists.txt │ ├── regress_main.c │ ├── regress_cfg.cfg │ ├── tinytest.h │ ├── regress_cfg.c │ ├── tinytest_macros.h │ └── tinytest.c └── smoke │ ├── CMakeLists.txt │ ├── ca.crt │ ├── client.key │ ├── server.key │ ├── server.crt │ ├── client.crt │ ├── smoke_cfg.cfg │ ├── smoke_downstream.c │ └── smoke.sh ├── src ├── CMakeLists.txt ├── request.c ├── lzq.h ├── lzlog.h ├── lzq.c ├── util.c ├── logger.c ├── lzlog.c ├── rproxy.h └── ssl.c ├── CMakeModules ├── Findlibevhtp.cmake ├── FindLibConfuse.cmake └── FindLibEvent.cmake ├── LICENSE ├── libs └── CMakeLists.txt ├── CMakeLists.txt ├── sample.cfg ├── README.markdown └── ChangeLog /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | add_subdirectory(smoke) 4 | #add_subdirectory(unit) 5 | -------------------------------------------------------------------------------- /test/unit/regress.h: -------------------------------------------------------------------------------- 1 | #ifndef __REGRESS_H__ 2 | #define __REGRESS_H__ 3 | 4 | #include "tinytest.h" 5 | #include "tinytest_macros.h" 6 | 7 | extern struct testcase_t cfg_testcases[]; 8 | 9 | #endif 10 | 11 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | add_executable(rproxy cfg.c downstream.c rproxy.c ssl.c request.c lzlog.c lzq.c logger.c util.c) 4 | target_link_libraries(rproxy ${RPROXY_EXTERNAL_LIBS} ${SYS_LIBS}) 5 | 6 | install(TARGETS rproxy RUNTIME DESTINATION bin) 7 | -------------------------------------------------------------------------------- /CMakeModules/Findlibevhtp.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the libevhtp 2 | # Once done this will define 3 | # 4 | # LIBEVHTP_FOUND - System has libevhtp 5 | # LIBEVHTP_INCLUDE_DIR - the libevhtp include directory 6 | # LIBEVHTP_LIBRARY 0 The library needed to use libevhtp 7 | 8 | FIND_PATH(LIBEVHTP_INCLUDE_DIR NAMES evhtp.h) 9 | FIND_LIBRARY(LIBEVHTP_LIBRARY NAMES evhtp) 10 | 11 | INCLUDE(FindPackageHandleStandardArgs) 12 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(libevhtp DEFAULT_MSG LIBEVHTP_LIBRARY LIBEVHTP_INCLUDE_DIR) 13 | MARK_AS_ADVANCED(LIBEVHTP_INCLUDE_DIR LIBEVHTP_LIBRARY) 14 | 15 | -------------------------------------------------------------------------------- /CMakeModules/FindLibConfuse.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the LibConfuse 2 | # Once done this will define 3 | # 4 | # LIBCONFUSE_FOUND - System has LibConfuse 5 | # LIBCONFUSE_INCLUDE_DIR - the LibConfuse include directory 6 | # LIBCONFUSE_LIBRARY 0 The library needed to use LibConfuse 7 | 8 | FIND_PATH(LIBCONFUSE_INCLUDE_DIR NAMES confuse.h) 9 | FIND_LIBRARY(LIBCONFUSE_LIBRARY NAMES confuse) 10 | 11 | INCLUDE(FindPackageHandleStandardArgs) 12 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibConfuse DEFAULT_MSG LIBCONFUSE_LIBRARY LIBCONFUSE_INCLUDE_DIR) 13 | MARK_AS_ADVANCED(LIBCONFUSE_INCLUDE_DIR LIBCONFUSE_LIBRARY) 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* Copyright [2012] [Mandiant, inc] 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | -------------------------------------------------------------------------------- /test/smoke/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | set(SMOKE_SOURCES 4 | smoke_downstream.c 5 | ) 6 | 7 | add_executable(smoke_downstream ${SMOKE_SOURCES}) 8 | target_link_libraries(smoke_downstream ${RPROXY_EXTERNAL_LIBS} ${SYS_LIBS}) 9 | 10 | set (SMOKE_FILES 11 | ${PROJECT_SOURCE_DIR}/test/smoke/smoke.sh 12 | ${PROJECT_SOURCE_DIR}/test/smoke/smoke_cfg.cfg 13 | ${PROJECT_SOURCE_DIR}/test/smoke/ca.crt 14 | ${PROJECT_SOURCE_DIR}/test/smoke/server.key 15 | ${PROJECT_SOURCE_DIR}/test/smoke/server.crt 16 | ${PROJECT_SOURCE_DIR}/test/smoke/client.key 17 | ${PROJECT_SOURCE_DIR}/test/smoke/client.crt 18 | ) 19 | 20 | file(COPY ${SMOKE_FILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) 21 | -------------------------------------------------------------------------------- /test/unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | set(REGRESS_SOURCES 4 | ${CMAKE_CURRENT_SOURCE_DIR}/../../src/cfg.c 5 | ${CMAKE_CURRENT_SOURCE_DIR}/../../src/downstream.c 6 | ${CMAKE_CURRENT_SOURCE_DIR}/../../src/request.c 7 | ${CMAKE_CURRENT_SOURCE_DIR}/../../src/ssl.c 8 | ${CMAKE_CURRENT_SOURCE_DIR}/../../src/logger.c 9 | ${CMAKE_CURRENT_SOURCE_DIR}/../../test/unit/tinytest.c 10 | regress_cfg.c 11 | regress_main.c 12 | ) 13 | 14 | include_directories(REGRES_INCLUDE_DIRS 15 | ${CMAKE_CURRENT_SOURCE_DIR}/../../src 16 | ${CMAKE_CURRENT_SOURCE_DIR} 17 | ) 18 | 19 | add_executable(regress_main ${REGRESS_SOURCES}) 20 | target_link_libraries(regress_main ${RPROXY_EXTERNAL_LIBS} ${SYS_LIBS}) 21 | 22 | file(COPY regress_cfg.cfg DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) 23 | -------------------------------------------------------------------------------- /test/smoke/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC+jCCAeKgAwIBAgIJAJwipF6miqBgMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV 3 | BAMMAkNBMB4XDTEyMDIwNDE2NTIzNloXDTIyMDIwMTE2NTIzNlowDTELMAkGA1UE 4 | AwwCQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDcwdCzHHML1n7A 5 | 4frEvWBBm1G1kX9uQNXqRD3gBuyCJOv3xJ4MZRB7pBBUVauAixvBWoSHhulktbiB 6 | 5S7dUhNGjIaSNgY2sxsKv0kJ1pzn1Cj1X00fZ6j+6nn3RmbEr6Xrit1l1jA4ivZC 7 | c73EpqAlgbuOAEPqgkPyh7l6nw5pQZw3dxNmI6ecDy0380sAst6RcrHSvWnWTIzW 8 | F2+nDOx5Y/MeIMnwwSA/3ibv6V2o3jO6bSajKYFSCg3FtJ5jjKlwx8PCSRq7qk/K 9 | FplUet83FHSKepFBAZN+pzvZ3Ekz2R+QfvS0n34Xd7koK0BhpE5fYWX432ymvPBd 10 | jOqettdDAgMBAAGjXTBbMB0GA1UdDgQWBBSFa+qBmgXQ9psD/fbpvsszuCy5CTAf 11 | BgNVHSMEGDAWgBSFa+qBmgXQ9psD/fbpvsszuCy5CTAMBgNVHRMEBTADAQH/MAsG 12 | A1UdDwQEAwIBpjANBgkqhkiG9w0BAQUFAAOCAQEAp4e01eSaYzyo6I63mMZYVY5b 13 | /3XFHyRpVrYIhIJtTeewxl894FsN2q7SpJjt22DexjFvBmWXmSNTqHlncdx1uREK 14 | WnnkkOyOdY6HDs/hJy9wbEndp3KpyecZMOxUxac109g+Jp3wzHUbMcwHJ7GzObzU 15 | 47D3crNvuSpkcOLZcNwcOUgQhConMjQ0/9rMNQfG9rYpQDVfTjiqU/bZGFNx9UcB 16 | oWIvZEQFGmRBefzNRv9/NKa14XvXYZCM+2HU5/f0tuRDsAqvkV7h32vKi7FbS1c0 17 | iWfbIeLBfdMu0uboGlaFdDK6atMl/0nQf7PWsRWmdPcTYyuglRZj3QNMA0acwg== 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /CMakeModules/FindLibEvent.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the LibEvent config processing library 2 | # Once done this will define 3 | # 4 | # LIBEVENT_FOUND - System has LibEvent 5 | # LIBEVENT_INCLUDE_DIR - the LibEvent include directory 6 | # LIBEVENT_LIBRARIES 0 The libraries needed to use LibEvent 7 | 8 | FIND_PATH(LIBEVENT_INCLUDE_DIR NAMES event.h) 9 | FIND_LIBRARY(LIBEVENT_LIBRARY NAMES event) 10 | FIND_LIBRARY(LIBEVENT_CORE_LIBRARY NAMES event_core) 11 | FIND_LIBRARY(LIBEVENT_PTHREADS_LIBRARY NAMES event_pthreads) 12 | FIND_LIBRARY(LIBEVENT_EXTRA_LIBRARY NAMES event_extra) 13 | FIND_LIBRARY(LIBEVENT_OPENSSL_LIBRARY NAMES event_openssl) 14 | 15 | 16 | INCLUDE(FindPackageHandleStandardArgs) 17 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibEvent DEFAULT_MSG LIBEVENT_LIBRARY LIBEVENT_INCLUDE_DIR) 18 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibEventPthreads DEFAULT_MSG LIBEVENT_PTHREADS_LIBRARY LIBEVENT_INCLUDE_DIR) 19 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibEventCore DEFAULT_MSG LIBEVENT_CORE_LIBRARY LIBEVENT_INCLUDE_DIR) 20 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibEventExtra DEFAULT_MSG LIBEVENT_EXTRA_LIBRARY LIBEVENT_INCLUDE_DIR) 21 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibEventOpenssl DEFAULT_MSG LIBEVENT_OPENSSL_LIBRARY LIBEVENT_INCLUDE_DIR) 22 | 23 | MARK_AS_ADVANCED(LIBEVENT_INCLUDE_DIR LIBEVENT_LIBRARY LIBEVENT_PTHREADS_LIBRARY LIBEVENT_OPENSSL_LIBRARY LIBEVENT_CORE_LIBRARY LIBEVENT_EXTRA_LIBRARY) 24 | -------------------------------------------------------------------------------- /src/request.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2012] [Mandiant, inc] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "rproxy.h" 24 | 25 | void 26 | request_free(request_t * request) { 27 | if (request == NULL) { 28 | return; 29 | } 30 | 31 | if (request->parser) { 32 | free(request->parser); 33 | } 34 | 35 | if (request->pending_ev) { 36 | event_free(request->pending_ev); 37 | } 38 | 39 | free(request); 40 | } 41 | 42 | request_t * 43 | request_new(rproxy_t * rproxy) { 44 | request_t * request; 45 | 46 | if (rproxy == NULL) { 47 | return NULL; 48 | } 49 | 50 | if (!(request = calloc(sizeof(request_t), 1))) { 51 | return NULL; 52 | } 53 | 54 | request->rproxy = rproxy; 55 | request->parser = htparser_new(); 56 | 57 | htparser_init(request->parser, htp_type_response); 58 | htparser_set_userdata(request->parser, request); 59 | 60 | return request; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /test/unit/regress_main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2012] [Mandiant, inc] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "rproxy.h" 26 | #include "regress.h" 27 | #include "tinytest.h" 28 | #include "tinytest_macros.h" 29 | 30 | #if 0 31 | static void 32 | test_foo(void * ptr) { 33 | int bar = 50; 34 | 35 | tt_assert(bar == 50); 36 | end: 37 | return; 38 | } 39 | 40 | static void 41 | test_fail(void * ptr) { 42 | int bar = 50; 43 | 44 | tt_assert(bar == 1); 45 | end: 46 | return; 47 | } 48 | 49 | struct testcase_t test_testcases[] = { 50 | { "foo", test_foo, 0, NULL, NULL }, 51 | { "fail", test_fail, 0, NULL, NULL }, 52 | END_OF_TESTCASES 53 | }; 54 | #endif 55 | 56 | struct testgroup_t testgroups[] = { 57 | { "cfg/", cfg_testcases }, 58 | END_OF_GROUPS 59 | }; 60 | 61 | int 62 | main(int argc, char ** argv) { 63 | if (tinytest_main(argc, (const char **)argv, testgroups)) { 64 | return 1; 65 | } 66 | 67 | return 0; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /test/smoke/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAo86j5WHZCNpl3mA/j1IZDpP3BMZEJibwYBnIybWgd+fICjiH 3 | LrPlXJP6881al1Aiii0s/695C6R0xlKZ3GDACnywCJAxJdW8qZNiCzK8d0iJflSd 4 | ib2JCExvTO6JUVBxvY2ue/7sPN00tU6aq9xCBUeKsIu4/ay2omxzLhSH5apiRO7B 5 | GDFCxe22cJOk466S6UzXHo8DIUW7za9b2EOL8qPrT0heaNHCGWxfdMGAgTV9pltr 6 | YIVsaVBg/3ZSeP2i58rOPYmRiu9I375dmZjlY5iXg5pUqzzxsu3Y5yJpXqZsSjnQ 7 | OUQExMOL6wtveWqrl8y7+z3F3QSp1X2NAaPR6QIDAQABAoIBAHWzGszM+9eCpcOd 8 | QM5IXTy+ULWbiIgpXnqOzFkzKjQ91NyTNX73QAy/P4hCu58Rs1+5iiXF/aHB7y45 9 | V9ijpBlk8k+U29ulskPeZIpLUGQdOL4PKos4JuhRk3+hvgUxWiirTv5Fc5VgFGla 10 | K4TlZILKnvqvyqJroWcfqbsogWUhW97ov2sIsYyEEYxdiUExZ6MetXti+0Ef/J1U 11 | SP3h4g+FcKi0LgIYG1G1yrNDoOxu8zfFbH0pDqUluWjlB/FjpJ1rgmO/GN7ziMjB 12 | +9j/JycSKTWlwPG3rCRdJZ//vWmutjZPrjk8BO4ee8LIQ4iBfMfv6KoBlrxkGT/q 13 | omdeqskCgYEAz63n3NSRKDNG1f0bNkEFOtK1HmVpqPz0ZAGp5lPQEY+w13c1iJZJ 14 | 7GPYqGzmKkyhNmw4k2jIRz9EY+gVBR6Fxuay0wnspzsaeGaR95rWM5ZccyfY8A9M 15 | DjRLphj2DeXYKU9lqZk6uocsOUBHnHduNu3DICpDBxpFEagm+u9p6bsCgYEAyeuP 16 | PuEVqw4Mn8FsKKo8eywCm9g0+9SlYVYL7TcQi/tgwuUxRnPDPzhLa2TOeSOsNg4W 17 | QGMLgahzs4MB6+Mpyu2erKvj/46ZBrocyuwIOaq9QUUto6ybCa4CnlTCnJebj/bq 18 | Q2O8BCHNrTsGax0vMStEkY2IvWbY1Qg1TE+x9qsCgYAwBxO76hy3o0QacLI+VcMT 19 | 226vXNyWjAkop4nXDkLsDMLk0Vi8AaWO2XhdWvHPjnEp2NUsoerPLz5trFpQGMLv 20 | QKLSU1U7R1vTxES1ckVfIMK4jtqIicEY99cWzmb4x9K5mvbzxc/jGyeLBeaJtMT0 21 | Rv/9DEj794NJlg2NsphXyQKBgA1kpYS7M0+8R9QU5VKxL//PhIDHz4Pgr/OL3JCo 22 | dV2DmAB1aVZN+WZpGn+OGS/p0NNpDcrJhwrbnIkMqIT39V6zcjHr2NyrnbTXhW9k 23 | Rotkqtb4erV/jm0O8LeF0b7HQ5dB/nnn1KHFiDn0T1GthuLRBuRMqgQ+HSXMPJTK 24 | OHivAoGACTCm7b3OCm1duEKac2suoWPgxImyvOQSBzklKTGxAR/FekqFidvQHAdu 25 | AqKEmC6RaW4C6IT5NBngIUgfzKoMpvjh/zF+Y5iTSbi/T4Vn0wk8tyqQ+XvTczdV 26 | 7U9zghBRu+1ofFP/XPf0aqakuDNpS9zxoWzvHsiXfCat2GIbKOg= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/smoke/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEAwmqSAvbjnLg5eijzCvc2im7dAqicyNoTxYqwC2dMGKqw/Hj5 3 | P0Ph7syAJYQ7KhAKHdQz6lVH2JBVmA+ViXLJnS/7GxfFQnGMRjZnKyRHpaGaakKZ 4 | 1DAuy6uG9FUJF3Z3EMDdl9FNXdBcWfNE5rs9ocxODcFPbZ/c/Hg4rTgcWakRsOvS 5 | vjT6/xfuh3cTxmT7v9pHOXytKVJl0EpwEzmr1aRy11VWWT1nUVTGHnb7sbegM1fI 6 | Kc5F5ES0nlF47/ZHUDoofaQNCYZVUQo141KruJYupGlh9AZODgdY0QwiLxfHMthS 7 | X3x8CoOILptZRssGQ1ctv6XGdoXeWJUNPWkoEwIDAQABAoIBADJ4IeFdICdN97XL 8 | rZQ6KtFzCDKpfNHzMtDEOQEs1qdMoFdaPDH9OEiak+WpVwaLVWT68mMxsk344g9w 9 | dDxTLR1+GCynVhDykxyjANNFPsgzlHdjRLW9sFg09fq1nH+XxD74Ik5IClKBRYgg 10 | S9Gt58j51YVimnD14BM2TlhBSbLIadQEOe95iBzV+AFDmnNz9YaMxQphL7IjpeOb 11 | YmWjiXgzWINxn6xDMGzmxKIgDYD7QxXHQU7mJQTP9J612QfKTp7TuadswcC0Gohc 12 | 7aiM5ST/SQEn90V9kbHltloFnTNZOfa7X53QW+NVy8D7mQnsjd+iDxL1E7bLUmoI 13 | VXWbicECgYEA8jAEVGp/3FUUNqjTwzdyTPq8W82h1w9DfRkJzGtzXQkFss0fcAE4 14 | 1tA3P3vW5L/l3dwr82iMvGf8FznDNCfhR+OsXwZkoqdYfdxNSKuv19EebEC5Vmry 15 | 4fRFU4r1rd7ba2pY/YfCYp0b8zJ4WhJ6wNVqblxHuDcIOg/Wq3YDVPMCgYEAzYEV 16 | gkYc3LijqJfYobFTLnQwHjQpSrqIXt0P8jO3NrZl5AGSQioM4+zhnVAruCGF3AaS 17 | mlqch4esw2RtpmYiSxwqd6qVPicLlyMCUxSY+fIGSZkTdMyzbsHI/7H6PL6Gs97t 18 | iGB+DMrO3Yvg4O+0NBeRuxWZwNi3kh3grrOgKGECgYAlN3nC2ybQ3gYgLCt2iVO4 19 | PqOpW90t5juRwsI9tplgx50nj9vv41Avuoly6P3p4w9Y25epbPyLi1My4n57ylEV 20 | MghwBdR7Jkoz9SGvNCrueEU7j2qru/2c+WOZ5fXBBUkkpZANTcpySwBbi/rwpHxW 21 | m7UiRm4DAfnyqjJr4NO8PwKBgQCidrFta0Z4DJR9vYXq1ohfSkWcKzqVtIAJFxT7 22 | Mn1eiLZmmgNpwVqtemEm//GAQShVOPD/jDEBSHaBhkzVZxsnbB36uuX/29hi5K96 23 | xObneroTxyvFg+s63YKs+qRND4bofo4k6bvdGxyNs37V9Uruf8+bKsmB1xLRNkOh 24 | eapdIQKBgQDlTi+3+J3nJgGus3WyUmMKX16kejNQGcD8jaSvKUMuQPxYMMj52Kpr 25 | 2kJEvJOlaI9+V9JDga63zUZchAb7xLmOLryI4GDeK30aPiEomJ9vqJcjs9Po3r48 26 | ka7ISqIH0IzJThNibuG2YbIOUCGNwDytEYZsGBW6L7WiDHy/tjRmig== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /src/lzq.h: -------------------------------------------------------------------------------- 1 | /* Copyright [2012] [Mandiant, inc] 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | * implied. 13 | * See the License for the specific language governing permissions 14 | * and 15 | * limitations under the License. 16 | */ 17 | #ifndef __LZQ_H__ 18 | #define __LZQ_H__ 19 | 20 | struct lztq_elem; 21 | struct lztq; 22 | 23 | typedef struct lztq_elem lztq_elem; 24 | typedef struct lztq lztq; 25 | 26 | typedef void (*lzq_freefn)(void *); 27 | typedef int (*lztq_iterfn)(lztq_elem * elem, void * arg); 28 | 29 | lztq * lztq_new(void); 30 | lztq * lztq_dup(lztq * tq); 31 | void lztq_free(lztq * tq); 32 | 33 | lztq_elem * lztq_elem_new(void * data, size_t len, lzq_freefn freefn); 34 | void lztq_elem_free(lztq_elem * elem); 35 | 36 | lztq_elem * lztq_append(lztq * head, void * data, size_t len, lzq_freefn freefn); 37 | lztq_elem * lztq_append_elem(lztq * head, lztq_elem * elem); 38 | lztq_elem * lztq_prepend(lztq * head, void * data, size_t len, lzq_freefn freefn); 39 | lztq_elem * lztq_prepend_elem(lztq * head, lztq_elem * elem); 40 | 41 | lztq_elem * lztq_first(lztq * head); 42 | lztq_elem * lztq_last(lztq * head); 43 | lztq_elem * lztq_next(lztq_elem * elem); 44 | lztq_elem * lztq_prev(lztq_elem * elem); 45 | 46 | int lztq_elem_remove(lztq_elem * elem); 47 | int lztq_for_each(lztq * head, lztq_iterfn iterfn, void * arg); 48 | 49 | void * lztq_elem_data(lztq_elem * elem); 50 | lztq * lztq_elem_head(lztq_elem * elem); 51 | size_t lztq_elem_size(lztq_elem * elem); 52 | size_t lztq_size(lztq * head); 53 | 54 | #endif 55 | 56 | -------------------------------------------------------------------------------- /libs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.4) 2 | 3 | include (ExternalProject) 4 | 5 | ExternalProject_Add ( 6 | libConfuse 7 | URL http://savannah.nongnu.org/download/confuse/confuse-2.7.tar.gz 8 | BUILD_IN_SOURCE 1 9 | UPDATE_COMMAND "" 10 | CONFIGURE_COMMAND ./configure --enable-static --disable-shared --prefix=${RPROXY_DEPS_INSTALL} 11 | BUILD_COMMAND make 12 | INSTALL_COMMAND make install 13 | ) 14 | 15 | set (SSL_DSWITCH "") 16 | 17 | if (NOT APPLE) 18 | if (${CMAKE_BUILD_TYPE} matches "Debug") 19 | set (SSL_DSWITCH "-d") 20 | endif() 21 | 22 | ExternalProject_Add ( 23 | openssl 24 | URL http://openssl.org/source/openssl-1.0.0i.tar.gz 25 | UPDATE_COMMAND "" 26 | CONFIGURE_COMMAND ./config --prefix=${RPROXY_DEPS_INSTALL} no-shared no-zlib ${SSL_DSWITCH} 27 | BUILD_IN_SOURCE 1 28 | BUILD_COMMAND make 29 | INSTALL_COMMAND make install_sw 30 | ) 31 | else() 32 | if (${CMAKE_BUILD_TYPE} MATCHES "Debug") 33 | set (SSL_DSWITCH "-g3 -O0") 34 | endif() 35 | 36 | ExternalProject_Add( 37 | openssl 38 | URL http://openssl.org/source/openssl-1.0.0i.tar.gz 39 | UPDATE_COMMAND "" 40 | CONFIGURE_COMMAND ./Configure darwin64-x86_64-cc --prefix=${RPROXY_DEPS_INSTALL} no-shared no-zlib ${SSL_DSWITCH} 41 | BUILD_IN_SOURCE 1 42 | BUILD_COMMAND make -j1 43 | INSTALL_COMMAND make install_sw 44 | ) 45 | endif() 46 | 47 | ExternalProject_Add( 48 | libevent 49 | DEPENDS openssl 50 | GIT_REPOSITORY git://github.com/libevent/libevent.git 51 | GIT_TAG release-2.0.19-stable 52 | UPDATE_COMMAND "" 53 | PATCH_COMMAND ./autogen.sh 54 | CONFIGURE_COMMAND ./configure --disable-shared --prefix=${RPROXY_DEPS_INSTALL} --disable-debug-mode CFLAGS=-I${RPROXY_DEPS_INSTALL}/include LDFLAGS=-L${RPROXY_DEPS_INSTALL}/lib OPENSSL_LIBADD=-ldl 55 | BUILD_IN_SOURCE 1 56 | BUILD_COMMAND make 57 | INSTALL_COMMAND make install 58 | ) 59 | 60 | ExternalProject_Add( 61 | libevhtp 62 | DEPENDS libevent 63 | GIT_REPOSITORY git://github.com/ellzey/libevhtp.git 64 | GIT_TAG develop 65 | UPDATE_COMMAND "" 66 | CMAKE_ARGS -DCMAKE_FIND_ROOT_PATH:PATH=${RPROXY_DEPS_INSTALL} -DCMAKE_PREFIX_PATH:PATH=${RPROXY_DEPS_INSTALL} -DCMAKE_INSTALL_PREFIX:PATH=${RPROXY_DEPS_INSTALL} -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} 67 | ) 68 | 69 | -------------------------------------------------------------------------------- /test/unit/regress_cfg.cfg: -------------------------------------------------------------------------------- 1 | daemonize = false 2 | user = mthomas 3 | group = mthomas 4 | rootdir = /tmp/ 5 | 6 | server { 7 | host = ieatfood.net 8 | addr = 127.0.0.1 9 | port = 8081 10 | backlog = 1024 11 | threads = 1 12 | 13 | read-timeout = 10 14 | write-timeout = 10 15 | pending-timeout = 5 16 | 17 | logging { 18 | enabled = true 19 | type = file 20 | filename = "./rproxy.log" 21 | errorlog = "./rproxy_error.log" 22 | format = '{SRC} {PROXY} [{TS}] "{METH} {URI} {PROTO}" - {STATUS} - "{REF}" - "{UA}" - "{HOST}"' 23 | } 24 | 25 | headers { 26 | x-forwarded-for = true 27 | x-ssl-subject = true 28 | x-ssl-issuer = true 29 | x-ssl-notbefore = false 30 | x-ssl-notafter = true 31 | x-ssl-serial = true 32 | x-ssl-cipher = true 33 | x-ssl-certificate = true 34 | } 35 | 36 | ssl { 37 | enabled = true 38 | protocols-on = { TLSv1, SSLv3 } 39 | protocols-off = { SSLv2 } 40 | 41 | cert = ./server.crt 42 | key = ./server.key 43 | ca = "" 44 | capath = "" 45 | ciphers = "RC4+RSA:HIGH:+MEDIUM:+LOW" 46 | verify-peer = true 47 | verify-depth = 42 48 | cache-enabled = false 49 | cache-timeout = 1024 50 | } 51 | 52 | rewrite { 53 | src = "^(/dir/).*" 54 | dst = "/" 55 | } 56 | 57 | rewrite { 58 | src = "^(/a/b/).*" 59 | dst = "/test/" 60 | } 61 | 62 | downstream { 63 | addr = "127.0.0.1" 64 | port = 8080 65 | connections = 1 66 | 67 | retry { 68 | seconds = 1 69 | useconds = 0 70 | } 71 | } 72 | } 73 | 74 | server { 75 | host = "google.com" 76 | addr = "0.0.0.0" 77 | port = 8082 78 | threads = 4 79 | 80 | rewrite { 81 | src = "^(/google/).*" 82 | dst = "/" 83 | } 84 | 85 | downstream { 86 | addr = "74.125.113.99" 87 | port = 80 88 | connections = 1 89 | 90 | retry { 91 | seconds = 5 92 | useconds = 0 93 | } 94 | } 95 | 96 | downstream { 97 | addr = "74.125.113.105" 98 | port = 80 99 | connections = 1 100 | 101 | retry { 102 | seconds = 5 103 | useconds = 0 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/lzlog.h: -------------------------------------------------------------------------------- 1 | /* Copyright [2012] [Mandiant, inc] 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #ifndef __LZLOG_H__ 17 | #define __LZLOG_H__ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define LZLOG_OPT_NONE (1 << 0) 28 | #define LZLOG_OPT_WDATE (1 << 1) 29 | #define LZLOG_OPT_WLEVEL (1 << 2) 30 | #define LZLOG_OPT_WPID (1 << 3) 31 | #define LZLOG_OPT_WNAME (1 << 4) 32 | #define LZLOG_OPT_NEWLINE (1 << 5) 33 | #define LZLOG_OPT_EMU_SYSLZLOG (LZLOG_OPT_WDATE | LZLOG_OPT_WNAME | LZLOG_OPT_WPID | LZLOG_OPT_NEWLINE) 34 | 35 | struct lzlog; 36 | struct lzlog_vtbl; 37 | 38 | enum lzlog_type { 39 | lzlog_file = 0, 40 | lzlog_syslog, 41 | lzlog_asl 42 | }; 43 | 44 | enum lzlog_level { 45 | lzlog_emerg = 0, 46 | lzlog_alert, 47 | lzlog_crit, 48 | lzlog_err, 49 | lzlog_warn, 50 | lzlog_notice, 51 | lzlog_info, 52 | lzlog_debug, 53 | lzlog_max 54 | }; 55 | 56 | typedef enum lzlog_level lzlog_level; 57 | typedef enum lzlog_type lzlog_type; 58 | typedef struct log lzlog; 59 | 60 | void lzlog_write(lzlog * log, lzlog_level level, const char * fmt, ...); 61 | void lzlog_free(lzlog * log); 62 | 63 | lzlog * lzlog_file_new(const char * file, const char * ident, int opts); 64 | lzlog * lzlog_syslog_new(const char * ident, int opts, int facility); 65 | lzlog * lzlog_from_template(const char * tmplate, const char * ident, int opts); 66 | #ifdef __APPLE__ 67 | lzlog * lzlog_asl_new(const char * ident, int opts, int facility); 68 | #endif 69 | 70 | void lzlog_set_level(lzlog * log, lzlog_level level); 71 | int lzlog_facilitystr_to_facility(const char * str); 72 | lzlog_level lzlog_levelstr_to_level(const char * str); 73 | lzlog_type lzlog_get_type(lzlog * log); 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(RProxy) 4 | 5 | set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules) 6 | 7 | # Retr and build all dependencies for RProxy: -DRPROXY_BUILD_DEPS:STRING=ON 8 | option (RPROXY_BUILD_DEPS "Build external dependencies" OFF) 9 | 10 | set(RPROXY_DEPS_INSTALL ${PROJECT_BINARY_DIR}/install/) 11 | 12 | if (RPROXY_BUILD_DEPS) 13 | add_subdirectory(libs) 14 | 15 | 16 | set(LIBEVHTP_LIBRARY evhtp) 17 | set(LIBEVENT_LIBRARY event) 18 | set(LIBEVENT_CORE_LIBRARY event_core) 19 | set(LIBEVENT_PTHREADS_LIBRARY event_pthreads) 20 | set(LIBEVENT_EXTRA_LIBRARY event_extra) 21 | set(LIBEVENT_OPENSSL_LIBRARY event_openssl) 22 | set(LIBEVHTP_LIBRARY evhtp) 23 | set(LIBCONFUSE_LIBRARY confuse) 24 | set(OPENSSL_LIBRARIES ssl crypto) 25 | 26 | link_directories(${RPROXY_DEPS_INSTALL}/lib/) 27 | include_directories(${RPROXY_DEPS_INSTALL}/include/) 28 | 29 | else() 30 | find_package(LibEvent REQUIRED) 31 | find_package(libevhtp REQUIRED) 32 | find_package(LibConfuse REQUIRED) 33 | find_package(OpenSSL REQUIRED) 34 | 35 | include_directories( 36 | ${LIBEVHTP_INCLUDE_DIR} 37 | ${OPENSSL_INCLUDE_DIR}/openssl 38 | ${LIBCONFUSE_INCLUDE_DIR} 39 | ) 40 | endif() 41 | 42 | set (RPROXY_EXTERNAL_LIBS 43 | ${LIBEVHTP_LIBRARY} 44 | ${LIBEVENT_LIBRARY} 45 | ${LIBEVENT_CORE_LIBRARY} 46 | ${LIBEVENT_PTHREADS_LIBRARY} 47 | ${LIBEVENT_OPENSSL_LIBRARY} 48 | ${LIBCONFUSE_LIBRARY} 49 | ${OPENSSL_LIBRARIES} 50 | ) 51 | 52 | # local system libraries 53 | find_library (LIB_PTHREAD pthread) 54 | find_library (LIB_DL dl) 55 | 56 | set (SYS_LIBS 57 | ${LIB_PTHREAD} 58 | ${LIB_DL} 59 | ) 60 | 61 | if (APPLE) 62 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations") 63 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__APPLE__") 64 | endif(APPLE) 65 | 66 | if (NOT APPLE) 67 | find_library (LIB_RT rt) 68 | set (SYS_LIBS ${SYS_LIBS} ${LIB_RT}) 69 | endif(NOT APPLE) 70 | 71 | INCLUDE (CheckFunctionExists) 72 | INCLUDE (CheckIncludeFiles) 73 | INCLUDE (CheckTypeSize) 74 | 75 | CHECK_FUNCTION_EXISTS(daemon HAVE_BUILTIN_DAEMON) 76 | CHECK_FUNCTION_EXISTS(mallopt HAVE_MALLOPT) 77 | CHECK_FUNCTION_EXISTS(strndup HAVE_STRNDUP) 78 | CHECK_FUNCTION_EXISTS(strnlen HAVE_STRNLEN) 79 | CHECK_FUNCTION_EXISTS(getrlimit HAVE_GETRLIMIT) 80 | CHECK_FUNCTION_EXISTS(setrlimit HAVE_SETRLIMIT) 81 | 82 | if (HAVE_BUILTIN_DAEMON) 83 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_BUILTIN_DAEMON") 84 | endif(HAVE_BUILTIN_DAEMON) 85 | 86 | if (HAVE_MALLOPT) 87 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSE_MALLOPT") 88 | endif(HAVE_MALLOPT) 89 | 90 | if (NOT HAVE_STRNDUP) 91 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_STRNDUP") 92 | endif(NOT HAVE_STRNDUP) 93 | 94 | if (NOT HAVE_STRNLEN) 95 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_STRNLEN") 96 | endif(NOT HAVE_STRNLEN) 97 | 98 | if (NOT HAVE_GETRLIMIT OR NOT HAVE_SETRLIMIT) 99 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_RLIMITS") 100 | endif(NOT HAVE_GETRLIMIT OR NOT HAVE_SETRLIMIT) 101 | 102 | if (NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") 103 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNDEBUG") 104 | endif (NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") 105 | 106 | add_subdirectory(src) 107 | add_subdirectory(test) 108 | 109 | message("BUILD TYPE: ${CMAKE_BUILD_TYPE}") 110 | message("BUILD FLAGS: ${CMAKE_C_FLAGS}") 111 | -------------------------------------------------------------------------------- /test/smoke/server.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 1 (0x1) 5 | Signature Algorithm: sha256WithRSAEncryption 6 | Issuer: C=US, ST=DC, L=Washington, O=Company, OU=Architecture, CN=CA 7 | Validity 8 | Not Before: Feb 4 16:10:35 2012 GMT 9 | Not After : Feb 3 16:10:35 2013 GMT 10 | Subject: C=US, ST=DC, L=Washington, O=Company, OU=Architecture, CN=Server 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | RSA Public Key: (2048 bit) 14 | Modulus (2048 bit): 15 | 00:c2:6a:92:02:f6:e3:9c:b8:39:7a:28:f3:0a:f7: 16 | 36:8a:6e:dd:02:a8:9c:c8:da:13:c5:8a:b0:0b:67: 17 | 4c:18:aa:b0:fc:78:f9:3f:43:e1:ee:cc:80:25:84: 18 | 3b:2a:10:0a:1d:d4:33:ea:55:47:d8:90:55:98:0f: 19 | 95:89:72:c9:9d:2f:fb:1b:17:c5:42:71:8c:46:36: 20 | 67:2b:24:47:a5:a1:9a:6a:42:99:d4:30:2e:cb:ab: 21 | 86:f4:55:09:17:76:77:10:c0:dd:97:d1:4d:5d:d0: 22 | 5c:59:f3:44:e6:bb:3d:a1:cc:4e:0d:c1:4f:6d:9f: 23 | dc:fc:78:38:ad:38:1c:59:a9:11:b0:eb:d2:be:34: 24 | fa:ff:17:ee:87:77:13:c6:64:fb:bf:da:47:39:7c: 25 | ad:29:52:65:d0:4a:70:13:39:ab:d5:a4:72:d7:55: 26 | 56:59:3d:67:51:54:c6:1e:76:fb:b1:b7:a0:33:57: 27 | c8:29:ce:45:e4:44:b4:9e:51:78:ef:f6:47:50:3a: 28 | 28:7d:a4:0d:09:86:55:51:0a:35:e3:52:ab:b8:96: 29 | 2e:a4:69:61:f4:06:4e:0e:07:58:d1:0c:22:2f:17: 30 | c7:32:d8:52:5f:7c:7c:0a:83:88:2e:9b:59:46:cb: 31 | 06:43:57:2d:bf:a5:c6:76:85:de:58:95:0d:3d:69: 32 | 28:13 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | X509v3 Key Usage: 36 | Non Repudiation, Key Encipherment 37 | Signature Algorithm: sha256WithRSAEncryption 38 | 73:11:5b:8b:5b:b6:97:20:68:f2:84:a0:db:83:a0:38:58:92: 39 | d9:39:e3:2d:43:75:c5:a9:58:fc:e1:9f:19:07:58:3b:51:b7: 40 | 92:88:10:f8:a8:d6:9d:e5:ba:13:1d:85:a4:52:e8:4e:ea:44: 41 | 6a:a0:b9:97:0a:e7:4f:c3:c8:41:43:2e:b0:10:93:d0:3c:88: 42 | 6e:17:62:6f:33:3e:e7:d1:8d:54:25:2d:49:65:af:2e:fb:9c: 43 | db:a1:7f:94:a3:3f:d2:ab:ad:1c:3b:49:fe:2c:5c:33:6a:dc: 44 | 97:b8:76:a6:03:b5:38:66:e7:39:84:3f:52:af:f2:41:e9:82: 45 | 17:8d:80:6b:99:82:68:4e:1f:13:d0:28:3f:13:9e:cd:72:3e: 46 | e8:40:da:ba:c7:88:bd:47:6b:70:a6:e6:f2:06:d0:47:b1:e0: 47 | 2b:07:0b:2e:ae:0d:f2:4b:53:24:15:7b:6a:0d:3f:e0:80:58: 48 | cf:a8:a0:df:3d:14:52:d6:f3:27:7b:28:e0:9d:43:75:46:52: 49 | db:58:ac:0f:17:ba:0f:52:7d:65:bf:6a:9c:43:27:31:98:ee: 50 | 95:4c:b9:e6:fb:07:7c:b3:6e:9d:8f:0a:7f:91:bc:c0:f1:07: 51 | 6d:29:ba:d5:c8:80:84:31:3a:8f:48:f0:7d:28:d4:60:80:04: 52 | f4:2e:3e:4f 53 | -----BEGIN CERTIFICATE----- 54 | MIIDWDCCAkCgAwIBAgIBATANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEL 55 | MAkGA1UECAwCREMxEzARBgNVBAcMCldhc2hpbmd0b24xEDAOBgNVBAoMB0NvbXBh 56 | bnkxFTATBgNVBAsMDEFyY2hpdGVjdHVyZTELMAkGA1UEAwwCQ0EwHhcNMTIwMjA0 57 | MTYxMDM1WhcNMTMwMjAzMTYxMDM1WjBpMQswCQYDVQQGEwJVUzELMAkGA1UECAwC 58 | REMxEzARBgNVBAcMCldhc2hpbmd0b24xEDAOBgNVBAoMB0NvbXBhbnkxFTATBgNV 59 | BAsMDEFyY2hpdGVjdHVyZTEPMA0GA1UEAwwGU2VydmVyMIIBIjANBgkqhkiG9w0B 60 | AQEFAAOCAQ8AMIIBCgKCAQEAwmqSAvbjnLg5eijzCvc2im7dAqicyNoTxYqwC2dM 61 | GKqw/Hj5P0Ph7syAJYQ7KhAKHdQz6lVH2JBVmA+ViXLJnS/7GxfFQnGMRjZnKyRH 62 | paGaakKZ1DAuy6uG9FUJF3Z3EMDdl9FNXdBcWfNE5rs9ocxODcFPbZ/c/Hg4rTgc 63 | WakRsOvSvjT6/xfuh3cTxmT7v9pHOXytKVJl0EpwEzmr1aRy11VWWT1nUVTGHnb7 64 | sbegM1fIKc5F5ES0nlF47/ZHUDoofaQNCYZVUQo141KruJYupGlh9AZODgdY0Qwi 65 | LxfHMthSX3x8CoOILptZRssGQ1ctv6XGdoXeWJUNPWkoEwIDAQABow8wDTALBgNV 66 | HQ8EBAMCBWAwDQYJKoZIhvcNAQELBQADggEBAHMRW4tbtpcgaPKEoNuDoDhYktk5 67 | 4y1DdcWpWPzhnxkHWDtRt5KIEPio1p3luhMdhaRS6E7qRGqguZcK50/DyEFDLrAQ 68 | k9A8iG4XYm8zPufRjVQlLUllry77nNuhf5SjP9KrrRw7Sf4sXDNq3Je4dqYDtThm 69 | 5zmEP1Kv8kHpgheNgGuZgmhOHxPQKD8Tns1yPuhA2rrHiL1Ha3Cm5vIG0Eex4CsH 70 | Cy6uDfJLUyQVe2oNP+CAWM+ooN89FFLW8yd7KOCdQ3VGUttYrA8Xug9SfWW/apxD 71 | JzGY7pVMueb7B3yzbp2PCn+RvMDxB20putXIgIQxOo9I8H0o1GCABPQuPk8= 72 | -----END CERTIFICATE----- 73 | -------------------------------------------------------------------------------- /test/unit/tinytest.h: -------------------------------------------------------------------------------- 1 | /* tinytest.h -- Copyright 2009-2011 Nick Mathewson 2 | * 3 | * Redistribution and use in source and binary forms, with or without 4 | * modification, are permitted provided that the following conditions 5 | * are met: 6 | * 1. Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * 2. Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 3. The name of the author may not be used to endorse or promote products 12 | * derived from this software without specific prior written permission. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef _TINYTEST_H 27 | #define _TINYTEST_H 28 | 29 | /** Flag for a test that needs to run in a subprocess. */ 30 | #define TT_FORK (1 << 0) 31 | /** Runtime flag for a test we've decided to skip. */ 32 | #define TT_SKIP (1 << 1) 33 | /** Internal runtime flag for a test we've decided to run. */ 34 | #define _TT_ENABLED (1 << 2) 35 | /** If you add your own flags, make them start at this point. */ 36 | #define TT_FIRST_USER_FLAG (1 << 3) 37 | 38 | typedef void (*testcase_fn)(void *); 39 | 40 | struct testcase_t; 41 | 42 | /** Functions to initialize/teardown a structure for a testcase. */ 43 | struct testcase_setup_t { 44 | /** Return a new structure for use by a given testcase. */ 45 | void *(* setup_fn)(const struct testcase_t *); 46 | /** Clean/free a structure from setup_fn. Return 1 if ok, 0 on err. */ 47 | int (* cleanup_fn)(const struct testcase_t *, void *); 48 | }; 49 | 50 | /** A single test-case that you can run. */ 51 | struct testcase_t { 52 | const char * name; /**< An identifier for this case. */ 53 | testcase_fn fn; /**< The function to run to implement this case. */ 54 | unsigned long flags; /**< Bitfield of TT_* flags. */ 55 | const struct testcase_setup_t * setup; /**< Optional setup/cleanup fns*/ 56 | void * setup_data; /**< Extra data usable by setup function */ 57 | }; 58 | #define END_OF_TESTCASES { NULL, NULL, 0, NULL, NULL } 59 | 60 | /** A group of tests that are selectable together. */ 61 | struct testgroup_t { 62 | const char * prefix; /**< Prefix to prepend to testnames. */ 63 | struct testcase_t * cases; /** Array, ending with END_OF_TESTCASES */ 64 | }; 65 | #define END_OF_GROUPS { NULL, NULL } 66 | 67 | /** Implementation: called from a test to indicate failure, before logging. */ 68 | void _tinytest_set_test_failed(void); 69 | /** Implementation: called from a test to indicate that we're skipping. */ 70 | void _tinytest_set_test_skipped(void); 71 | /** Implementation: return 0 for quiet, 1 for normal, 2 for loud. */ 72 | int _tinytest_get_verbosity(void); 73 | /** Implementation: Set a flag on tests matching a name; returns number 74 | * of tests that matched. */ 75 | int _tinytest_set_flag(struct testgroup_t *, const char *, unsigned long); 76 | 77 | /** Set all tests in 'groups' matching the name 'named' to be skipped. */ 78 | #define tinytest_skip(groups, named) \ 79 | _tinytest_set_flag(groups, named, TT_SKIP) 80 | 81 | /** Run a single testcase in a single group. */ 82 | int testcase_run_one(const struct testgroup_t *, const struct testcase_t *); 83 | /** Run a set of testcases from an END_OF_GROUPS-terminated array of groups, 84 | * as selected from the command line. */ 85 | int tinytest_main(int argc, const char ** argv, struct testgroup_t * groups); 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /sample.cfg: -------------------------------------------------------------------------------- 1 | daemonize = false 2 | 3 | #logging { 4 | # general { 5 | # output = "file:/dev/stdout" 6 | # level = error 7 | # } 8 | #} 9 | 10 | server { 11 | addr = 0.0.0.0 12 | port = 7070 13 | backlog = 1024 14 | threads = 1 15 | 16 | read-timeout = { 2, 0 } 17 | write-timeout = { 0, 0 } 18 | pending-timeout = { 10, 0 } 19 | max-pending = 200 20 | high-watermark = 5242880 21 | 22 | downstream ds_01 { 23 | addr = 127.0.0.1 24 | port = 8081 25 | connections = 10 26 | high-watermark = 0 27 | read-timeout = { 0, 0 } 28 | write-timeout = { 0, 0 } 29 | retry = { 1, 50000 } 30 | } 31 | 32 | downstream ds_02 { 33 | addr = 127.0.0.1 34 | port = 8082 35 | connections = 10 36 | retry = { 1, 50000 } 37 | } 38 | 39 | downstream ds_03 { 40 | addr = 127.0.0.1 41 | port = 8083 42 | connections = 10 43 | retry = { 1, 50000 } 44 | } 45 | 46 | downstream ds_04 { 47 | addr = 127.0.0.1 48 | port = 80 49 | connections = 10 50 | retry = { 1, 50000 } 51 | } 52 | 53 | downstream passthru_test { 54 | addr = 127.0.0.1 55 | port = 9095 56 | connections = 2 57 | retry = { 1, 50000 } 58 | } 59 | 60 | downstream default_downstream { 61 | enabled = true 62 | addr = 127.0.0.1 63 | port = 8084 64 | } 65 | 66 | ssl { 67 | enabled = false 68 | cert = ./server.crt 69 | key = ./server.key 70 | } 71 | 72 | logging { 73 | error { 74 | enabled = true 75 | level = error 76 | output = "file:/dev/stdout" 77 | } 78 | } 79 | 80 | vhost "localhost*" { 81 | aliases = { "172.21.*", 127.0.0.1, box.local } 82 | strip-headers = { "foobar" } 83 | 84 | ssl { 85 | enabled = true 86 | protocols-on = { TLSv1, SSLv3 } 87 | protocols-off = { SSLv3 } 88 | cert = ./server.crt 89 | key = ./server.key 90 | verify-peer = true 91 | verify-depth = 4 92 | cache-enabled = false 93 | } 94 | 95 | rule test_passthrough { 96 | uri-match = "/ownme" 97 | passthrough = true 98 | downstreams = { passthru_test } 99 | } 100 | 101 | rule test_redirect { 102 | uri-match = "/redir" 103 | allow-redirect = true 104 | downstreams = { ds_01 } 105 | } 106 | 107 | rule test_redirect_filter { 108 | uri-match = "/redir_filter" 109 | allow-redirect = true 110 | redirect-filter = { "localhost*", "*ieatfood.net*", "blerp*" } 111 | downstreams = { ds_01 } 112 | } 113 | 114 | rule derp { 115 | uri-gmatch = "*/file*" 116 | downstreams = { ds_04 } 117 | } 118 | 119 | rule test_1 { 120 | uri-match = "/ugh" 121 | downstreams = { ds_04 } 122 | 123 | # set rule-specific connection read/write timeout to infinity 124 | upstream-read-timeout = { 1, 0 } 125 | upstream-write-timeout = { 0, 0 } 126 | } 127 | 128 | rule test_2 { 129 | uri-match = "/poll" 130 | downstreams = { ds_01, ds_02 } 131 | lb-method = roundrobin 132 | upstream-read-timeout = { 10, 0 } 133 | 134 | headers { 135 | x-forwarded-for = true 136 | x-ssl-certificate = false 137 | } 138 | } 139 | 140 | rule test_rmatch { 141 | uri-rmatch = "^/things/(.*)" 142 | downstreams = { ds_03 } 143 | } 144 | 145 | rule test_gmatch { 146 | uri-gmatch = "/stuff/*" 147 | downstreams = { ds_01, ds_02, ds_03 } 148 | lb-method = roundrobin 149 | } 150 | 151 | rule default { 152 | uri-gmatch = "*" 153 | downstreams = { ds_01 } 154 | } 155 | 156 | logging { 157 | request { 158 | enabled = true 159 | output = "file:/dev/stdout" 160 | format = "{TS}: {RULE} {SRC} {HOST} {URI}" 161 | } 162 | 163 | error { 164 | enabled = true 165 | level = error 166 | output = "file:/dev/stderr" 167 | format = "{SRC} {HOST} {URI}" 168 | } 169 | } 170 | } 171 | 172 | vhost test.blah { 173 | aliases = { test.blah.local } 174 | 175 | ssl { 176 | enabled = true 177 | cert = ./server2.pem 178 | cache-enabled = false 179 | } 180 | 181 | rule default { 182 | uri-gmatch = "/*" 183 | downstreams = { ds_01 } 184 | } 185 | 186 | logging { 187 | error { 188 | enabled = true 189 | output = "file:/dev/stdout" 190 | format = "{SRC} {HOST} {URI}" 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /test/smoke/client.crt: -------------------------------------------------------------------------------- 1 | Certificate: 2 | Data: 3 | Version: 3 (0x2) 4 | Serial Number: 1 (0x1) 5 | Signature Algorithm: sha256WithRSAEncryption 6 | Issuer: CN=CA 7 | Validity 8 | Not Before: Feb 4 16:52:42 2012 GMT 9 | Not After : Feb 1 16:52:42 2022 GMT 10 | Subject: CN=Client 11 | Subject Public Key Info: 12 | Public Key Algorithm: rsaEncryption 13 | RSA Public Key: (2048 bit) 14 | Modulus (2048 bit): 15 | 00:a3:ce:a3:e5:61:d9:08:da:65:de:60:3f:8f:52: 16 | 19:0e:93:f7:04:c6:44:26:26:f0:60:19:c8:c9:b5: 17 | a0:77:e7:c8:0a:38:87:2e:b3:e5:5c:93:fa:f3:cd: 18 | 5a:97:50:22:8a:2d:2c:ff:af:79:0b:a4:74:c6:52: 19 | 99:dc:60:c0:0a:7c:b0:08:90:31:25:d5:bc:a9:93: 20 | 62:0b:32:bc:77:48:89:7e:54:9d:89:bd:89:08:4c: 21 | 6f:4c:ee:89:51:50:71:bd:8d:ae:7b:fe:ec:3c:dd: 22 | 34:b5:4e:9a:ab:dc:42:05:47:8a:b0:8b:b8:fd:ac: 23 | b6:a2:6c:73:2e:14:87:e5:aa:62:44:ee:c1:18:31: 24 | 42:c5:ed:b6:70:93:a4:e3:ae:92:e9:4c:d7:1e:8f: 25 | 03:21:45:bb:cd:af:5b:d8:43:8b:f2:a3:eb:4f:48: 26 | 5e:68:d1:c2:19:6c:5f:74:c1:80:81:35:7d:a6:5b: 27 | 6b:60:85:6c:69:50:60:ff:76:52:78:fd:a2:e7:ca: 28 | ce:3d:89:91:8a:ef:48:df:be:5d:99:98:e5:63:98: 29 | 97:83:9a:54:ab:3c:f1:b2:ed:d8:e7:22:69:5e:a6: 30 | 6c:4a:39:d0:39:44:04:c4:c3:8b:eb:0b:6f:79:6a: 31 | ab:97:cc:bb:fb:3d:c5:dd:04:a9:d5:7d:8d:01:a3: 32 | d1:e9 33 | Exponent: 65537 (0x10001) 34 | X509v3 extensions: 35 | 1.2.3.4: 36 | . PUT_TEXT_HERE 37 | X509v3 Basic Constraints: 38 | CA:FALSE 39 | X509v3 Key Usage: 40 | Digital Signature, Non Repudiation, Key Encipherment 41 | Netscape Comment: 42 | OpenSSL Generated Certificate 43 | X509v3 Subject Key Identifier: 44 | EF:11:3B:C5:24:F0:98:82:6D:D3:E3:B8:22:09:91:0E:AB:CF:E2:1F 45 | X509v3 Authority Key Identifier: 46 | keyid:85:6B:EA:81:9A:05:D0:F6:9B:03:FD:F6:E9:BE:CB:33:B8:2C:B9:09 47 | 48 | Signature Algorithm: sha256WithRSAEncryption 49 | 6a:9b:69:70:1b:25:a0:81:9b:58:4c:eb:3d:09:7b:4b:f9:77: 50 | 50:a9:36:0e:38:80:a3:fb:2d:80:1f:c7:dd:1c:9c:70:3e:5f: 51 | 20:c1:9c:da:38:9d:63:c2:48:02:b9:11:39:24:5d:0b:2d:30: 52 | ca:ec:20:79:10:bc:34:bf:ff:78:38:4e:9a:7e:43:86:f4:ea: 53 | 32:3b:75:25:1a:26:b3:f2:51:42:bc:3e:ef:91:4b:88:4f:b3: 54 | b5:1c:6c:27:3f:d6:98:7e:20:81:4f:d5:88:20:32:6f:cb:09: 55 | 9c:ac:aa:1d:80:87:1c:ff:50:7a:8d:ce:d2:ce:d4:16:ea:64: 56 | 16:20:5d:29:61:9b:60:00:cf:72:50:04:72:ff:56:04:bc:04: 57 | a4:0a:e6:b1:86:54:70:fe:2b:04:ee:fa:09:14:64:83:9d:d2: 58 | 58:e9:71:42:76:8e:d4:b8:5b:1b:b2:e8:22:f6:1a:31:ee:95: 59 | 49:6a:d7:00:a7:b9:20:46:7e:a2:50:eb:e1:fa:e4:48:0d:10: 60 | 4d:e1:0d:52:85:29:39:9b:10:9d:45:92:b3:10:1d:4d:9f:18: 61 | c4:bd:60:b2:11:a5:93:f4:eb:2d:54:84:68:f3:c1:9d:6d:03: 62 | e4:7b:8b:27:5a:c4:51:b9:aa:48:70:10:de:d4:c8:0a:88:59: 63 | 07:d9:0c:b3 64 | -----BEGIN CERTIFICATE----- 65 | MIIDOzCCAiOgAwIBAgIBATANBgkqhkiG9w0BAQsFADANMQswCQYDVQQDDAJDQTAe 66 | Fw0xMjAyMDQxNjUyNDJaFw0yMjAyMDExNjUyNDJaMBExDzANBgNVBAMMBkNsaWVu 67 | dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKPOo+Vh2QjaZd5gP49S 68 | GQ6T9wTGRCYm8GAZyMm1oHfnyAo4hy6z5VyT+vPNWpdQIootLP+veQukdMZSmdxg 69 | wAp8sAiQMSXVvKmTYgsyvHdIiX5UnYm9iQhMb0zuiVFQcb2Nrnv+7DzdNLVOmqvc 70 | QgVHirCLuP2stqJscy4Uh+WqYkTuwRgxQsXttnCTpOOukulM1x6PAyFFu82vW9hD 71 | i/Kj609IXmjRwhlsX3TBgIE1faZba2CFbGlQYP92Unj9oufKzj2JkYrvSN++XZmY 72 | 5WOYl4OaVKs88bLt2OciaV6mbEo50DlEBMTDi+sLb3lqq5fMu/s9xd0EqdV9jQGj 73 | 0ekCAwEAAaOBoTCBnjAWBgMqAwQEDwwNUFVUX1RFWFRfSEVSRTAJBgNVHRMEAjAA 74 | MAsGA1UdDwQEAwIF4DAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQg 75 | Q2VydGlmaWNhdGUwHQYDVR0OBBYEFO8RO8Uk8JiCbdPjuCIJkQ6rz+IfMB8GA1Ud 76 | IwQYMBaAFIVr6oGaBdD2mwP99um+yzO4LLkJMA0GCSqGSIb3DQEBCwUAA4IBAQBq 77 | m2lwGyWggZtYTOs9CXtL+XdQqTYOOICj+y2AH8fdHJxwPl8gwZzaOJ1jwkgCuRE5 78 | JF0LLTDK7CB5ELw0v/94OE6afkOG9OoyO3UlGiaz8lFCvD7vkUuIT7O1HGwnP9aY 79 | fiCBT9WIIDJvywmcrKodgIcc/1B6jc7SztQW6mQWIF0pYZtgAM9yUARy/1YEvASk 80 | CuaxhlRw/isE7voJFGSDndJY6XFCdo7UuFsbsugi9hox7pVJatcAp7kgRn6iUOvh 81 | +uRIDRBN4Q1ShSk5mxCdRZKzEB1NnxjEvWCyEaWT9OstVIRo88GdbQPke4snWsRR 82 | uapIcBDe1MgKiFkH2Qyz 83 | -----END CERTIFICATE----- 84 | -------------------------------------------------------------------------------- /test/unit/regress_cfg.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "rproxy.h" 8 | #include "regress.h" 9 | 10 | static rproxy_cfg_t * rp_cfg = NULL; 11 | 12 | static void 13 | _cfg_parse_file(void * ptr) { 14 | rp_cfg = rproxy_cfg_parse_file("test/unit/regress_cfg.cfg"); 15 | 16 | tt_assert(rp_cfg != NULL); 17 | end: 18 | return; 19 | } 20 | 21 | static void 22 | _cfg_headers_check_validity(headers_cfg_t * cfg, 23 | bool x_forwarded_for, 24 | bool x_ssl_subject, 25 | bool x_ssl_issuer, 26 | bool x_ssl_notbefore, 27 | bool x_ssl_notafter, 28 | bool x_ssl_serial, 29 | bool x_ssl_cipher, 30 | bool x_ssl_certificate) { 31 | tt_assert(cfg->x_forwarded_for == x_forwarded_for); 32 | tt_assert(cfg->x_ssl_subject == x_ssl_subject); 33 | tt_assert(cfg->x_ssl_issuer == x_ssl_issuer); 34 | tt_assert(cfg->x_ssl_notbefore == x_ssl_notbefore); 35 | tt_assert(cfg->x_ssl_notafter == x_ssl_notafter); 36 | tt_assert(cfg->x_ssl_serial == x_ssl_serial); 37 | tt_assert(cfg->x_ssl_cipher == x_ssl_cipher); 38 | tt_assert(cfg->x_ssl_certificate == x_ssl_certificate); 39 | 40 | end: 41 | return; 42 | } 43 | 44 | static void 45 | _cfg_logger_check_validity(logger_cfg_t * cfg, logger_type type, 46 | const char * filename, 47 | const char * errorlog, 48 | const char * logfmt) { 49 | tt_assert(cfg->type == type); 50 | tt_assert(strcmp(cfg->filename, filename) == 0); 51 | tt_assert(strcmp(cfg->errorlog, errorlog) == 0); 52 | tt_assert(strcmp(cfg->format, logfmt) == 0); 53 | 54 | end: 55 | return; 56 | } 57 | 58 | static void 59 | _cfg_rewrite_check_validity(rewrite_cfg_t * cfg, const char * src, const char * dst) { 60 | tt_assert(strcmp(cfg->src, src) == 0); 61 | tt_assert(strcmp(cfg->dst, dst) == 0); 62 | 63 | end: 64 | return; 65 | } 66 | 67 | static void 68 | _cfg_server_check_validity(void * ptr) { 69 | server_cfg_t * serv_cfg; 70 | 71 | int num_servers = 0; 72 | 73 | tt_assert(rp_cfg != NULL); 74 | 75 | TAILQ_FOREACH(serv_cfg, &rp_cfg->servers, next) { 76 | num_servers++; 77 | } 78 | 79 | tt_assert(num_servers == 2); 80 | 81 | num_servers = 0; 82 | 83 | TAILQ_FOREACH(serv_cfg, &rp_cfg->servers, next) { 84 | rewrite_cfg_t * rw_cfg; 85 | int num_rewrites = 0; 86 | 87 | switch (num_servers++) { 88 | case 0: 89 | tt_assert(strcmp(serv_cfg->bind_addr, "127.0.0.1") == 0); 90 | tt_assert(serv_cfg->bind_port == 8081); 91 | tt_assert(serv_cfg->num_threads == 1); 92 | tt_assert(serv_cfg->read_timeout == 10); 93 | tt_assert(serv_cfg->write_timeout == 10); 94 | tt_assert(serv_cfg->pending_timeout == 5); 95 | tt_assert(serv_cfg->backlog == 1024); 96 | tt_assert(serv_cfg->ssl != NULL); 97 | tt_assert(serv_cfg->headers != NULL); 98 | 99 | _cfg_headers_check_validity(serv_cfg->headers, 100 | true, true, true, false, 101 | true, true, true, true); 102 | _cfg_logger_check_validity(serv_cfg->logger, 103 | logger_type_file, 104 | "./rproxy.log", 105 | "./rproxy_error.log", 106 | "{SRC} {PROXY} [{TS}] \"{METH} {URI} {PROTO}\" - {STATUS} - \"{REF}\" - \"{UA}\" - \"{HOST}\""); 107 | 108 | TAILQ_FOREACH(rw_cfg, &serv_cfg->rewrites, next) { 109 | num_rewrites++; 110 | } 111 | 112 | tt_assert(num_rewrites == 2); 113 | num_rewrites = 0; 114 | 115 | TAILQ_FOREACH(rw_cfg, &serv_cfg->rewrites, next) { 116 | switch (num_rewrites++) { 117 | case 0: 118 | _cfg_rewrite_check_validity(rw_cfg, "^(/dir/).*", "/"); 119 | break; 120 | case 1: 121 | _cfg_rewrite_check_validity(rw_cfg, "^(/a/b/).*", "/test/"); 122 | break; 123 | } 124 | } 125 | 126 | break; 127 | } /* switch */ 128 | } 129 | 130 | 131 | end: 132 | return; 133 | } /* _cfg_server_check_validity */ 134 | 135 | struct testcase_t cfg_testcases[] = { 136 | { "parse-file", _cfg_parse_file, 0, NULL, NULL }, 137 | { "server-check-validity", _cfg_server_check_validity, 0, NULL, NULL }, 138 | END_OF_TESTCASES 139 | }; 140 | 141 | -------------------------------------------------------------------------------- /src/lzq.c: -------------------------------------------------------------------------------- 1 | /* Copyright [2012] [Mandiant, inc] 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "lzq.h" 23 | 24 | struct lztq_elem { 25 | lztq * tq_head; 26 | void * data; 27 | size_t len; 28 | lzq_freefn free_fn; 29 | 30 | TAILQ_ENTRY(lztq_elem) next; 31 | }; 32 | 33 | TAILQ_HEAD(__lztqhd, lztq_elem); 34 | 35 | struct lztq { 36 | size_t n_elem; 37 | struct __lztqhd elems; 38 | }; 39 | 40 | static void 41 | _lzq_freefn(void * data) { 42 | if (data) { 43 | free(data); 44 | } 45 | } 46 | 47 | lztq * 48 | lztq_new(void) { 49 | lztq * tq; 50 | 51 | if (!(tq = malloc(sizeof(lztq)))) { 52 | return NULL; 53 | } 54 | 55 | TAILQ_INIT(&tq->elems); 56 | 57 | tq->n_elem = 0; 58 | 59 | return tq; 60 | } 61 | 62 | void 63 | lztq_free(lztq * tq) { 64 | lztq_elem * elem; 65 | lztq_elem * temp; 66 | 67 | if (!tq) { 68 | return; 69 | } 70 | 71 | for (elem = lztq_first(tq); elem != NULL; elem = temp) { 72 | temp = lztq_next(elem); 73 | 74 | lztq_elem_remove(elem); 75 | lztq_elem_free(elem); 76 | } 77 | 78 | free(tq); 79 | } 80 | 81 | static int 82 | lztq_dup_itercb(lztq_elem * elem, void * arg) { 83 | lztq * tq = arg; 84 | size_t len; 85 | void * data; 86 | 87 | len = lztq_elem_size(elem); 88 | data = lztq_elem_data(elem); 89 | 90 | if (len) { 91 | if (!(data = malloc(len))) { 92 | return -1; 93 | } 94 | 95 | memcpy(data, lztq_elem_data(elem), len); 96 | } 97 | 98 | if (!lztq_append(tq, data, len, elem->free_fn)) { 99 | return -1; 100 | } 101 | 102 | return 0; 103 | } 104 | 105 | lztq * 106 | lztq_dup(lztq * tq) { 107 | lztq * new_tq; 108 | 109 | if (tq == NULL) { 110 | return NULL; 111 | } 112 | 113 | if (!(new_tq = lztq_new())) { 114 | return NULL; 115 | } 116 | 117 | if (lztq_for_each(tq, lztq_dup_itercb, new_tq)) { 118 | lztq_free(new_tq); 119 | return NULL; 120 | } 121 | 122 | return new_tq; 123 | } 124 | 125 | lztq_elem * 126 | lztq_elem_new(void * data, size_t len, lzq_freefn freefn) { 127 | lztq_elem * elem; 128 | 129 | if (!(elem = malloc(sizeof(lztq_elem)))) { 130 | return NULL; 131 | } 132 | 133 | elem->data = data; 134 | elem->len = len; 135 | elem->tq_head = NULL; 136 | 137 | if (freefn) { 138 | elem->free_fn = freefn; 139 | } else { 140 | elem->free_fn = _lzq_freefn; 141 | } 142 | 143 | return elem; 144 | } 145 | 146 | void 147 | lztq_elem_free(lztq_elem * elem) { 148 | if (elem == NULL) { 149 | return; 150 | } 151 | 152 | if (elem->data) { 153 | (elem->free_fn)(elem->data); 154 | } 155 | 156 | free(elem); 157 | } 158 | 159 | lztq_elem * 160 | lztq_append(lztq * tq, void * data, size_t len, lzq_freefn freefn) { 161 | lztq_elem * elem; 162 | 163 | if (tq == NULL) { 164 | return NULL; 165 | } 166 | 167 | if (!(elem = lztq_elem_new(data, len, freefn))) { 168 | return NULL; 169 | } 170 | 171 | return lztq_append_elem(tq, elem); 172 | } 173 | 174 | lztq_elem * 175 | lztq_append_elem(lztq * tq, lztq_elem * elem) { 176 | if (!tq || !elem) { 177 | return NULL; 178 | } 179 | 180 | TAILQ_INSERT_TAIL(&tq->elems, elem, next); 181 | 182 | tq->n_elem += 1; 183 | elem->tq_head = tq; 184 | 185 | return elem; 186 | } 187 | 188 | lztq_elem * 189 | lztq_prepend(lztq * tq, void * data, size_t len, lzq_freefn freefn) { 190 | lztq_elem * elem; 191 | 192 | if (!tq) { 193 | return NULL; 194 | } 195 | 196 | if (!(elem = lztq_elem_new(data, len, freefn))) { 197 | return NULL; 198 | } 199 | 200 | return lztq_prepend_elem(tq, elem); 201 | } 202 | 203 | lztq_elem * 204 | lztq_prepend_elem(lztq * tq, lztq_elem * elem) { 205 | if (!tq || !elem) { 206 | return NULL; 207 | } 208 | 209 | TAILQ_INSERT_HEAD(&tq->elems, elem, next); 210 | 211 | tq->n_elem += 1; 212 | elem->tq_head = tq; 213 | 214 | return elem; 215 | } 216 | 217 | lztq_elem * 218 | lztq_first(lztq * tq) { 219 | if (!tq) { 220 | return NULL; 221 | } 222 | 223 | return TAILQ_FIRST(&tq->elems); 224 | } 225 | 226 | lztq_elem * 227 | lztq_last(lztq * tq) { 228 | if (!tq) { 229 | return NULL; 230 | } 231 | 232 | return TAILQ_LAST(&tq->elems, __lztqhd); 233 | } 234 | 235 | lztq_elem * 236 | lztq_next(lztq_elem * elem) { 237 | if (!elem) { 238 | return NULL; 239 | } 240 | 241 | return TAILQ_NEXT(elem, next); 242 | } 243 | 244 | lztq_elem * 245 | lztq_prev(lztq_elem * elem) { 246 | if (!elem) { 247 | return NULL; 248 | } 249 | 250 | return TAILQ_PREV(elem, __lztqhd, next); 251 | } 252 | 253 | int 254 | lztq_elem_remove(lztq_elem * elem) { 255 | lztq * head; 256 | 257 | if (!elem) { 258 | return -1; 259 | } 260 | 261 | if (!(head = elem->tq_head)) { 262 | return -1; 263 | } 264 | 265 | TAILQ_REMOVE(&head->elems, elem, next); 266 | 267 | head->n_elem -= 1; 268 | 269 | return 0; 270 | } 271 | 272 | int 273 | lztq_for_each(lztq * tq, lztq_iterfn iterfn, void * arg) { 274 | lztq_elem * elem; 275 | 276 | if (!tq || !iterfn) { 277 | return -1; 278 | } 279 | 280 | TAILQ_FOREACH(elem, &tq->elems, next) { 281 | int sres; 282 | 283 | if ((sres = (iterfn)(elem, arg)) != 0) { 284 | return sres; 285 | } 286 | } 287 | 288 | return 0; 289 | } 290 | 291 | void * 292 | lztq_elem_data(lztq_elem * elem) { 293 | if (elem) { 294 | return elem->data; 295 | } 296 | 297 | return NULL; 298 | } 299 | 300 | size_t 301 | lztq_elem_size(lztq_elem * elem) { 302 | if (elem) { 303 | return elem->len; 304 | } 305 | 306 | return 0; 307 | } 308 | 309 | lztq * 310 | lztq_elem_head(lztq_elem * elem) { 311 | if (elem) { 312 | return elem->tq_head; 313 | } 314 | 315 | return NULL; 316 | } 317 | 318 | size_t 319 | lztq_size(lztq * head) { 320 | if (!head) { 321 | return 0; 322 | } 323 | 324 | return head->n_elem; 325 | } 326 | 327 | -------------------------------------------------------------------------------- /test/smoke/smoke_cfg.cfg: -------------------------------------------------------------------------------- 1 | daemonize = false 2 | rootdir = /tmp/ 3 | 4 | server { 5 | addr = 127.0.0.1 6 | port = 8081 7 | threads = 1 8 | 9 | read-timeout = { 10, 0 } 10 | write-timeout = { 10, 0 } 11 | pending-timeout = { 10, 0 } 12 | max-pending = 1 13 | 14 | downstream ds { 15 | enabled = true 16 | addr = 127.0.0.1 17 | port = 8090 18 | connections = 1 19 | retry = { 1, 0 } 20 | } 21 | 22 | vhost localhost { 23 | aliases = { 127.0.0.1:8081, localhost:8081 } 24 | 25 | logging { 26 | request { 27 | enabled = true 28 | output = "file:./rproxy.log" 29 | format = '{SRC} {PROXY} [{TS}] "{METH} {URI} {PROTO}" - {STATUS} - "{REF}" - "{UA}" - "{HOST}' 30 | } 31 | 32 | error { 33 | enabled = true 34 | output = "file:./rproxy_error.log" 35 | } 36 | } 37 | 38 | rule simple { 39 | uri-match = "/simple/" 40 | downstreams = { ds } 41 | 42 | headers { 43 | x-forwarded-for = true 44 | } 45 | } 46 | 47 | rule notfound { 48 | uri-match = "/notfound/" 49 | downstreams = { ds } 50 | 51 | headers { 52 | x-forwarded-for = true 53 | } 54 | } 55 | 56 | rule busy { 57 | uri-match = "/busy/" 58 | downstreams = { ds } 59 | headers { x-forwarded-for = true } 60 | } 61 | 62 | rule data { 63 | uri-match = "/data/" 64 | downstreams = { ds } 65 | headers { x-forwarded-for = true } 66 | } 67 | 68 | rule forwarded { 69 | uri-match = "/forwarded/" 70 | downstreams = { ds } 71 | headers { x-forwarded-for = true } 72 | } 73 | 74 | rule slowdata { 75 | uri-match = "/slowdata/" 76 | downstreams = { ds } 77 | headers { x-forwarded-for = true } 78 | } 79 | 80 | rule badchunklen { 81 | uri-match = "/badchunklength/" 82 | downstreams = { ds } 83 | headers { x-forwarded-for = true } 84 | } 85 | 86 | rule badchunktrans { 87 | uri-match = "/badchunktransfer/" 88 | downstreams = { ds } 89 | headers { x-forwarded-for = true } 90 | } 91 | } 92 | } 93 | 94 | server { 95 | addr = 127.0.0.1 96 | port = 8082 97 | threads = 1 98 | 99 | read-timeout = { 10, 0 } 100 | write-timeout = { 10, 0 } 101 | pending-timeout = { 10, 0 } 102 | max-pending = 1 103 | 104 | downstream ds { 105 | addr = 127.0.0.1 106 | port = 8090 107 | connections = 1 108 | retry = { 1, 0 } 109 | } 110 | 111 | ssl { 112 | enabled = true 113 | protocols-on = { TLSv1, SSLv3 } 114 | protocols-off = { SSLv2 } 115 | cert = ./test/smoke/server.crt 116 | key = ./test/smoke/server.key 117 | ciphers = "eNULL:RC4-SHA" 118 | } 119 | 120 | vhost localhost { 121 | aliases = { 127.0.0.1:8082, localhost:8082 } 122 | 123 | logging { 124 | request { 125 | enabled = true 126 | output = "file:./rproxy.log" 127 | format = '{SRC} {PROXY} [{TS}] "{METH} {URI} {PROTO}" - {STATUS} - "{REF}" - "{UA}" - "{HOST}' 128 | } 129 | 130 | error { 131 | enabled = true 132 | output = "file:./rproxy_error.log" 133 | } 134 | } 135 | 136 | rule simple { 137 | uri-match = "/simple/" 138 | downstreams = { ds } 139 | } 140 | } 141 | } 142 | 143 | server { 144 | addr = 127.0.0.1 145 | port = 8083 146 | threads = 1 147 | 148 | read-timeout = { 10, 0 } 149 | write-timeout = { 10, 0 } 150 | pending-timeout = { 10, 0 } 151 | max-pending = 1 152 | 153 | ssl { 154 | enabled = true 155 | protocols-on = { TLSv1, SSLv3 } 156 | protocols-off = { SSLv2 } 157 | cert = ./test/smoke/server.crt 158 | key = ./test/smoke/server.key 159 | ca = ./test/smoke/ca.crt 160 | ciphers = "eNULL:RC4-SHA" 161 | verify-peer = true 162 | verify-depth = 4 163 | enforce-peer-cert = true 164 | cache-enabled = false 165 | } 166 | 167 | downstream ds { 168 | addr = 127.0.0.1 169 | port = 8090 170 | connections = 1 171 | retry = { 1, 0 } 172 | } 173 | 174 | vhost localhost { 175 | aliases = { 127.0.0.1:8083, localhost:8083 } 176 | 177 | logging { 178 | request { 179 | enabled = true 180 | output = "file:./rproxy.log" 181 | format = '{SRC} {RULE} {PROXY} [{TS}] "{METH} {URI} {PROTO}" - {STATUS} - "{REF}" - "{UA}" - "{HOST}' 182 | } 183 | 184 | error { 185 | enabled = true 186 | output = "file:./rproxy_error.log" 187 | } 188 | } 189 | 190 | headers { 191 | x-ssl-subject = true 192 | x-ssl-issuer = true 193 | x-ssl-notbefore = true 194 | x-ssl-notafter = true 195 | x-ssl-serial = true 196 | x-ssl-cipher = true 197 | x-ssl-certificate = true 198 | 199 | x509-extension { 200 | name = "x-ssl-extension" 201 | oid = "1.2.3.4" 202 | } 203 | } 204 | 205 | rule simple { 206 | uri-match = "/simple/" 207 | downstreams = { ds } 208 | } 209 | 210 | rule subject { 211 | uri-match = "/subject/" 212 | downstreams = { ds } 213 | } 214 | 215 | rule issuer { 216 | uri-match = "/issuer/" 217 | downstreams = { ds } 218 | } 219 | 220 | rule notbefore { 221 | uri-match = "/notbefore/" 222 | downstreams = { ds } 223 | } 224 | 225 | rule notafter { 226 | uri-match = "/notafter/" 227 | downstreams = { ds } 228 | } 229 | 230 | rule serial { 231 | uri-match = "/serial/" 232 | downstreams = { ds } 233 | } 234 | 235 | rule cipher { 236 | uri-match = "/cipher/" 237 | downstreams = { ds } 238 | } 239 | 240 | rule certificate { 241 | uri-match = "/certificate/" 242 | downstreams = { ds } 243 | } 244 | 245 | rule useragent { 246 | uri-match = "/useragent/" 247 | downstreams = { ds } 248 | } 249 | 250 | rule host { 251 | uri-match = "/host/" 252 | downstreams = { ds } 253 | } 254 | 255 | rule accept { 256 | uri-match = "/accept/" 257 | downstreams = { ds } 258 | } 259 | 260 | rule extension { 261 | uri-match = "/extension/" 262 | downstreams = { ds } 263 | } 264 | 265 | rule slowdata { 266 | uri-match = "/slowdata/" 267 | downstreams = { ds } 268 | } 269 | 270 | rule badchunklen { 271 | uri-match = "/badchunklength/" 272 | downstreams = { ds } 273 | } 274 | 275 | rule badchunkxfer { 276 | uri-match = "/badchunktransfer/" 277 | downstreams = { ds } 278 | } 279 | } 280 | } 281 | 282 | server { 283 | addr = 127.0.0.1 284 | port = 8084 285 | threads = 1 286 | 287 | read-timeout = { 10, 0 } 288 | write-timeout = { 10, 0 } 289 | pending-timeout = { 10, 0 } 290 | max-pending = 1 291 | 292 | downstream ds1 { 293 | addr = 127.0.0.1 294 | port = 8090 295 | connections = 1 296 | retry = { 1, 0 } 297 | } 298 | 299 | downstream ds2 { 300 | addr = 127.0.0.1 301 | port = 8091 302 | connections = 1 303 | retry = { 1, 0 } 304 | } 305 | 306 | vhost localhost { 307 | aliases = { localhost:8084, 127.0.0.1:8084 } 308 | 309 | rule test_rr { 310 | uri-match = "/test_rr/" 311 | downstreams = { ds1, ds2 } 312 | lb-method = roundrobin 313 | } 314 | } 315 | } 316 | 317 | -------------------------------------------------------------------------------- /test/smoke/smoke_downstream.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2012] [Mandiant, inc] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | 19 | #include 20 | #include 21 | 22 | struct event_base * evbase; 23 | struct event * timer_event; 24 | int slow_data_bytes; 25 | 26 | static void 27 | head_cb(evhtp_request_t * req, void * arg) { 28 | evhtp_send_reply(req, EVHTP_RES_OK); 29 | } 30 | 31 | static void 32 | simple_cb(evhtp_request_t * req, void * arg) { 33 | evhtp_send_reply(req, EVHTP_RES_OK); 34 | } 35 | 36 | static void 37 | busy_cb(evhtp_request_t * req, void * arg) { 38 | evhtp_send_reply(req, EVHTP_RES_SERVUNAVAIL); 39 | } 40 | 41 | static void 42 | nettirwer_cb(evhtp_request_t * req, void * arg) { 43 | evhtp_send_reply(req, EVHTP_RES_OK); 44 | } 45 | 46 | static void 47 | data_cb(evhtp_request_t * req, void * arg) { 48 | evbuf_t * buf; 49 | 50 | buf = evbuffer_new(); 51 | 52 | evhtp_send_reply_chunk_start(req, EVHTP_RES_OK); 53 | 54 | evbuffer_add(buf, "SUCCESS", strlen("SUCCESS")); 55 | evhtp_send_reply_chunk(req, buf); 56 | 57 | evbuffer_drain(buf, -1); 58 | 59 | evhtp_send_reply_chunk_end(req); 60 | evbuffer_free(buf); 61 | } 62 | 63 | static void 64 | send_reply_header(evhtp_request_t * req, const char * header) { 65 | evbuf_t * buf = evbuffer_new(); 66 | const char* found = evhtp_header_find(req->headers_in, header); 67 | 68 | evhtp_send_reply_chunk_start(req, EVHTP_RES_OK); 69 | evbuffer_add(buf, found, strlen(found)); 70 | evhtp_send_reply_chunk(req, buf); 71 | 72 | evbuffer_drain(buf, -1); 73 | 74 | evhtp_send_reply_chunk_end(req); 75 | evbuffer_free(buf); 76 | } 77 | 78 | static void 79 | forwarded_cb(evhtp_request_t * req, void * arg) { 80 | send_reply_header(req, "x-forwarded-for"); 81 | } 82 | 83 | static void 84 | ssl_subject_cb(evhtp_request_t * req, void * arg) { 85 | send_reply_header(req, "x-ssl-subject"); 86 | } 87 | 88 | static void 89 | ssl_issuer_cb(evhtp_request_t * req, void * arg) { 90 | send_reply_header(req, "x-ssl-issuer"); 91 | } 92 | 93 | static void 94 | ssl_notbefore_cb(evhtp_request_t * req, void * arg) { 95 | send_reply_header(req, "x-ssl-notbefore"); 96 | } 97 | 98 | static void 99 | ssl_notafter_cb(evhtp_request_t * req, void * arg) { 100 | send_reply_header(req, "x-ssl-notafter"); 101 | } 102 | 103 | static void 104 | ssl_serial_cb(evhtp_request_t * req, void * arg) { 105 | send_reply_header(req, "x-ssl-serial"); 106 | } 107 | 108 | static void 109 | ssl_cipher_cb(evhtp_request_t * req, void * arg) { 110 | send_reply_header(req, "x-ssl-cipher"); 111 | } 112 | 113 | static void 114 | ssl_certificate_cb(evhtp_request_t * req, void * arg) { 115 | send_reply_header(req, "x-ssl-certificate"); 116 | } 117 | 118 | static void 119 | useragent_cb(evhtp_request_t * req, void * arg) { 120 | send_reply_header(req, "user-agent"); 121 | } 122 | 123 | static void 124 | host_cb(evhtp_request_t * req, void * arg) { 125 | send_reply_header(req, "host"); 126 | } 127 | 128 | static void 129 | accept_cb(evhtp_request_t * req, void * arg) { 130 | send_reply_header(req, "accept"); 131 | } 132 | 133 | static void 134 | extension_cb(evhtp_request_t * req, void * arg) { 135 | send_reply_header(req, "x-ssl-extension"); 136 | } 137 | 138 | static void 139 | slowdata_timer_cb(evutil_socket_t fd, short event, void * arg) { 140 | evhtp_request_t * req = (evhtp_request_t *)arg; 141 | 142 | /* Write an additional 1 byte */ 143 | evbuf_t* buf = evbuffer_new(); 144 | 145 | evbuffer_add(buf, "0", 1); 146 | evhtp_send_reply_chunk(req, buf); 147 | evbuffer_drain(buf, -1); 148 | ++slow_data_bytes; 149 | 150 | if (slow_data_bytes == 10) { 151 | /* Reached our limit, close the reply */ 152 | evhtp_send_reply_chunk_end(req); 153 | evtimer_del(timer_event); 154 | } else { 155 | /* Keep going */ 156 | struct timeval tv; 157 | tv.tv_sec = 0; 158 | tv.tv_usec = 500000; 159 | evtimer_add(timer_event, &tv); 160 | } 161 | 162 | evbuffer_free(buf); 163 | } 164 | 165 | static void 166 | slowdata_cb(evhtp_request_t * req, void * arg) { 167 | /* Init our slow data vars */ 168 | slow_data_bytes = 0; 169 | 170 | /* Start the reply */ 171 | evhtp_send_reply_chunk_start(req, EVHTP_RES_OK); 172 | 173 | /* Create a timer for this request */ 174 | timer_event = evtimer_new(evbase, slowdata_timer_cb, req); 175 | 176 | /* Set the timer and exit. */ 177 | struct timeval tv; 178 | tv.tv_sec = 0; 179 | tv.tv_usec = 500000; 180 | evtimer_add(timer_event, &tv); 181 | } 182 | 183 | static void 184 | send_chunk(evhtp_request_t * req, const char* data, const char* hdrfmt, size_t len) { 185 | evbuf_t * buf = evbuffer_new(); 186 | evbuf_t * output; 187 | 188 | /* Add the data to the output */ 189 | evbuffer_add(buf, data, strlen(data)); 190 | output = bufferevent_get_output(req->conn->bev); 191 | evbuffer_add_printf(output, hdrfmt, len); 192 | 193 | /* Send the buffer */ 194 | evhtp_send_reply_body(req, buf); 195 | evbuffer_add(output, "\r\n", 2); 196 | 197 | /* Drain the buffer and free it */ 198 | evbuffer_drain(buf, -1); 199 | evbuffer_free(buf); 200 | } 201 | 202 | static void 203 | badchunk_length_cb(evhtp_request_t * req, void * arg) { 204 | evbuf_t * buf = evbuffer_new(); 205 | evbuf_t * output; 206 | 207 | /* Start the chunk */ 208 | evhtp_send_reply_chunk_start(req, EVHTP_RES_OK); 209 | 210 | /* Send some data */ 211 | send_chunk(req, "SUCCESS", "%d\r\n", strlen("SUCCESS")); 212 | 213 | /* Close the chunk */ 214 | evhtp_send_reply_chunk_end(req); 215 | evbuffer_free(buf); 216 | } 217 | 218 | static void 219 | badchunk_transfer_cb(evhtp_request_t * req, void * arg) { 220 | /* Start the chunk */ 221 | evhtp_send_reply_chunk_start(req, EVHTP_RES_OK); 222 | 223 | /* Send a few chunks with a bogus GET in the middle */ 224 | send_chunk(req, "DATA", "%d\r\n", strlen("DATA")); 225 | send_chunk(req, "GET /index.html HTTP/1.1", "", 0); 226 | send_chunk(req, "MOREDATA", "%d\r\n", strlen("DATA")); 227 | 228 | /* Flush the connection */ 229 | bufferevent_flush(req->conn->bev, EV_WRITE, BEV_FLUSH); 230 | 231 | /* Close the chunk */ 232 | evhtp_send_reply_chunk_end(req); 233 | } 234 | 235 | static void 236 | rr_cb(evhtp_request_t * req, void * arg) { 237 | evbuffer_add(req->buffer_out, arg, strlen((const char *)arg)); 238 | evhtp_send_reply(req, EVHTP_RES_OK); 239 | } 240 | 241 | int 242 | main(int argc, char ** argv) { 243 | evhtp_t * htp = NULL; 244 | evhtp_t * htp2 = NULL; 245 | 246 | evbase = event_base_new(); 247 | htp = evhtp_new(evbase, NULL); 248 | htp2 = evhtp_new(evbase, NULL); 249 | 250 | slow_data_bytes = 0; 251 | 252 | evhtp_set_cb(htp, "/", head_cb, NULL); 253 | evhtp_set_cb(htp, "/simple/", simple_cb, NULL); 254 | evhtp_set_cb(htp, "/busy/", busy_cb, NULL); 255 | evhtp_set_cb(htp, "/nettirwer/", nettirwer_cb, NULL); 256 | evhtp_set_cb(htp, "/data/", data_cb, NULL); 257 | evhtp_set_cb(htp, "/forwarded/", forwarded_cb, NULL); 258 | evhtp_set_cb(htp, "/subject/", ssl_subject_cb, NULL); 259 | evhtp_set_cb(htp, "/issuer/", ssl_issuer_cb, NULL); 260 | evhtp_set_cb(htp, "/notbefore/", ssl_notbefore_cb, NULL); 261 | evhtp_set_cb(htp, "/notafter/", ssl_notafter_cb, NULL); 262 | evhtp_set_cb(htp, "/serial/", ssl_serial_cb, NULL); 263 | evhtp_set_cb(htp, "/cipher/", ssl_cipher_cb, NULL); 264 | evhtp_set_cb(htp, "/certificate/", ssl_certificate_cb, NULL); 265 | evhtp_set_cb(htp, "/useragent/", useragent_cb, NULL); 266 | evhtp_set_cb(htp, "/host/", host_cb, NULL); 267 | evhtp_set_cb(htp, "/accept/", accept_cb, NULL); 268 | evhtp_set_cb(htp, "/extension/", extension_cb, NULL); 269 | evhtp_set_cb(htp, "/slowdata/", slowdata_cb, NULL); 270 | evhtp_set_cb(htp, "/badchunklength/", badchunk_length_cb, NULL); 271 | evhtp_set_cb(htp, "/badchunktransfer/", badchunk_transfer_cb, NULL); 272 | 273 | evhtp_set_cb(htp, "/test_rr/", rr_cb, "one\n"); 274 | evhtp_set_cb(htp2, "/test_rr/", rr_cb, "two\n"); 275 | 276 | if (evhtp_bind_socket(htp, "0.0.0.0", 8090, 128) < 0) { 277 | fprintf(stderr, "Could not bind socket: %s\n", strerror(errno)); 278 | exit(-1); 279 | } 280 | 281 | if (evhtp_bind_socket(htp2, "0.0.0.0", 8091, 128) < 0) { 282 | fprintf(stderr, "Couldnot bind to socket: %s\n", strerror(errno)); 283 | exit(-1); 284 | } 285 | 286 | event_base_loop(evbase, 0); 287 | 288 | return 0; 289 | } /* main */ 290 | 291 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef NO_RLIMITS 13 | #include 14 | #include 15 | #endif 16 | 17 | #include "rproxy.h" 18 | 19 | int 20 | util_write_header_to_evbuffer(evhtp_header_t * hdr, void * arg) { 21 | evbuf_t * buf; 22 | 23 | buf = arg; 24 | assert(buf != NULL); 25 | 26 | evbuffer_add(buf, hdr->key, hdr->klen); 27 | evbuffer_add(buf, ": ", 2); 28 | evbuffer_add(buf, hdr->val, hdr->vlen); 29 | evbuffer_add(buf, "\r\n", 2); 30 | 31 | return 0; 32 | } 33 | 34 | evbuf_t * 35 | util_request_to_evbuffer(evhtp_request_t * r) { 36 | evbuf_t * buf; 37 | char * query_args; 38 | 39 | if (r->uri && r->uri->query_raw) { 40 | query_args = r->uri->query_raw; 41 | } else { 42 | query_args = ""; 43 | } 44 | 45 | if (*query_args == '?') { 46 | query_args++; 47 | } 48 | 49 | buf = evbuffer_new(); 50 | assert(buf != NULL); 51 | 52 | evbuffer_add_printf(buf, "%s %s%s%s HTTP/%d.%d\r\n", 53 | htparser_get_methodstr(r->conn->parser), 54 | r->uri->path->full, 55 | *query_args ? "?" : "", query_args, 56 | htparser_get_major(r->conn->parser), 57 | htparser_get_minor(r->conn->parser)); 58 | 59 | evhtp_headers_for_each(r->headers_in, util_write_header_to_evbuffer, buf); 60 | evbuffer_add(buf, "\r\n", 2); 61 | 62 | return buf; 63 | } 64 | 65 | void 66 | util_dropperms(const char * user, const char * group) { 67 | if (group) { 68 | struct group * grp; 69 | 70 | if (!(grp = getgrnam(group))) { 71 | fprintf(stderr, "No such group '%s'\n", group); 72 | exit(1); 73 | } 74 | 75 | if (setgid(grp->gr_gid) != 0) { 76 | fprintf(stderr, "Could not grp perm to '%s' (%s)\n", 77 | group, strerror(errno)); 78 | exit(1); 79 | } 80 | } 81 | 82 | if (user) { 83 | struct passwd * usr; 84 | 85 | if (!(usr = getpwnam(user))) { 86 | fprintf(stderr, "No such user '%s'\n", user); 87 | exit(1); 88 | } 89 | 90 | if (seteuid(usr->pw_uid) != 0) { 91 | fprintf(stderr, "Could not usr perm to '%s' (%s)\n", 92 | user, strerror(errno)); 93 | exit(1); 94 | } 95 | } 96 | } 97 | 98 | int 99 | util_daemonize(char * root, int noclose) { 100 | int fd; 101 | 102 | switch (fork()) { 103 | case -1: 104 | return -1; 105 | case 0: 106 | break; 107 | default: 108 | exit(EXIT_SUCCESS); 109 | } 110 | 111 | if (setsid() == -1) { 112 | return -1; 113 | } 114 | 115 | if (root == 0) { 116 | if (chdir(root) != 0) { 117 | perror("chdir"); 118 | return -1; 119 | } 120 | } 121 | 122 | if (noclose == 0 && (fd = open("/dev/null", O_RDWR, 0)) != -1) { 123 | if (dup2(fd, STDIN_FILENO) < 0) { 124 | perror("dup2 stdin"); 125 | return -1; 126 | } 127 | if (dup2(fd, STDOUT_FILENO) < 0) { 128 | perror("dup2 stdout"); 129 | return -1; 130 | } 131 | if (dup2(fd, STDERR_FILENO) < 0) { 132 | perror("dup2 stderr"); 133 | return -1; 134 | } 135 | 136 | if (fd > STDERR_FILENO) { 137 | if (close(fd) < 0) { 138 | perror("close"); 139 | return -1; 140 | } 141 | } 142 | } 143 | return 0; 144 | } /* daemonize */ 145 | 146 | int 147 | util_set_rlimits(int nofiles) { 148 | #ifndef NO_RLIMITS 149 | struct rlimit limit; 150 | rlim_t max_nofiles; 151 | 152 | if (nofiles <= 0) { 153 | return -1; 154 | } 155 | 156 | if (getrlimit(RLIMIT_NOFILE, &limit) == -1) { 157 | fprintf(stderr, "Could not obtain curr NOFILE lim: %s\n", strerror(errno)); 158 | return 0; 159 | } 160 | 161 | if (nofiles > limit.rlim_max) { 162 | fprintf(stderr, "Unable to set curr NOFILE (requested=%d, sys-limit=%d)\n", 163 | (int)nofiles, (int)limit.rlim_max); 164 | fprintf(stderr, "Please make sure your systems limits.conf is set high enough (usually in /etc/security/limits.conf!\n"); 165 | return -1; 166 | } 167 | 168 | if (nofiles < 10000) { 169 | fprintf(stderr, "WARNING: %d max-nofiles is very small, this could be bad, lets check...\n", nofiles); 170 | 171 | if ((int)limit.rlim_max >= 10000) { 172 | fprintf(stderr, "INFO: using %d (your hard-limit) on max-nofiles instead of %d!\n", (int)limit.rlim_max, nofiles); 173 | nofiles = limit.rlim_max; 174 | } else { 175 | fprintf(stderr, "WARN: nope, can't go any higher, you may want to fix this...\n"); 176 | } 177 | } 178 | 179 | limit.rlim_cur = nofiles; 180 | 181 | if (setrlimit(RLIMIT_NOFILE, &limit) == -1) { 182 | fprintf(stderr, "Could not set NOFILE lim: %s\n", strerror(errno)); 183 | return -1; 184 | } 185 | 186 | #else 187 | fprintf(stderr, "Your system does not support get|setrlimits!\n"); 188 | #endif 189 | return 0; 190 | } /* rproxy_set_rlimits */ 191 | 192 | /** 193 | * @brief glob/wildcard type pattern matching. 194 | * 195 | * Note: This code was derived from redis's (v2.6) stringmatchlen() function. 196 | * 197 | * @param pattern 198 | * @param string 199 | * 200 | * @return 201 | */ 202 | int 203 | util_glob_match(const char * pattern, const char * string) { 204 | size_t pat_len; 205 | size_t str_len; 206 | 207 | if (!pattern || !string) { 208 | return 0; 209 | } 210 | 211 | pat_len = strlen(pattern); 212 | str_len = strlen(string); 213 | 214 | while (pat_len) { 215 | if (pattern[0] == '*') { 216 | while (pattern[1] == '*') { 217 | pattern++; 218 | pat_len--; 219 | } 220 | 221 | if (pat_len == 1) { 222 | return 1; 223 | } 224 | 225 | while (str_len) { 226 | if (util_glob_match(pattern + 1, string)) { 227 | return 1; 228 | } 229 | 230 | string++; 231 | str_len--; 232 | } 233 | 234 | return 0; 235 | } else { 236 | if (pattern[0] != string[0]) { 237 | return 0; 238 | } 239 | 240 | string++; 241 | str_len--; 242 | } 243 | 244 | pattern++; 245 | pat_len--; 246 | 247 | if (str_len == 0) { 248 | while (*pattern == '*') { 249 | pattern++; 250 | pat_len--; 251 | } 252 | break; 253 | } 254 | } 255 | 256 | if (pat_len == 0 && str_len == 0) { 257 | return 1; 258 | } 259 | 260 | return 0; 261 | } /* util_glob_match */ 262 | 263 | static int 264 | _glob_match_iterfn(lztq_elem * elem, void * arg) { 265 | const char * needle; 266 | const char * haystack; 267 | 268 | if (!(needle = (const char *)arg)) { 269 | return -1; 270 | } 271 | 272 | if (!(haystack = (const char *)lztq_elem_data(elem))) { 273 | return -1; 274 | } 275 | 276 | return util_glob_match(haystack, needle); 277 | } 278 | 279 | /** 280 | * @brief iterate over a tailq of strings and attempt to find a wildcard match. 281 | * 282 | * @param tq a lztq of strings to match against. (haystack) 283 | * @param str the string to compare. (needle) 284 | * 285 | * @return -1 on errir, 1 on match, 0 on no match. 286 | */ 287 | int 288 | util_glob_match_lztq(lztq * tq, const char * str) { 289 | /* if the tailq is empty, we default to allowed */ 290 | if (tq == NULL || lztq_size(tq) == 0) { 291 | return 1; 292 | } 293 | 294 | /* since lztq_for_each will break from the loop if the iterator returns 295 | * anything but zero, we utilize the result as an indicator as to whether 296 | * the wildcard match was sucessful. 297 | * 298 | * result = 0 == no match 299 | * result = 1 == match 300 | * result = -1 == matching error 301 | */ 302 | return lztq_for_each(tq, _glob_match_iterfn, (void *)str); 303 | } 304 | 305 | static int 306 | _rm_headers_iterfn(lztq_elem * elem, void * arg) { 307 | evhtp_headers_t * headers; 308 | const char * header_key; 309 | 310 | if (!(headers = (evhtp_headers_t *)arg)) { 311 | return -1; 312 | } 313 | 314 | if (!(header_key = (const char *)lztq_elem_data(elem))) { 315 | return -1; 316 | } 317 | 318 | evhtp_kv_rm_and_free(headers, evhtp_kvs_find_kv(headers, header_key)); 319 | 320 | return 0; 321 | } 322 | 323 | /** 324 | * @brief iterates over a lzq of strings and removes any headers with that 325 | * string. 326 | * 327 | * @param tq 328 | * @param headers 329 | * 330 | * @return 0 on success, otherwise -1 331 | */ 332 | int 333 | util_rm_headers_via_lztq(lztq * tq, evhtp_headers_t * headers) { 334 | if (tq == NULL || headers == NULL) { 335 | return 0; 336 | } 337 | 338 | return lztq_for_each(tq, _rm_headers_iterfn, (void *)headers); 339 | } 340 | 341 | -------------------------------------------------------------------------------- /test/unit/tinytest_macros.h: -------------------------------------------------------------------------------- 1 | /* tinytest_macros.h -- Copyright 2009-2011 Nick Mathewson 2 | * 3 | * Redistribution and use in source and binary forms, with or without 4 | * modification, are permitted provided that the following conditions 5 | * are met: 6 | * 1. Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * 2. Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 3. The name of the author may not be used to endorse or promote products 12 | * derived from this software without specific prior written permission. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #ifndef _TINYTEST_MACROS_H 27 | #define _TINYTEST_MACROS_H 28 | 29 | /* Helpers for defining statement-like macros */ 30 | #define TT_STMT_BEGIN do { 31 | #define TT_STMT_END \ 32 | } while (0) 33 | 34 | /* Redefine this if your test functions want to abort with something besides 35 | * "goto end;" */ 36 | #ifndef TT_EXIT_TEST_FUNCTION 37 | #define TT_EXIT_TEST_FUNCTION TT_STMT_BEGIN goto end; TT_STMT_END 38 | #endif 39 | 40 | /* Redefine this if you want to note success/failure in some different way. */ 41 | #ifndef TT_DECLARE 42 | #define TT_DECLARE(prefix, args) \ 43 | TT_STMT_BEGIN \ 44 | printf("\n %s %s:%d: ", prefix, __FILE__, __LINE__); \ 45 | printf args; \ 46 | TT_STMT_END 47 | #endif 48 | 49 | /* Announce a failure. Args are parenthesized printf args. */ 50 | #define TT_GRIPE(args) TT_DECLARE("FAIL", args) 51 | 52 | /* Announce a non-failure if we're verbose. */ 53 | #define TT_BLATHER(args) \ 54 | TT_STMT_BEGIN \ 55 | if (_tinytest_get_verbosity() > 1) { \ 56 | TT_DECLARE(" OK", args); \ 57 | } \ 58 | TT_STMT_END 59 | 60 | #define TT_DIE(args) \ 61 | TT_STMT_BEGIN \ 62 | _tinytest_set_test_failed(); \ 63 | TT_GRIPE(args); \ 64 | TT_EXIT_TEST_FUNCTION; \ 65 | TT_STMT_END 66 | 67 | #define TT_FAIL(args) \ 68 | TT_STMT_BEGIN \ 69 | _tinytest_set_test_failed(); \ 70 | TT_GRIPE(args); \ 71 | TT_STMT_END 72 | 73 | /* Fail and abort the current test for the reason in msg */ 74 | #define tt_abort_printf(msg) TT_DIE(msg) 75 | #define tt_abort_perror(op) TT_DIE(("%s: %s [%d]", (op), strerror(errno), errno)) 76 | #define tt_abort_msg(msg) TT_DIE(("%s", msg)) 77 | #define tt_abort() TT_DIE(("%s", "(Failed.)")) 78 | 79 | /* Fail but do not abort the current test for the reason in msg. */ 80 | #define tt_fail_printf(msg) TT_FAIL(msg) 81 | #define tt_fail_perror(op) TT_FAIL(("%s: %s [%d]", (op), strerror(errno), errno)) 82 | #define tt_fail_msg(msg) TT_FAIL(("%s", msg)) 83 | #define tt_fail() TT_FAIL(("%s", "(Failed.)")) 84 | 85 | /* End the current test, and indicate we are skipping it. */ 86 | #define tt_skip() \ 87 | TT_STMT_BEGIN \ 88 | _tinytest_set_test_skipped(); \ 89 | TT_EXIT_TEST_FUNCTION; \ 90 | TT_STMT_END 91 | 92 | #define _tt_want(b, msg, fail) \ 93 | TT_STMT_BEGIN \ 94 | if (!(b)) { \ 95 | _tinytest_set_test_failed(); \ 96 | TT_GRIPE(("%s", msg)); \ 97 | fail; \ 98 | } else { \ 99 | TT_BLATHER(("%s", msg)); \ 100 | } \ 101 | TT_STMT_END 102 | 103 | /* Assert b, but do not stop the test if b fails. Log msg on failure. */ 104 | #define tt_want_msg(b, msg) \ 105 | _tt_want(b, msg, ); 106 | 107 | /* Assert b and stop the test if b fails. Log msg on failure. */ 108 | #define tt_assert_msg(b, msg) \ 109 | _tt_want(b, msg, TT_EXIT_TEST_FUNCTION); 110 | 111 | /* Assert b, but do not stop the test if b fails. */ 112 | #define tt_want(b) tt_want_msg( (b), "want(" # b ")") 113 | /* Assert b, and stop the test if b fails. */ 114 | #define tt_assert(b) tt_assert_msg((b), "assert(" # b ")") 115 | 116 | #define tt_assert_test_fmt_type(a, b, str_test, type, test, printf_type, printf_fmt, \ 117 | setup_block, cleanup_block, die_on_fail) \ 118 | TT_STMT_BEGIN \ 119 | type _val1 = (type)(a); \ 120 | type _val2 = (type)(b); \ 121 | int _tt_status = (test); \ 122 | if (!_tt_status || _tinytest_get_verbosity() > 1) { \ 123 | printf_type _print; \ 124 | printf_type _print1; \ 125 | printf_type _print2; \ 126 | type _value = _val1; \ 127 | setup_block; \ 128 | _print1 = _print; \ 129 | _value = _val2; \ 130 | setup_block; \ 131 | _print2 = _print; \ 132 | TT_DECLARE(_tt_status ? " OK": "FAIL", \ 133 | ("assert(%s): " printf_fmt " vs " printf_fmt, \ 134 | str_test, _print1, _print2)); \ 135 | _print = _print1; \ 136 | cleanup_block; \ 137 | _print = _print2; \ 138 | cleanup_block; \ 139 | if (!_tt_status) { \ 140 | _tinytest_set_test_failed(); \ 141 | die_on_fail; \ 142 | } \ 143 | } \ 144 | TT_STMT_END 145 | 146 | #define tt_assert_test_type(a, b, str_test, type, test, fmt, die_on_fail) \ 147 | tt_assert_test_fmt_type(a, b, str_test, type, test, type, fmt, \ 148 | { _print = _value; }, {}, die_on_fail) 149 | 150 | /* Helper: assert that a op b, when cast to type. Format the values with 151 | * printf format fmt on failure. */ 152 | #define tt_assert_op_type(a, op, b, type, fmt) \ 153 | tt_assert_test_type(a, b, # a " " # op " " # b, type, (_val1 op _val2), fmt, \ 154 | TT_EXIT_TEST_FUNCTION) 155 | 156 | #define tt_int_op(a, op, b) \ 157 | tt_assert_test_type(a, b, # a " " # op " " # b, long, (_val1 op _val2), \ 158 | "%ld", TT_EXIT_TEST_FUNCTION) 159 | 160 | #define tt_uint_op(a, op, b) \ 161 | tt_assert_test_type(a, b, # a " " # op " " # b, unsigned long, \ 162 | (_val1 op _val2), "%lu", TT_EXIT_TEST_FUNCTION) 163 | 164 | #define tt_ptr_op(a, op, b) \ 165 | tt_assert_test_type(a, b, # a " " # op " " # b, void*, \ 166 | (_val1 op _val2), "%p", TT_EXIT_TEST_FUNCTION) 167 | 168 | #define tt_str_op(a, op, b) \ 169 | tt_assert_test_type(a, b, # a " " # op " " # b, const char *, \ 170 | (strcmp(_val1, _val2) op 0), "<%s>", TT_EXIT_TEST_FUNCTION) 171 | 172 | #define tt_want_int_op(a, op, b) \ 173 | tt_assert_test_type(a, b, # a " " # op " " # b, long, (_val1 op _val2), "%ld", (void)0) 174 | 175 | #define tt_want_uint_op(a, op, b) \ 176 | tt_assert_test_type(a, b, # a " " # op " " # b, unsigned long, \ 177 | (_val1 op _val2), "%lu", (void)0) 178 | 179 | #define tt_want_ptr_op(a, op, b) \ 180 | tt_assert_test_type(a, b, # a " " # op " " # b, void*, \ 181 | (_val1 op _val2), "%p", (void)0) 182 | 183 | #define tt_want_str_op(a, op, b) \ 184 | tt_assert_test_type(a, b, # a " " # op " " # b, const char *, \ 185 | (strcmp(_val1, _val2) op 0), "<%s>", (void)0) 186 | 187 | #endif 188 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # RProxy 2 | 3 | RProxy is a reverse proxy server written with performance and scale in mind. 4 | 5 | # Building and installing 6 | 7 | ## Dependencies 8 | * [Libevent](http://libevent.org) 9 | * [Libevhtp](http://github.com/ellzey/libevhtp) 10 | * [OpenSSL](http://openssl.org) 11 | * [Libconfuse](http://www.nongnu.org/confuse/) 12 | 13 | ## Building with all dependencies compiled and statically linked 14 | 1. cd build 15 | 2. cmake -DRPROXY_BUILD_DEPS:STRING=ON .. 16 | 3. make 17 | 18 | ## Build using system-wide dependencies 19 | 1. cd build 20 | 2. cmake .. 21 | 3. make 22 | 23 | 24 | # Configuration 25 | 26 | ## Base Configuration 27 | 28 | Before any other sub-section of the configuration is processed, the following 29 | options can be set. 30 | 31 | daemonize = false 32 | rootdir = /tmp 33 | user = nobody 34 | group = nobody 35 | max-nofile = 1024 36 | 37 | 38 | * __daemonize__ 39 | 40 | The value of this option is a boolean (either true or false). If the value 41 | is true, RProxy will daemonize after start, otherwise the server will run in the 42 | foreground. 43 | 44 | * __rootdir__ 45 | 46 | If RProxy is configured to daemonize, the service will daemonize into this 47 | i directory. 48 | 49 | * __user__ 50 | 51 | Drop permissions to this user once root operations have been executed. 52 | The default of this is to run as the current user. 53 | 54 | * __group__ 55 | 56 | Drop permissions to this group once root operations have been executed. 57 | The default of this is to run as the current group. 58 | 59 | * __max-nofile__ 60 | 61 | If your system supports set/get rlimits, this sets the maximum number of 62 | file-descriptors the server can use at one time. Since RProxy will attempt 63 | to keep all of the downstream connections alive, it is suggested that this 64 | number be pretty high. 65 | 66 | It should be noted that by default, most systems won't allow a user to go 67 | over a static number (most of the time 1024) even with setrlimit. In this 68 | case, a user must perform system-wide configurations. 69 | 70 | On linux you can add the following to the file /etc/security/limits.conf: 71 | 72 | "* hard nofile 590000" 73 | 74 | Note a reboot is required for this setting to take effect. 75 | 76 | On OSX, the following command can be run 77 | 78 | "sudo launchctl limit maxfiles 590000 590000" 79 | 80 | ## Base Server Configuration 81 | 82 | RProxy configuration must contain one or more "server" configuration sections. 83 | These sections contains all the required information to deal with an incoming 84 | request. The base configuration for a server is as follows: 85 | 86 | addr = 127.0.0.1 87 | port = 8080 88 | threads = 4 89 | read-timeout = { 0, 0 } 90 | write-timeout = { 0, 0 } 91 | pending-timeout = { 0, 0 } 92 | high-watermark = 0 93 | max-pending = 0 94 | backlog = 0 95 | 96 | * __addr__ 97 | 98 | The IP address in which to listen on. 99 | 100 | * __port__ 101 | 102 | The port to listen on 103 | 104 | * __threads__ 105 | 106 | The number of threads to use for this server (note that downstream 107 | connections are multiplied by this number). 108 | 109 | * __read-timeout__ 110 | 111 | The timeout in { 'seconds', 'microseconds' } for a client connection who has 112 | not sent any data. For example, if the value of this was "{ 1, 0 }", if the 113 | client has not been active for 1 second, the connection is closed. 114 | 115 | Setting this will evade potential idle connection DoS attacks. 116 | 117 | * __write-timeout__ 118 | 119 | The timeout in { 'seconds', 'microseconds' } to wait for data to the client 120 | to be written. If a client is blocking on the read for this long, the 121 | connection is closed. 122 | 123 | * __pending-timeout__ 124 | 125 | When a connection is first made to RProxy, it is not immediately processed, 126 | instead it is placed in a pending queue. Only when a downstream connection has 127 | become available for use does the client get serviced. If a downstream does not 128 | become available for this amount of time, the client connection is shut down and 129 | removed from the pending queue. 130 | 131 | This makes sure both the RProxy service and the downstream services are 132 | never overloaded. 133 | 134 | * __max-pending__ 135 | 136 | If there are this many clients waiting for a backend server to be available, 137 | the incoming connection will not be accepted. 138 | 139 | This setting assures that the server cannot be overloaded with too many 140 | connections. 141 | 142 | * __high-watermark__ 143 | 144 | In many cases, a downstream may write faster to the RProxy than it can to 145 | the client. If the client is unable to keep up with the speed the backend server 146 | is sending, you may experience very high memory consumption. This is called a 147 | fast-writer/slow-reader effect. 148 | 149 | If the number of bytes in the output buffer to the client goes over this 150 | number, RProxy will disable reading data from the backend until all data in the 151 | output queue to the client has been sent. 152 | 153 | 154 | ## Server::Downstream Configuration 155 | 156 | Each server section contains one or more "downstreams" (in other words, a 157 | backend service). Each downstream configuration directive is named, which is 158 | referenced by a rule (discussed later). 159 | 160 | The information contained within the downstream configuration is global to a 161 | server; all virtual host and rule will use the downstream information contained 162 | within each downstream section. 163 | 164 | downstream { 165 | enabled = true 166 | addr = x.x.x.x 167 | port = nnnn 168 | connections = 4 169 | high-watermark = 0 170 | read-timeout = { 0, 0 } 171 | write-timeout = { 0, 0 } 172 | retry = { 0, 500000 } 173 | } 174 | 175 | * __NAME__ 176 | 177 | Each downstream must have a unique name. 178 | 179 | * __enabled__ 180 | 181 | If this value is set to false, RProxy will not attempt to make connections 182 | or utilize this downstream in any of the rules. 183 | 184 | * __addr__ 185 | 186 | The FQDN or IP address of this downstream server. 187 | 188 | * __port__ 189 | 190 | The listening port of the downstream server. 191 | 192 | * __connections__ 193 | 194 | The number of connections RProxy will attempt to keep available. Note that 195 | this number is multiplied by the number of threads configured for the 196 | server. For example if you have 4 threads, and 2 connections, RProxy actually 197 | maintains 8 connections. 198 | 199 | * __high-watermark__ 200 | 201 | If number of bytes in the sendQ is over the value of this number (in bytes), 202 | further reading from the client is disabled until the sendQ has been emptied. 203 | 204 | This setting assures that backend servers will not be overloaded by clients. 205 | 206 | * __read-timeout__ 207 | 208 | If no data has been read from this downstream for this many { seconds, 209 | microseconds }, the connection is terminated. 210 | 211 | * __write-timeout__ 212 | 213 | If a write request takes over { seconds, microseconds } to happen, the 214 | connection is terminated. 215 | 216 | * __retry__ 217 | 218 | If one of the downstream connections has been terminated for some reason, 219 | this is the time in { seconds, microseconds } RProxy will wait until it tries to 220 | re-establish the connection. 221 | 222 | ## Server::SSL Configuration 223 | 224 | When this configuration section is present and enabled, RProxy will treat 225 | incoming connections as SSL. 226 | 227 | ssl { 228 | enabled = false 229 | protocols-on = { ALL } 230 | protocols-off = { } 231 | cert = bleh.crt 232 | key = bleh.key 233 | ca = blah.ca 234 | capath = /capath/ 235 | ciphers = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:RC4-SHA:RC4-MD5:ECDHE-RSA-AES256-SHA:AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:DES-CBC3-SHA:AES128-SHA" 236 | verify-peer = false 237 | enforce-peer-cert = false 238 | verify-depth = 0 239 | context-timeout = 172800 240 | cache-enabled = true 241 | cache-timeout = 1024 242 | cache-size = 65535 243 | crl = {} 244 | } 245 | 246 | * __enabled__ 247 | 248 | If set to true, SSL is enabled, otherwise SSL is disabled. 249 | 250 | * __cert__ 251 | 252 | The servers SSL cert 253 | 254 | * __key__ 255 | 256 | The servers private SSL key 257 | 258 | * __ca__ 259 | A specific CA file 260 | 261 | * __capath__ 262 | Relative path to search for valid CA's 263 | 264 | * __ciphers__ 265 | 266 | Accepted ciphers 267 | 268 | * __protocols-(on|off)__ 269 | 270 | The SSL options for enabling or disabling SSL specific protocols. Options: SSLv2, SSLv3, TLSv1, or ALL 271 | 272 | * __verify-peer__ 273 | 274 | Enables SSL client peer verification 275 | 276 | * __enforce-peer-cert__ 277 | 278 | If true, a client is rejected if it does not supply a client certificate. 279 | 280 | * __cache-enabled__ 281 | 282 | Enable SSL certificate cache 283 | 284 | * __cache-size__ 285 | 286 | Maximum size of the SSL cache 287 | 288 | * __cache-timeout__ 289 | 290 | The lifetime a cert will be kept in the cache. 291 | 292 | * __context-timeout__ 293 | 294 | Timeout for (OpenSSL >= 1.0) session timeouts. 295 | 296 | ## Server::SSL::CRL Configuration 297 | 298 | A server / vhost can be configured to use a CRL list or file which can be 299 | reloaded without restarting or signals. This configuration is turned off by 300 | default. 301 | 302 | ssl { 303 | crl { 304 | file = "/path/to/crl.pem" 305 | dir = "/path/to/crldir/" 306 | reload = { 10, 0 } 307 | } 308 | } 309 | 310 | * __file__ 311 | 312 | Read from a specific file for CRLs. 313 | 314 | * __dir__ 315 | 316 | Read from a specific directory for CRLs 317 | 318 | * __reload__ 319 | 320 | A timer (in secuds, useconds) to check for changes and reload the CRL 321 | configuration if changes have been mae. 322 | 323 | ## Server::Vhost Configuration 324 | 325 | ## Server::Vhost::Rule Configuration 326 | 327 | ## Server::Logging Configuration 328 | -------------------------------------------------------------------------------- /test/smoke/smoke.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Quick smoke test that exercises data moving across rproxy. 4 | # Expected to be run at root of rproxy install tree. 5 | 6 | DOCOLOR=0 7 | USAGE="Usage: `basename $0` [-c]" 8 | 9 | # Parse command line options. 10 | while getopts hc OPT; do 11 | case "$OPT" in 12 | h) 13 | echo $USAGE 14 | exit 0 15 | ;; 16 | c) 17 | DOCOLOR=1 18 | ;; 19 | esac 20 | done 21 | 22 | if [ ${DOCOLOR} -eq 1 ] 23 | then 24 | txtred=$(tput setaf 1) # Red 25 | txtgrn=$(tput setaf 2) # Green 26 | txtylw=$(tput setaf 3) # Yellow 27 | txtrst=$(tput sgr0) # Text reset 28 | fi 29 | 30 | SUCCESS="${txtgrn}[SUCCESS]${txtrst}" 31 | FAILURE="${txtred}[FAILURE]${txtrst}" 32 | WARNING="${txtylw}[WARNING]${txtrst}" 33 | 34 | export BASE=`pwd` 35 | export TEMP=`mktemp -d smoke.XXXX` 36 | 37 | # See if we have curl 38 | CURL=`command -v curl` 39 | 40 | if [ "$CURL" = "" ] 41 | then 42 | echo "curl not found, exiting..." 43 | exit 1 44 | fi 45 | 46 | export success=0 47 | export failure=0 48 | export warning=0 49 | 50 | echo "------------- Initializing" 51 | echo "Creating temporary directory ${TEMP}." 52 | 53 | function checkcode { 54 | echo "Checking http code $1" 55 | if [ $? -eq 0 ] && [ "${code}" = "$1" ] 56 | then 57 | success=`expr $success + 1` 58 | echo "${SUCCESS}" 59 | else 60 | failure=`expr $failure + 1` 61 | echo "${FAILURE}" 62 | fi 63 | } 64 | 65 | function checkresult { 66 | echo "Checking http code $1 with regex '$2'" 67 | export data=`cat ${TEMP}/curl.out` 68 | echo $data 69 | if [ $? -eq 0 ] && [ "${code}" = "$1" ] && [[ "${data}" =~ ${2} ]] 70 | then 71 | success=`expr $success + 1` 72 | echo "${SUCCESS}" 73 | else 74 | failure=`expr $failure + 1` 75 | echo "${FAILURE}" 76 | fi 77 | rm ${TEMP}/curl.out 78 | } 79 | 80 | # Start the backend server 81 | ${BASE}/test/smoke/smoke_downstream & 82 | sleep 1 83 | 84 | # Start rproxy 85 | ${BASE}/src/rproxy ${BASE}/test/smoke/smoke_cfg.cfg & 86 | sleep 1 87 | 88 | export RP_HOST=localhost 89 | export RP_PORT_1=8081 90 | export RP_PORT_2=8082 91 | export RP_PORT_3=8083 92 | export RP_PORT_4=8084 93 | 94 | 95 | echo "" 96 | echo "------------- TEST 1: Testing simple GET on downstream." 97 | export code=`${CURL} -w "%{http_code}\n" -s -o /dev/null -X GET "http://${RP_HOST}:${RP_PORT_1}/simple/"` 98 | checkcode 200 99 | 100 | echo "" 101 | echo "------------- TEST 2: Testing simple POST on downstream." 102 | export code=`${CURL} -w "%{http_code}\n" -s -o /dev/null -X POST "http://${RP_HOST}:${RP_PORT_1}/simple/"` 103 | checkcode 200 104 | 105 | echo "" 106 | echo "------------- TEST 3: Testing simple PUT on downstream." 107 | export code=`${CURL} -w "%{http_code}\n" -s -o /dev/null -X PUT -T ./test/smoke/smoke.sh "http://${RP_HOST}:${RP_PORT_1}/simple/"` 108 | checkcode 200 109 | 110 | echo "" 111 | echo "------------- TEST 4: Testing simple DELETE on downstream." 112 | export code=`${CURL} -w "%{http_code}\n" -s -o /dev/null -X DELETE "http://${RP_HOST}:${RP_PORT_1}/simple/"` 113 | checkcode 200 114 | 115 | echo "" 116 | echo "------------- TEST 5: Testing busy on downstream." 117 | export code=`${CURL} -w "%{http_code}\n" -s -o /dev/null -X GET "http://${RP_HOST}:${RP_PORT_1}/busy/"` 118 | checkcode 503 119 | 120 | echo "" 121 | echo "------------- TEST 6: Testing not found on downstream." 122 | export code=`${CURL} -w "%{http_code}\n" -s -o /dev/null -X GET "http://${RP_HOST}:${RP_PORT_1}/notfound/"` 123 | checkcode 404 124 | 125 | echo "" 126 | echo "------------- TEST 7: Testing non-recognized uri (not configured in rproxy)." 127 | export code=`${CURL} -w "%{http_code}\n" -s -o /dev/null -X GET "http://${RP_HOST}:${RP_PORT_1}/nonexistent/"` 128 | checkcode 404 129 | 130 | echo "" 131 | echo "------------- TEST 8: Testing rewritten uri on downstream" 132 | export code=`${CURL} -w "%{http_code}\n" -s -o /dev/null -X GET "http://${RP_HOST}:${RP_PORT_1}/rewritten/"` 133 | checkcode 200 134 | 135 | echo "" 136 | echo "------------- TEST 9: Testing rewritten uri with bad downstream uri." 137 | export code=`${CURL} -w "%{http_code}\n" -s -o /dev/null -X GET "http://${RP_HOST}:${RP_PORT_1}/badrewrite/"` 138 | checkcode 404 139 | 140 | echo "" 141 | echo "------------- TEST 10: Testing GET with data on downstream." 142 | export code=`${CURL} -w "%{http_code}\n" -s -o ${TEMP}/curl.out -X GET "http://${RP_HOST}:${RP_PORT_1}/data/"` 143 | checkresult 200 'SUCCESS' 144 | 145 | echo "" 146 | echo "------------- TEST 11: Testing x-forwarded-for header." 147 | export code=`${CURL} -w "%{http_code}\n" -s -o ${TEMP}/curl.out -X GET "http://${RP_HOST}:${RP_PORT_1}/forwarded/"` 148 | checkresult 200 '127.0.0.1' 149 | 150 | echo "" 151 | echo "------------- TEST 12: Testing simple SSL enabled GET on downstream (Server-side SSL)." 152 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out -X GET "https://${RP_HOST}:${RP_PORT_2}/simple/"` 153 | checkcode 200 154 | 155 | echo "" 156 | echo "------------- TEST 13: Testing simple SSL enabled GET on downstream (Two-way SSL)." 157 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/simple/"` 158 | checkcode 200 159 | 160 | echo "" 161 | echo "------------- TEST 14: Testing x-ssl-subject header." 162 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/subject/"` 163 | checkresult 200 '/CN=Client' 164 | 165 | echo "" 166 | echo "------------- TEST 15: Testing x-ssl-issuer header." 167 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/issuer/"` 168 | checkresult 200 '/CN=CA' 169 | 170 | echo "" 171 | echo "------------- TEST 16: Testing x-ssl-notbefore header." 172 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/notbefore/"` 173 | checkresult 200 'Feb 4 16:52:42 2012 GMT' 174 | 175 | echo "" 176 | echo "------------- TEST 17: Testing x-ssl-notafter header." 177 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/notafter/"` 178 | checkresult 200 'Feb 1 16:52:42 2022 GMT' 179 | 180 | echo "" 181 | echo "------------- TEST 18: Testing x-ssl-serial header." 182 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/serial/"` 183 | checkresult 200 '01' 184 | 185 | echo "" 186 | echo "------------- TEST 19: Testing x-ssl-cipher header." 187 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/cipher/"` 188 | checkresult 200 'RC4-SHA' 189 | 190 | echo "" 191 | echo "------------- TEST 20: Testing x-ssl-certificate header." 192 | echo "SKIPPED: Enable when evhtp supports parsing multi-line headers." 193 | echo ${WARNING} 194 | warning=`expr $warning + 1` 195 | # export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/rproxy/client.key --cert ${BASE}/test/rproxy/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/certificate/"` 196 | # checkresult 200 '' 197 | 198 | echo "" 199 | echo "------------- TEST 21: Testing user-agent header." 200 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/useragent/"` 201 | checkresult 200 "^curl.*" 202 | 203 | echo "" 204 | echo "------------- TEST 22: Testing host header." 205 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/host/"` 206 | checkresult 200 "${RP_HOST}:${RP_PORT_3}" 207 | 208 | echo "" 209 | echo "------------- TEST 23: Testing accept header." 210 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -H "Accept: text/plain" -X GET "https://${RP_HOST}:${RP_PORT_3}/accept/"` 211 | checkresult 200 'text/plain' 212 | 213 | echo "" 214 | echo "------------- TEST 24: Testing x509 extension header." 215 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/extension/"` 216 | checkresult 200 'PUT_TEXT_HERE' 217 | 218 | echo "" 219 | echo "------------- TEST 25: Testing a slow backend connection." 220 | export code=`${CURL} -w "%{http_code}\n" -s -o ${TEMP}/curl.out -X GET "http://${RP_HOST}:${RP_PORT_1}/slowdata/"` 221 | checkresult 200 "0000000000" 222 | 223 | echo "" 224 | echo "------------- TEST 26: Testing a slow backend connection (SSL)." 225 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/slowdata/"` 226 | checkresult 200 "0000000000" 227 | 228 | echo "" 229 | echo "------------- TEST 27: Testing an invalid chunk length." 230 | export code=`${CURL} -w "%{http_code}\n" -s -o ${TEMP}/curl.out -X GET "http://${RP_HOST}:${RP_PORT_1}/badchunklength/"` 231 | checkresult 200 "SUCCESS" 232 | 233 | echo "" 234 | echo "------------- TEST 28: Testing an invalid chunk response (SSL)." 235 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/badchunklength/"` 236 | checkresult 200 "SUCCESS" 237 | 238 | echo "" 239 | echo "------------- TEST 29: Testing an invalid chunk transfer." 240 | export code=`${CURL} -w "%{http_code}\n" -s -o ${TEMP}/curl.out -X GET "http://${RP_HOST}:${RP_PORT_1}/badchunktransfer/"` 241 | checkresult 200 "DATA" 242 | 243 | echo "" 244 | echo "------------- TEST 30: Testing an invalid chunk transfer (SSL)." 245 | export code=`${CURL} -w "%{http_code}\n" -k -s -o ${TEMP}/curl.out --key ${BASE}/test/smoke/client.key --cert ${BASE}/test/smoke/client.crt -X GET "https://${RP_HOST}:${RP_PORT_3}/badchunktransfer/"` 246 | checkresult 200 "DATA" 247 | 248 | echo "" 249 | echo "------------- TEST 30: testing round-robin based load balancing." 250 | echo "Making 8 requests, which should result in 4 responses downstream 1, and 4 responses from downstream 2" 251 | for i in {1..8}; do 252 | ${CURL} -s -X GET http://${RP_HOST}:${RP_PORT_4}/test_rr/ >> ${TEMP}/rr.out 253 | done 254 | 255 | one_count=`grep one ${TEMP}/rr.out | wc -l` 256 | two_count=`grep two ${TEMP}/rr.out | wc -l` 257 | echo "Downstream 1 count: ${one_count}, Downstream 2 count: ${two_count}" 258 | 259 | if [ "${one_count}" -eq 4 ] && [ "${two_count}" -eq 4 ] 260 | then 261 | success=`expr $success + 1` 262 | echo "${SUCCESS}" 263 | else 264 | failure=`expr $failure + 1` 265 | echo "${FAILURE}" 266 | fi 267 | 268 | echo "" 269 | echo "------------- Killing spawned processes." 270 | for job in `jobs -p` 271 | do 272 | kill $job 273 | done 274 | 275 | echo "" 276 | echo "------------- SUMMARY" 277 | echo "${txtgrn}Success: ${success}${txtrst}" 278 | echo "${txtred}Failure: ${failure}${txtrst}" 279 | echo "${txtylw}Warning: ${warning}${txtrst}" 280 | 281 | exit ${failure} 282 | -------------------------------------------------------------------------------- /test/unit/tinytest.c: -------------------------------------------------------------------------------- 1 | /* tinytest.c -- Copyright 2009-2011 Nick Mathewson 2 | * 3 | * Redistribution and use in source and binary forms, with or without 4 | * modification, are permitted provided that the following conditions 5 | * are met: 6 | * 1. Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * 2. Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * 3. The name of the author may not be used to endorse or promote products 12 | * derived from this software without specific prior written permission. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #ifdef TINYTEST_LOCAL 32 | #include "tinytest_local.h" 33 | #endif 34 | 35 | #ifdef _WIN32 36 | #include 37 | #else 38 | #include 39 | #include 40 | #include 41 | #endif 42 | 43 | #ifndef __GNUC__ 44 | #define __attribute__ (x) 45 | #endif 46 | 47 | #include "tinytest.h" 48 | #include "tinytest_macros.h" 49 | 50 | #define LONGEST_TEST_NAME 16384 51 | 52 | static int in_tinytest_main = 0; /**< true if we're in tinytest_main().*/ 53 | static int n_ok = 0; /**< Number of tests that have passed */ 54 | static int n_bad = 0; /**< Number of tests that have failed. */ 55 | static int n_skipped = 0; /**< Number of tests that have been skipped. */ 56 | 57 | static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/ 58 | static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */ 59 | static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */ 60 | const char * verbosity_flag = ""; 61 | 62 | enum outcome { SKIP=2, OK=1, FAIL=0 }; 63 | static enum outcome cur_test_outcome = 0; 64 | const char * cur_test_prefix = NULL; /**< prefix of the current test group */ 65 | /** Name of the current test, if we haven't logged is yet. Used for --quiet */ 66 | const char * cur_test_name = NULL; 67 | 68 | #ifdef _WIN32 69 | /* Copy of argv[0] for win32. */ 70 | static char commandname[MAX_PATH + 1]; 71 | #endif 72 | 73 | static void usage(struct testgroup_t * groups, int list_groups) 74 | __attribute__((noreturn)); 75 | 76 | static enum outcome 77 | _testcase_run_bare(const struct testcase_t * testcase) { 78 | void * env = NULL; 79 | int outcome; 80 | 81 | if (testcase->setup) { 82 | env = testcase->setup->setup_fn(testcase); 83 | if (!env) { 84 | return FAIL; 85 | } else if (env == (void*)TT_SKIP) { 86 | return SKIP; 87 | } 88 | } 89 | 90 | cur_test_outcome = OK; 91 | testcase->fn(env); 92 | outcome = cur_test_outcome; 93 | 94 | if (testcase->setup) { 95 | if (testcase->setup->cleanup_fn(testcase, env) == 0) { 96 | outcome = FAIL; 97 | } 98 | } 99 | 100 | return outcome; 101 | } 102 | 103 | #define MAGIC_EXITCODE 42 104 | 105 | static enum outcome 106 | _testcase_run_forked(const struct testgroup_t * group, 107 | const struct testcase_t * testcase) { 108 | #ifdef _WIN32 109 | /* Fork? On Win32? How primitive! We'll do what the smart kids do: 110 | * we'll invoke our own exe (whose name we recall from the command 111 | * line) with a command line that tells it to run just the test we 112 | * want, and this time without forking. 113 | * 114 | * (No, threads aren't an option. The whole point of forking is to 115 | * share no state between tests.) 116 | */ 117 | int ok; 118 | char buffer[LONGEST_TEST_NAME + 256]; 119 | STARTUPINFOA si; 120 | PROCESS_INFORMATION info; 121 | DWORD exitcode; 122 | 123 | if (!in_tinytest_main) { 124 | printf("\nERROR. On Windows, _testcase_run_forked must be" 125 | " called from within tinytest_main.\n"); 126 | abort(); 127 | } 128 | if (opt_verbosity > 0) { 129 | printf("[forking] "); 130 | } 131 | 132 | snprintf(buffer, sizeof(buffer), "%s --RUNNING-FORKED %s %s%s", 133 | commandname, verbosity_flag, group->prefix, testcase->name); 134 | 135 | memset(&si, 0, sizeof(si)); 136 | memset(&info, 0, sizeof(info)); 137 | si.cb = sizeof(si); 138 | 139 | ok = CreateProcessA(commandname, buffer, NULL, NULL, 0, 140 | 0, NULL, NULL, &si, &info); 141 | if (!ok) { 142 | printf("CreateProcess failed!\n"); 143 | return 0; 144 | } 145 | WaitForSingleObject(info.hProcess, INFINITE); 146 | GetExitCodeProcess(info.hProcess, &exitcode); 147 | CloseHandle(info.hProcess); 148 | CloseHandle(info.hThread); 149 | if (exitcode == 0) { 150 | return OK; 151 | } else if (exitcode == MAGIC_EXITCODE) { 152 | return SKIP; 153 | } else{ 154 | return FAIL; 155 | } 156 | #else 157 | int outcome_pipe[2]; 158 | pid_t pid; 159 | (void)group; 160 | 161 | if (pipe(outcome_pipe)) { 162 | perror("opening pipe"); 163 | } 164 | 165 | if (opt_verbosity > 0) { 166 | printf("[forking] "); 167 | } 168 | pid = fork(); 169 | if (!pid) { 170 | /* child. */ 171 | int test_r, write_r; 172 | char b[1]; 173 | close(outcome_pipe[0]); 174 | test_r = _testcase_run_bare(testcase); 175 | assert(0 <= (int)test_r && (int)test_r <= 2); 176 | b[0] = "NYS"[test_r]; 177 | write_r = (int)write(outcome_pipe[1], b, 1); 178 | if (write_r != 1) { 179 | perror("write outcome to pipe"); 180 | exit(1); 181 | } 182 | exit(0); 183 | return FAIL; /* unreachable */ 184 | } else { 185 | /* parent */ 186 | int status, r; 187 | char b[1]; 188 | /* Close this now, so that if the other side closes it, 189 | * our read fails. */ 190 | close(outcome_pipe[1]); 191 | r = (int)read(outcome_pipe[0], b, 1); 192 | if (r == 0) { 193 | printf("[Lost connection!] "); 194 | return 0; 195 | } else if (r != 1) { 196 | perror("read outcome from pipe"); 197 | } 198 | waitpid(pid, &status, 0); 199 | close(outcome_pipe[0]); 200 | return b[0] == 'Y' ? OK : (b[0] == 'S' ? SKIP : FAIL); 201 | } 202 | #endif 203 | } /* _testcase_run_forked */ 204 | 205 | int 206 | testcase_run_one(const struct testgroup_t * group, 207 | const struct testcase_t * testcase) { 208 | enum outcome outcome; 209 | 210 | if (testcase->flags & TT_SKIP) { 211 | if (opt_verbosity > 0) { 212 | printf("%s%s: SKIPPED\n", 213 | group->prefix, testcase->name); 214 | } 215 | ++n_skipped; 216 | return SKIP; 217 | } 218 | 219 | if (opt_verbosity > 0 && !opt_forked) { 220 | printf("%s%s: ", group->prefix, testcase->name); 221 | } else { 222 | if (opt_verbosity == 0) { 223 | printf("."); 224 | } 225 | cur_test_prefix = group->prefix; 226 | cur_test_name = testcase->name; 227 | } 228 | 229 | if ((testcase->flags & TT_FORK) && !(opt_forked || opt_nofork)) { 230 | outcome = _testcase_run_forked(group, testcase); 231 | } else { 232 | outcome = _testcase_run_bare(testcase); 233 | } 234 | 235 | if (outcome == OK) { 236 | ++n_ok; 237 | if (opt_verbosity > 0 && !opt_forked) { 238 | puts(opt_verbosity == 1 ? "OK" : ""); 239 | } 240 | } else if (outcome == SKIP) { 241 | ++n_skipped; 242 | if (opt_verbosity > 0 && !opt_forked) { 243 | puts("SKIPPED"); 244 | } 245 | } else { 246 | ++n_bad; 247 | if (!opt_forked) { 248 | printf("\n [%s FAILED]\n", testcase->name); 249 | } 250 | } 251 | 252 | if (opt_forked) { 253 | exit(outcome == OK ? 0 : (outcome == SKIP ? MAGIC_EXITCODE : 1)); 254 | return 1; /* unreachable */ 255 | } else { 256 | return (int)outcome; 257 | } 258 | } /* testcase_run_one */ 259 | 260 | int 261 | _tinytest_set_flag(struct testgroup_t * groups, const char * arg, unsigned long flag) { 262 | int i, j; 263 | size_t length = LONGEST_TEST_NAME; 264 | char fullname[LONGEST_TEST_NAME]; 265 | int found = 0; 266 | 267 | if (strstr(arg, "..")) { 268 | length = strstr(arg, "..") - arg; 269 | } 270 | for (i = 0; groups[i].prefix; ++i) { 271 | for (j = 0; groups[i].cases[j].name; ++j) { 272 | snprintf(fullname, sizeof(fullname), "%s%s", 273 | groups[i].prefix, groups[i].cases[j].name); 274 | if (!flag) { /* Hack! */ 275 | printf(" %s\n", fullname); 276 | } 277 | if (!strncmp(fullname, arg, length)) { 278 | groups[i].cases[j].flags |= flag; 279 | ++found; 280 | } 281 | } 282 | } 283 | return found; 284 | } 285 | 286 | static void 287 | usage(struct testgroup_t * groups, int list_groups) { 288 | puts("Options are: [--verbose|--quiet|--terse] [--no-fork]"); 289 | puts(" Specify tests by name, or using a prefix ending with '..'"); 290 | puts(" To skip a test, list give its name prefixed with a colon."); 291 | puts(" Use --list-tests for a list of tests."); 292 | if (list_groups) { 293 | puts("Known tests are:"); 294 | _tinytest_set_flag(groups, "..", 0); 295 | } 296 | exit(0); 297 | } 298 | 299 | int 300 | tinytest_main(int c, const char ** v, struct testgroup_t * groups) { 301 | int i, j, n = 0; 302 | 303 | #ifdef _WIN32 304 | const char * sp = strrchr(v[0], '.'); 305 | const char * extension = ""; 306 | if (!sp || stricmp(sp, ".exe")) { 307 | extension = ".exe"; /* Add an exe so CreateProcess will work */ 308 | } 309 | snprintf(commandname, sizeof(commandname), "%s%s", v[0], extension); 310 | commandname[MAX_PATH] = '\0'; 311 | #endif 312 | for (i = 1; i < c; ++i) { 313 | if (v[i][0] == '-') { 314 | if (!strcmp(v[i], "--RUNNING-FORKED")) { 315 | opt_forked = 1; 316 | } else if (!strcmp(v[i], "--no-fork")) { 317 | opt_nofork = 1; 318 | } else if (!strcmp(v[i], "--quiet")) { 319 | opt_verbosity = -1; 320 | verbosity_flag = "--quiet"; 321 | } else if (!strcmp(v[i], "--verbose")) { 322 | opt_verbosity = 2; 323 | verbosity_flag = "--verbose"; 324 | } else if (!strcmp(v[i], "--terse")) { 325 | opt_verbosity = 0; 326 | verbosity_flag = "--terse"; 327 | } else if (!strcmp(v[i], "--help")) { 328 | usage(groups, 0); 329 | } else if (!strcmp(v[i], "--list-tests")) { 330 | usage(groups, 1); 331 | } else { 332 | printf("Unknown option %s. Try --help\n", v[i]); 333 | return -1; 334 | } 335 | } else { 336 | const char * test = v[i]; 337 | int flag = _TT_ENABLED; 338 | if (test[0] == ':') { 339 | ++test; 340 | flag = TT_SKIP; 341 | } else { 342 | ++n; 343 | } 344 | if (!_tinytest_set_flag(groups, test, flag)) { 345 | printf("No such test as %s!\n", v[i]); 346 | return -1; 347 | } 348 | } 349 | } 350 | if (!n) { 351 | _tinytest_set_flag(groups, "..", _TT_ENABLED); 352 | } 353 | 354 | setvbuf(stdout, NULL, _IONBF, 0); 355 | 356 | ++in_tinytest_main; 357 | for (i = 0; groups[i].prefix; ++i) { 358 | for (j = 0; groups[i].cases[j].name; ++j) { 359 | if (groups[i].cases[j].flags & _TT_ENABLED) { 360 | testcase_run_one(&groups[i], 361 | &groups[i].cases[j]); 362 | } 363 | } 364 | } 365 | 366 | --in_tinytest_main; 367 | 368 | if (opt_verbosity == 0) { 369 | puts(""); 370 | } 371 | 372 | if (n_bad) { 373 | printf("%d/%d TESTS FAILED. (%d skipped)\n", n_bad, 374 | n_bad + n_ok, n_skipped); 375 | } else if (opt_verbosity >= 1) { 376 | printf("%d tests ok. (%d skipped)\n", n_ok, n_skipped); 377 | } 378 | 379 | return (n_bad == 0) ? 0 : 1; 380 | } /* tinytest_main */ 381 | 382 | int 383 | _tinytest_get_verbosity(void) { 384 | return opt_verbosity; 385 | } 386 | 387 | void 388 | _tinytest_set_test_failed(void) { 389 | if (opt_verbosity <= 0 && cur_test_name) { 390 | if (opt_verbosity == 0) { 391 | puts(""); 392 | } 393 | printf("%s%s: ", cur_test_prefix, cur_test_name); 394 | cur_test_name = NULL; 395 | } 396 | cur_test_outcome = 0; 397 | } 398 | 399 | void 400 | _tinytest_set_test_skipped(void) { 401 | if (cur_test_outcome == OK) { 402 | cur_test_outcome = SKIP; 403 | } 404 | } 405 | 406 | -------------------------------------------------------------------------------- /src/logger.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2012] [Mandiant, inc] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "rproxy.h" 25 | 26 | struct { 27 | logger_argtype type; 28 | const char * str; 29 | } logger_argtype_strmap[] = { 30 | { logger_argtype_src, "{SRC}" }, 31 | { logger_argtype_proxy, "{PROXY}" }, 32 | { logger_argtype_ds_sport, "{DS_SPORT}" }, 33 | { logger_argtype_us_sport, "{US_SPORT}" }, 34 | { logger_argtype_ts, "{TS}" }, 35 | { logger_argtype_ua, "{UA}" }, 36 | { logger_argtype_meth, "{METH}" }, 37 | { logger_argtype_uri, "{URI}" }, 38 | { logger_argtype_proto, "{PROTO}" }, 39 | { logger_argtype_status, "{STATUS}" }, 40 | { logger_argtype_ref, "{REF}" }, 41 | { logger_argtype_host, "{HOST}" }, 42 | { logger_argtype_us_hdrval, "{US_HDR}:" }, 43 | { logger_argtype_ds_hdrval, "{DS_HDR}:" }, 44 | { logger_argtype_rule, "{RULE}" }, 45 | { logger_argtype_printable, NULL }, 46 | }; 47 | 48 | void 49 | logger_log_request_tostr(logger_t * logger, request_t * request, evbuf_t * buf) { 50 | logger_arg_t * arg; 51 | evhtp_request_t * upstream_r; 52 | evhtp_connection_t * upstream_c; 53 | downstream_c_t * downstream_c; 54 | downstream_t * downstream; 55 | struct sockaddr_in * sin; 56 | int sres; 57 | char tmp[256]; 58 | 59 | if (!logger) { 60 | return; 61 | } 62 | 63 | if (!request) { 64 | return; 65 | } 66 | 67 | if (!buf) { 68 | return; 69 | } 70 | 71 | if (!(upstream_r = request->upstream_request)) { 72 | return; 73 | } 74 | 75 | if (!(upstream_c = upstream_r->conn)) { 76 | return; 77 | } 78 | 79 | if (!(downstream_c = request->downstream_conn)) { 80 | return; 81 | } 82 | 83 | if (!(downstream = downstream_c->parent)) { 84 | return; 85 | } 86 | 87 | TAILQ_FOREACH(arg, &logger->args, next) { 88 | switch (arg->type) { 89 | case logger_argtype_rule: 90 | if (request->rule && request->rule->config && request->rule->config->name) { 91 | evbuffer_add_printf(buf, "%s", request->rule->config->name); 92 | } else { 93 | evbuffer_add(buf, "-", 1); 94 | } 95 | break; 96 | case logger_argtype_us_hdrval: 97 | if (arg->data == NULL) { 98 | evbuffer_add(buf, "-", 1); 99 | } else { 100 | const char * hdr_val; 101 | 102 | hdr_val = evhtp_header_find(upstream_r->headers_in, (const char *)arg->data); 103 | 104 | if (hdr_val == NULL) { 105 | evbuffer_add(buf, "-", 1); 106 | } else { 107 | evbuffer_add(buf, hdr_val, strlen(hdr_val)); 108 | } 109 | } 110 | break; 111 | case logger_argtype_ds_hdrval: 112 | if (arg->data == NULL) { 113 | evbuffer_add(buf, "-", 1); 114 | } else { 115 | const char * hdr_val; 116 | 117 | hdr_val = evhtp_header_find(upstream_r->headers_out, (const char *)arg->data); 118 | 119 | if (hdr_val == NULL) { 120 | evbuffer_add(buf, "-", 1); 121 | } else { 122 | evbuffer_add(buf, hdr_val, strlen(hdr_val)); 123 | } 124 | } 125 | break; 126 | case logger_argtype_ds_sport: 127 | evbuffer_add_printf(buf, "%d", downstream_c->sport); 128 | break; 129 | case logger_argtype_us_sport: 130 | sin = (struct sockaddr_in *)upstream_c->saddr; 131 | 132 | evbuffer_add_printf(buf, "%d", ntohs(sin->sin_port)); 133 | break; 134 | case logger_argtype_src: 135 | /* log the upstreams IP address */ 136 | sin = (struct sockaddr_in *)upstream_c->saddr; 137 | 138 | evutil_inet_ntop(AF_INET, &sin->sin_addr, tmp, sizeof(tmp)); 139 | evbuffer_add(buf, tmp, strlen(tmp)); 140 | break; 141 | case logger_argtype_proxy: 142 | /* log the downstreams host and port information */ 143 | sres = snprintf(tmp, sizeof(tmp), "%s:%d", 144 | downstream->config->host, 145 | downstream->config->port); 146 | 147 | if (sres >= sizeof(tmp) || sres < 0) { 148 | /* overflow condition, shouldn't ever get here */ 149 | fprintf(stderr, "[CRIT] overflow in log_request!\n"); 150 | exit(EXIT_FAILURE); 151 | } 152 | 153 | evbuffer_add(buf, tmp, strlen(tmp)); 154 | break; 155 | case logger_argtype_ts: 156 | { 157 | /* log an RFC compliant HTTP log timestamp */ 158 | time_t t; 159 | struct tm * tmtmp; 160 | 161 | t = time(NULL); 162 | tmtmp = localtime(&t); 163 | 164 | strftime(tmp, sizeof(tmp), "%d/%b/%Y:%X %z", tmtmp); 165 | evbuffer_add(buf, tmp, strlen(tmp)); 166 | } 167 | break; 168 | case logger_argtype_meth: 169 | { 170 | /* log the method of the request */ 171 | const char * methodstr = htparser_get_methodstr(upstream_c->parser); 172 | 173 | if (methodstr == NULL) { 174 | methodstr = "-"; 175 | } 176 | 177 | evbuffer_add(buf, methodstr, strlen(methodstr)); 178 | } 179 | break; 180 | case logger_argtype_uri: 181 | /* log the URI requested by the upstream */ 182 | if (upstream_r->uri && upstream_r->uri->path && 183 | upstream_r->uri->path->full) { 184 | evbuffer_add(buf, upstream_r->uri->path->full, 185 | strlen(upstream_r->uri->path->full)); 186 | } else { 187 | evbuffer_add(buf, "-", 1); 188 | } 189 | break; 190 | case logger_argtype_proto: 191 | sres = snprintf(tmp, sizeof(tmp), "HTTP/%d.%d", 192 | htparser_get_major(upstream_c->parser), 193 | htparser_get_minor(upstream_c->parser)); 194 | 195 | if (sres >= sizeof(tmp) || sres < 0) { 196 | /* overflow condition, shouldn't get here */ 197 | fprintf(stderr, "[CRIT] overflow in log_request!\n"); 198 | exit(EXIT_FAILURE); 199 | } 200 | 201 | evbuffer_add(buf, tmp, strlen(tmp)); 202 | break; 203 | case logger_argtype_status: 204 | sres = snprintf(tmp, sizeof(tmp), "%d", 205 | htparser_get_status(request->parser)); 206 | 207 | if (sres >= sizeof(tmp) || sres < 0) { 208 | /* overflow condition, shouldn't get here */ 209 | fprintf(stderr, "[CRIT] overflow in log_request!\n"); 210 | exit(EXIT_FAILURE); 211 | } 212 | 213 | evbuffer_add(buf, tmp, strlen(tmp)); 214 | break; 215 | case logger_argtype_ref: 216 | { 217 | char * ref_str; 218 | 219 | ref_str = (char *)evhtp_header_find(upstream_r->headers_in, "referrer"); 220 | 221 | if (!ref_str) { 222 | ref_str = "-"; 223 | } 224 | 225 | evbuffer_add(buf, ref_str, strlen(ref_str)); 226 | } 227 | break; 228 | case logger_argtype_ua: 229 | { 230 | char * ua_str; 231 | 232 | ua_str = (char *)evhtp_header_find(upstream_r->headers_in, "user-agent"); 233 | 234 | if (!ua_str) { 235 | ua_str = "-"; 236 | } 237 | 238 | evbuffer_add(buf, ua_str, strlen(ua_str)); 239 | } 240 | break; 241 | case logger_argtype_host: 242 | { 243 | char * h_str; 244 | 245 | h_str = (char *)evhtp_header_find(upstream_r->headers_in, "host"); 246 | 247 | if (!h_str) { 248 | h_str = "-"; 249 | } 250 | 251 | evbuffer_add(buf, h_str, strlen(h_str)); 252 | } 253 | break; 254 | case logger_argtype_printable: 255 | evbuffer_add(buf, arg->data, strlen(arg->data)); 256 | break; 257 | } /* switch */ 258 | } 259 | } /* logger_log_request_tostr */ 260 | 261 | void 262 | logger_log(logger_t * logger, lzlog_level level, char * fmt, ...) { 263 | va_list ap; 264 | 265 | 266 | if (!logger) { 267 | return; 268 | } 269 | 270 | va_start(ap, fmt); 271 | { 272 | lzlog_vprintf(logger->log, level, fmt, ap); 273 | } 274 | va_end(ap); 275 | } 276 | 277 | void 278 | logger_log_request_error(logger_t * logger, request_t * request, char * fmt, ...) { 279 | va_list ap; 280 | evbuf_t * buf; 281 | 282 | if (!logger || !request) { 283 | return; 284 | } 285 | 286 | buf = evbuffer_new(); 287 | assert(buf != NULL); 288 | 289 | logger_log_request_tostr(logger, request, buf); 290 | evbuffer_add(buf, ", ", 2); 291 | 292 | va_start(ap, fmt); 293 | { 294 | evbuffer_add_vprintf(buf, fmt, ap); 295 | } 296 | va_end(ap); 297 | 298 | lzlog_write(logger->log, lzlog_err, evbuffer_pullup(buf, -1)); 299 | evbuffer_free(buf); 300 | } 301 | 302 | void 303 | logger_log_request(logger_t * logger, request_t * request) { 304 | evbuf_t * buf; 305 | 306 | if (!logger) { 307 | return; 308 | } 309 | 310 | buf = evbuffer_new(); 311 | assert(buf != NULL); 312 | 313 | logger_log_request_tostr(logger, request, buf); 314 | evbuffer_add(buf, "\0", 1); 315 | 316 | lzlog_write(logger->log, 1, "%s", evbuffer_pullup(buf, -1)); 317 | 318 | evbuffer_free(buf); 319 | } /* logger_log_request */ 320 | 321 | logger_argtype 322 | logger_argtype_fromstr(const char * str, int * arglen) { 323 | int i; 324 | 325 | for (i = 0; logger_argtype_strmap[i].str; i++) { 326 | const char * s = logger_argtype_strmap[i].str; 327 | logger_argtype t = logger_argtype_strmap[i].type; 328 | 329 | if (!strncasecmp(s, str, strlen(s))) { 330 | *arglen = strlen(s); 331 | 332 | return t; 333 | } 334 | } 335 | 336 | return -1; 337 | } 338 | 339 | logger_arg_t * 340 | logger_arg_new(logger_argtype type) { 341 | logger_arg_t * a; 342 | 343 | if (type <= logger_argtype_nil) { 344 | return NULL; 345 | } 346 | 347 | if (!(a = calloc(sizeof(logger_arg_t), 1))) { 348 | return NULL; 349 | } 350 | 351 | a->type = type; 352 | 353 | return a; 354 | } 355 | 356 | int 357 | logger_arg_addchar(logger_arg_t * arg, const char c) { 358 | if (arg == NULL) { 359 | return -1; 360 | } 361 | 362 | if (arg->data == NULL) { 363 | if (!(arg->data = calloc(8, 1))) { 364 | return -1; 365 | } 366 | 367 | arg->len = 8; 368 | arg->used = 0; 369 | } 370 | 371 | if ((arg->used + 2) >= arg->len) { 372 | if (!(arg->data = realloc(arg->data, arg->len + 16))) { 373 | return -1; 374 | } 375 | 376 | arg->len += 16; 377 | } 378 | 379 | arg->data[arg->used++] = c; 380 | arg->data[arg->used] = '\0'; 381 | 382 | return 0; 383 | } 384 | 385 | logger_t * 386 | logger_init(logger_cfg_t * c, int opts) { 387 | logger_t * logger; 388 | const char * strp; 389 | 390 | if (c == NULL) { 391 | return NULL; 392 | } 393 | 394 | if (!(logger = calloc(sizeof(logger_t), 1))) { 395 | return NULL; 396 | } 397 | 398 | logger->config = c; 399 | 400 | TAILQ_INIT(&logger->args); 401 | 402 | switch (c->type) { 403 | case logger_type_file: 404 | logger->log = lzlog_file_new(c->path, "RProxy", opts | LZLOG_OPT_NEWLINE); 405 | break; 406 | case logger_type_syslog: 407 | logger->log = lzlog_syslog_new("RProxy", opts, c->facility); 408 | break; 409 | default: 410 | free(logger); 411 | return NULL; 412 | } 413 | 414 | lzlog_set_level(logger->log, c->level); 415 | 416 | if (c->format == NULL) { 417 | return logger; 418 | } 419 | 420 | for (strp = c->format; *strp != '\0'; strp++) { 421 | logger_arg_t * larg = NULL; 422 | int insert = 0; 423 | 424 | if (*strp == '{') { 425 | int arglen; 426 | logger_argtype type; 427 | 428 | if ((type = logger_argtype_fromstr(strp, &arglen)) < 0) { 429 | return NULL; 430 | } 431 | 432 | if (!(larg = logger_arg_new(type))) { 433 | return NULL; 434 | } 435 | 436 | strp += arglen - 1; 437 | insert = 1; 438 | 439 | if (type == logger_argtype_us_hdrval || type == logger_argtype_ds_hdrval) { 440 | if (*strp++ != ':') { 441 | printf("Log format error\n"); 442 | return NULL; 443 | } 444 | 445 | if (*strp++ != '\'') { 446 | printf("Log format error\n"); 447 | return NULL; 448 | } 449 | 450 | while (1) { 451 | if (*strp != '\'') { 452 | logger_arg_addchar(larg, *strp++); 453 | } else { 454 | break; 455 | } 456 | } 457 | } 458 | } else { 459 | if (TAILQ_EMPTY(&logger->args)) { 460 | larg = NULL; 461 | } else { 462 | larg = TAILQ_LAST(&logger->args, logger_args); 463 | } 464 | 465 | if (!larg || larg->type != logger_argtype_printable) { 466 | if (!(larg = logger_arg_new(logger_argtype_printable))) { 467 | return NULL; 468 | } 469 | 470 | insert = 1; 471 | } 472 | 473 | if (logger_arg_addchar(larg, *strp) != 0) { 474 | return NULL; 475 | } 476 | } 477 | 478 | if (insert > 0) { 479 | TAILQ_INSERT_TAIL(&logger->args, larg, next); 480 | } 481 | } 482 | 483 | return logger; 484 | } /* logger_init */ 485 | 486 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | v2.0.17 2 | o Fixes for redirection and client socket handling. (0f09bfe Mark Ellzey) 3 | o Extra logging for redirection responses. (05d9cb8 Mark Ellzey) 4 | o Append X-Redirected-Via for redirected requests (06613e7 Mark Ellzey) 5 | o redirect cleanup and documentation (f81fa6e Mark Ellzey) 6 | 7 | v2.0.16 8 | o Fix potential null deref with method logging. (d26ca8f Mark Ellzey) 9 | o Set method str to '-' if method is unknown. (f573411 Mark Ellzey) 10 | o Ability to turn off nagle on client/listener/downstream sockets. (0b1b4db Mark Ellzey) 11 | o Bugfix for methstr assignment. (fc5a326 Mark Ellzey) 12 | 13 | v2.0.15 14 | o Initial CRL verification support. (d367397 Mark Ellzey) 15 | o OSX returns struct timespec for stat while linux returns a time_t. This was fixed. (781e3ca Mark Ellzey) 16 | o Actually add the crl config to ssl {} (ee5f428 Mark Ellzey) 17 | o SSL proto opts were not being placed in the config, this got removed somehow, so I readded it. (6f9add7 Mark Ellzey) 18 | o Add logging, fix leak. (04dc43c Sean Cunningham) 19 | o Added locking around CRL ent checks. (7266970 Mark Ellzey) 20 | o Fix pthread_mutex_init() call. (b8aea13 Mark Ellzey) 21 | o Apply vhost args from main evhtp if ssl not local (18c6cc4 Mark Ellzey) 22 | 23 | v2.0.14 24 | o Fix bug where connection->request was not being set to NULL on error. (f7da115 Mark Ellzey) 25 | o use evhtp_disable_100_continue for each new evhtp. (7f0d2fd Mark Ellzey) 26 | o Fix bug where evhtp_disable_100_continue was being called on main evhtp instead of the vhost. (450d715 Mark Ellzey) 27 | o Deal with downstream -> rproxy -> upstream 1xx response. (c4d923c Mark Ellzey) 28 | 29 | v2.0.13 30 | o during cfg parsing, stat SSL configuration files to ensure that they exist (51386c7 Steven Ross) 31 | o use htparser_get_content_pending (c972ff3 Mark Ellzey) 32 | 33 | v2.0.11 34 | o Updating the README (unfinished) (83cb952 Mark Ellzey) 35 | o Close connection if no rules are matched. (18d5009 Mark Ellzey) 36 | o Version 2.0.10, see ChangeLog for details (ccdbc31 Mark Ellzey) 37 | o Added resource utilization reporting on startup (cfcbfe9 Mark Ellzey) 38 | o Add support for sha-1 fingerprint. (072598e Sean Cunningham) 39 | o Formatting fixup. (e3a81e8 Mark Ellzey) 40 | o Fix for trailing } on ifdef. (b05fe7f Mark Ellzey) 41 | 42 | v2.0.10 43 | o resume client request if pending timeout hits. (c282793 Mark Ellzey) 44 | o Updating the README (unfinished) (83cb952 Mark Ellzey) 45 | o Close connection if no rules are matched. (18d5009 Mark Ellzey) 46 | 47 | v2.0.9 48 | o Default logging level is now error. (4c8e275 Mark Ellzey) 49 | o Set "downstream down" messages log at level 'info'. (6e7d902 Mark Ellzey) 50 | o Add strip-headers config, see commit msg for details. (78a1075 Mark Ellzey) 51 | 52 | v2.0.8 53 | o Make @b6n not mad at me with default ciphers. (4911743 Mark Ellzey) 54 | o Added upstream high-watermark logic. (0c8064d Mark Ellzey) 55 | o Require libevhtp 1.1.7 (7d43a20 Mark Ellzey) 56 | 57 | v2.0.7 58 | o Added redirect host filtering. (4ef9155 Mark Ellzey) 59 | 60 | v2.0.6 61 | o Log ssl errors. (2e2c647 Mark Ellzey) 62 | o Rules with no downstreams configured now return 404 and log. (49d0a03 Mark Ellzey) 63 | o Default log level to error. (7c0d840 Mark Ellzey) 64 | o Convert \n in x-ssl-certificates to \r\n's. (1d0ad8c Mark Ellzey) 65 | o Use ASL logging for OSX instead of syslog. (cb26a3a Mark Ellzey) 66 | 67 | v2.0.5 68 | o default ciphers from google defaults (2305454 Mark Ellzey) 69 | o Fixed race bug when odd upstream errors happen. (46cfc9e Mark Ellzey) 70 | 71 | v2.0.4 72 | o Error on invalid rule pattern, log source port on set_idle. (b7ecd58 Mark Ellzey) 73 | o Rule configuration directive changes. (21b6191 Mark Ellzey) 74 | o Fix memoryleak in log_error_request. (1849bcf Mark Ellzey) 75 | o Added {RULE} logging argument. (6a77ae9 Mark Ellzey) 76 | o Fix smoke tests for new rule configuration. (f130c8e Mark Ellzey) 77 | 78 | v2.0.3 79 | o Added a "default" rule type. (934bb6d Mark Ellzey) 80 | o Logging heiarchy server -> vhost -> rule (6b2de9c Mark Ellzey) 81 | o Set event errors as INFO error logging. (d044e91 Mark Ellzey) 82 | o Fixu non-request logging bugs. (4c050e5 Mark Ellzey) 83 | o Require evhtp v1.0.0 (c8b3497 Mark Ellzey) 84 | 85 | v2.0.2 86 | o set connection->request NULL when passthrough event (7585256 Mark Ellzey) 87 | o Require evhtp 0.4.16 (017b93c Mark Ellzey) 88 | 89 | v2.0.1 90 | o set downstream down when request is null. (7c5992d Mark Ellzey) 91 | o Removing doxygen output. (3031101 Mark Ellzey) 92 | o Fixed issue with timeouts and passthrough's. (56d7583 Mark Ellzey) 93 | 94 | v2.0.0 95 | o Moving into the 2.x rule-based proxy architecture. (2dac0cb Mark Ellzey) 96 | o vhost / SNI support. (a08a68b Mark Ellzey) 97 | o Fixed thread race condition bug for downstream IO (258cea1 Mark Ellzey) 98 | o Cleanup and documentation. (4081e75 Mark Ellzey) 99 | o Initial checkin of 'passthrough' feature. (9af3453 Mark Ellzey) 100 | o Move passthrough ownership transition after upstream headers have been processed. (01b2404 Mark Ellzey) 101 | o Added log abstraction API "lzlog" (e3a23b6 Mark Ellzey) 102 | o Set -Wno-deprecated-declarations cflag for OSX (710b735 Mark Ellzey) 103 | o Request logging initial re-commit. (b7d9841 Mark Ellzey) 104 | o downstream host cfg can now be a hostname. (7a74627 Mark Ellzey) 105 | o Finished up logging API conversion over to lzlog. (a146877 Mark Ellzey) 106 | o Replaced old logging calls with new API (e188b54 Mark Ellzey) 107 | o Removed old logging calls. (cd551fe Mark Ellzey) 108 | o upstream_request_start is now an on_hostname hook. (d30816a Mark Ellzey) 109 | o Initial commit of the internal redirect feature. (aab993d Mark Ellzey) 110 | o Working internal redirect initial commit. (4b8eb58 Mark Ellzey) 111 | o Redirect now enabled on a per-rule basis. (94cbbf2 Mark Ellzey) 112 | o Smoketest updates, bugfixes, extra config opts. (49151d1 Mark Ellzey) 113 | o Remove sensitive x-headers even if not configured. (7cb57dd Mark Ellzey) 114 | o Added mem_trimsz for mallopt config. (77b4e81 Mark Ellzey) 115 | o Set upstream req to NULL when take_ownership is on (1ec8fc0 Mark Ellzey) 116 | o Free request if passthrough enabled. (2a40e46 Mark Ellzey) 117 | o Logging updates, potential doublewrite fix. (a37b22c Mark Ellzey) 118 | o Fix for double write, hopefully. (d0a4459 Mark Ellzey) 119 | o removed x509 verify logging for now. (3f0366d Mark Ellzey) 120 | o Fixed up file logging. (c0cf17d Mark Ellzey) 121 | o Documentation updates. (693a194 Mark Ellzey) 122 | 123 | v1.0.22 124 | o Fix potential garbage ret from ssl_x509_ext_tostr (5a29cf5 Mark Ellzey) 125 | 126 | v1.0.21 127 | o Fixed an assert logic error. (b711410 Mark Ellzey) 128 | 129 | v1.0.19 130 | o Log before write. (3a00ff9 Mark Ellzey) 131 | o Error handling refactoring, new logging formats, and much more, see full commit log. (c20e8bd Mark Ellzey) 132 | o Cleanup - Updated documentation for new log formats. (ad0cc31 Mark Ellzey) 133 | o Require use of libevhtp v0.4.12 or higher. (6033ba0 Mark Ellzey) 134 | o Updating API docs. (ef1b46a Mark Ellzey) 135 | o Make sure we use libevhtp-v0.4.13 (ccbbaaf Mark Ellzey) 136 | o Prep release v1.0.20. (606bcb0 Mark Ellzey) 137 | 138 | v1.0.18 139 | o Force pending timeout on requests in the high-watermark wait phase. (26fd21e Mark Ellzey) 140 | o delete the pending_ev if the writecb is called. (6901add Mark Ellzey) 141 | o Added high-watermark-timeout config option for timing out dead downstreams when a high-watermark is hit. Additionally added functionality to stagger pending timers to stop huge bursts of timeouts. (1a8baf6 Mark Ellzey) 142 | o Last commit was buggy, adding some debug msgs. (538ed54 Mark Ellzey) 143 | o More debug. (e0e878a Mark Ellzey) 144 | o Unset hooks on timeout. (2f03648 Mark Ellzey) 145 | o Fixes (82d5ccd Mark Ellzey) 146 | o More debug info. (8527eab Mark Ellzey) 147 | o derp (641502c Mark Ellzey) 148 | o fjkdsla (cd2ffd3 Mark Ellzey) 149 | o More. (bbe92a1 Mark Ellzey) 150 | o updates (51c635d Mark Ellzey) 151 | o Apply read/write timeouts to downstreams too. (42fc67a Mark Ellzey) 152 | o per-downstream read/write timeouts. (8dd4a28 Mark Ellzey) 153 | o Set default build-type to Release. (546d1b6 Mark Ellzey) 154 | o Adding Apache2.0 license to all source. (773c902 Mark Ellzey) 155 | o Updating documentation. (585a765 Mark Ellzey) 156 | o Updated documentation on setting file limits. (c387e12 Mark Ellzey) 157 | o send_upstream_* now handles errors gracefully. (b607209 Mark Ellzey) 158 | o Fixing up sample.cfg (cc4bea2 Mark Ellzey) 159 | o Making NDEBUG == RPROXY_DEBUG (4ed028d Mark Ellzey) 160 | o Adding source port information for connected downstream sockets. (937ce7a Mark Ellzey) 161 | o Adding a more verbose event error log. (2bedd9c Mark Ellzey) 162 | o New req logging fmts for up/downstream src ports. (cf5b2da Mark Ellzey) 163 | o Prep release v1.0.19 (106b2d5 Mark Ellzey) 164 | 165 | v1.0.17 166 | o Pending timers now staggered, avoiding bursts. (5fbf068 Mark Ellzey) 167 | o Replaced high-watermark-timeout with downstream r/w timeouts. (e4c9d3e Mark Ellzey) 168 | o per-downstream read/write timeouts. (9c8cccb Mark Ellzey) 169 | o Updating documentation for new config options. (85d1611 Mark Ellzey) 170 | o Set default build-type to Release. (3ec8caa Mark Ellzey) 171 | o Adding Apache2.0 license to all source. (3f0044b Mark Ellzey) 172 | o Prep release v1.0.18 (08c458a Mark Ellzey) 173 | 174 | v1.0.16 175 | o Added max-nofile config for setting max file open fd's. Updated documentation. (bc2d422 Mark Ellzey) 176 | o Fixed memory leak in ssl_x509_ext_tostr() (3503944 Mark Ellzey) 177 | o Prep release v1.0.17 (5601da1 Mark Ellzey) 178 | 179 | v1.0.15 180 | o Only display downstream down logs if the previous state was NOT down. (8b80172 Mark Ellzey) 181 | o Added more restrictive error logging for socket errors. (ba1f386 Mark Ellzey) 182 | o Prep release v1.0.16 (003542d Mark Ellzey) 183 | 184 | v1.0.14 185 | o Fixed bug in round-robin lb-method where if a wrap occurs, it does not check to see if it has any idle connections to use. This would potentially never return an idle connection if one goes down. (c43d370 Mark Ellzey) 186 | o Prep release v1.0.15 (8041d2c Mark Ellzey) 187 | 188 | v1.0.13 189 | o Add port to X-Forwarded-For. Support IPv6. (1b17c0c Sean Cunningham) 190 | o Fix formatting and host order. (560bfb4 Sean Cunningham) 191 | o Moar logging fixes (6f40145 Stephen Cox) 192 | o Format cleanup (9405368 Mark Ellzey) 193 | o Initial commit of different load-balancing methods. (4923249 Mark Ellzey) 194 | o Added round-robin lb method; Removed "ping" stuff as it is just an extra layer of complexity which won't be used. Updated documentation to reflect these changes. (02a27ee Mark Ellzey) 195 | o Cleaning up unused code. (610ec3e Mark Ellzey) 196 | o Formatting cleanup. (0caa904 Mark Ellzey) 197 | o Added smoke tests for round-robin functionality + updated unit-tests + minor bugfixes (d9ee51b Mark Ellzey) 198 | o updated sample.cfg to use lb-method = roundrobin (97934e9 Mark Ellzey) 199 | o Added lb-method none, which simply chooses the the first available connection without any calculations. (7eaddc5 Mark Ellzey) 200 | o Fixed smoke test to not use 'seq' command. (ddbed8d Mark Ellzey) 201 | o Prepping release v1.0.14 (1192938 Mark Ellzey) 202 | 203 | v1.0.12 204 | o Fixed issue where request_free() was called before error logging. (74cdc44 Mark Ellzey) 205 | o Prep release v1.0.13 (0d8cf12 Mark Ellzey) 206 | 207 | v1.0.11 208 | o Updating the README documentation. (152e8c0 Mark Ellzey) 209 | o Add error log helper for including the request in the logs; Minor tweak to smoke test build. (81ec5a5 Stephen Cox) 210 | o Cleanup - some error checking additions. (fbaaa9a Mark Ellzey) 211 | o Removed unnecessary newlines from error logging. (43496f4 Mark Ellzey) 212 | o Prepping release v1.0.12 (6f2d3d9 Mark Ellzey) 213 | 214 | v1.0.10 215 | o Don't use mallopt on systems without it. (2f91c7d Mark Ellzey) 216 | o Fixes for strn* functions not avail on system. (4f60c91 Mark Ellzey) 217 | o Prepping v1.0.11 (809393e Mark Ellzey) 218 | 219 | v1.0.9 220 | o Fixed comparison error. (3b83c21 Mark Ellzey) 221 | o Upping version (0ce2635 Mark Ellzey) 222 | 223 | v1.0.8 224 | o Added upstream_pre_accept to close a connection before SSL handshake if the backlog is full. (4aa401b Mark Ellzey) 225 | o smoke_backend cleanups (3bb1b88 Mark Ellzey) 226 | o Fixup for checking pending count. (d4cb39e Mark Ellzey) 227 | o Added libevhtp version to output. (ae6adf3 Mark Ellzey) 228 | o Added some extra debug logging. (117d65d Mark Ellzey) 229 | o More debug logging. (20738a6 Mark Ellzey) 230 | o even more debug logging. (6f7a513 Mark Ellzey) 231 | o even more debug logging. (53f4305 Mark Ellzey) 232 | o draining on idle, potential fix, prob. not. (b968888 Mark Ellzey) 233 | o adding NODELAY to downstream connections. (e385089 Mark Ellzey) 234 | o Add smoke test for invalid upstream chunking (b1e028e Stephen Cox) 235 | o testing. (7c06d69 Mark Ellzey) 236 | o Add config/backend support for chunking test (c233b6d Stephen Cox) 237 | o active when pending is full in start (0ef3f91 Mark Ellzey) 238 | o removing setsockopt (9b068ba Mark Ellzey) 239 | o removing drain from set_idle() (4eea75b Mark Ellzey) 240 | o removing event_active (f233970 Mark Ellzey) 241 | o test (a223831 Mark Ellzey) 242 | o rearrange timer_del (6fcff90 Mark Ellzey) 243 | o Added some pending debug logs (eda3a38 Mark Ellzey) 244 | o Fix potential hanging-connection issue if client sends connection: close header. (63e1372 Mark Ellzey) 245 | o Make various timers fire at a lower priority than other events. (d30f3fa Mark Ellzey) 246 | o removing priorities. (82eb732 Mark Ellzey) 247 | o testing thresholds (5001246 Mark Ellzey) 248 | o updating tests (999a86a Mark Ellzey) 249 | o Made high-watermark for downstreams configurable (8736fd6 Mark Ellzey) 250 | o Removing stupid debug log. (aaadb84 Mark Ellzey) 251 | o Testing removal of pending check when it's already been accepted. (59ba13f Mark Ellzey) 252 | o Prepping v1.0.9 (449a3e7 Mark Ellzey) 253 | 254 | v1.0.7 255 | o Fix logging param on downstream reconnect (44bb5aa Stephen Cox) 256 | o Allow SSL context timeout to be passed in via config (7d016e2 Stephen Cox) 257 | o Add smoke tests (0c48048 Stephen Cox) 258 | o Prefer x-ssl-certificate instead of x-ssl-cert for consistency (354134f Stephen Cox) 259 | o Implement missing tostr functions (x-ssl header support) (41fd76c Stephen Cox) 260 | o Add additional test rewrite rules; Clean up spacing (e956cb2 Stephen Cox) 261 | o Allow arbitrary x509 extension to be added as a header to downstream; Fix directory issues with smoke test (b4589f8 Stephen Cox) 262 | o Revert cfg user/group to rproxy (901e154 Stephen Cox) 263 | o Add smoke tests for slow connections and invalid chunk length. (b759937 Stephen Cox) 264 | o cleanup (6c8933b Mark Ellzey) 265 | o Have smoke tests run in build directory without install. (0b71ef9 Mark Ellzey) 266 | o Updating README.md (cd23e34 Mark Ellzey) 267 | o Prepping release v1.0.8 (785b380 Mark Ellzey) 268 | 269 | v1.0.6 270 | o tp2656 - fix to immediately re-ping upon down -> idle downstream state transitions. (236f1b5 Mark Ellzey) 271 | o TP-2653 protocols-(on|off) directives being ignored. (7b1cbf1 Mark Ellzey) 272 | o Various bugfixes (b184ef6 Mark Ellzey) 273 | o Prepping release v1.0.7 (c18f337 Mark Ellzey) 274 | 275 | v1.0.5 276 | o Inform evhtp to not keep the connection alive if pending timeout is reached. (75dd245 Mark Ellzey) 277 | o Set connection: close header if pending timeout occurs (acf3983 Mark Ellzey) 278 | o Require libevhtp version >= 0.4.5 (f6c984a Mark Ellzey) 279 | o Prepping release v1.0.6 (92c0fa0 Mark Ellzey) 280 | 281 | v1.0.4 282 | o When a downstream errors during a response to the upstream, we must force the client connection closed instead of sending back just a 503. (6d66efd Mark Ellzey) 283 | o Fixed syntax error. (67ad8e6 Mark Ellzey) 284 | o fix with unsetting hooks before connection_free (851af51 Mark Ellzey) 285 | o Prepping release 1.0.5 (951ae40 Mark Ellzey) 286 | 287 | v1.0.3 288 | o Cert buffer not large enough to accomodate NULL byte. Do not assume last character in input is '\n'. (000708a Sean Cunningham) 289 | o Prep release v1.0.4 (11abee1 Mark Ellzey) 290 | 291 | v1.0.2 292 | o Modified ChangeLog (f15529b Mark Ellzey) 293 | o Added enforce-peer-cert and updated documentation. (dc4bd2d Mark Ellzey) 294 | o If the system contains the daemon() function, then use that instead of the built-in one. (02367ac Mark Ellzey) 295 | o Removed x509_chk_issued_cb callback. (773517f Mark Ellzey) 296 | o Daemonize fix (99b0037 Mark Ellzey) 297 | o Prepping release v1.0.3 (507fee5 Mark Ellzey) 298 | 299 | v1.0.1 300 | o Added max-pending config directive to limit the number of requests in a pending state. (399b30c Mark Ellzey) 301 | o Prepping release 1.0.2 (ba2a353 Mark Ellzey) 302 | 303 | v1.0.0 304 | o Updated unit tests. (2716cef Mark Ellzey) 305 | o Fix crash when pending-timeout = 0 then attempts to delete the event. (1926c7e Mark Ellzey) 306 | o Updating version information. (82d80f6 Mark Ellzey) 307 | 308 | -------------------------------------------------------------------------------- /src/lzlog.c: -------------------------------------------------------------------------------- 1 | /* Copyright [2012] [Mandiant, inc] 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "lzlog.h" 23 | 24 | typedef struct lzlog_vtbl lzlog_vtbl; 25 | 26 | struct log { 27 | lzlog_vtbl * vtbl; 28 | char * ident; 29 | int opts; 30 | lzlog_level level; 31 | lzlog_type type; 32 | pthread_mutex_t mutex; 33 | }; 34 | 35 | struct lzlog_vtbl { 36 | size_t size; 37 | void (* destroy)(lzlog * log); 38 | void (* print)(lzlog * log, lzlog_level level, const char * fmt, va_list ap); 39 | }; 40 | 41 | static char * _level_str[] = { 42 | "EMERG", 43 | "ALERT", 44 | "CRIT", 45 | "ERROR", 46 | "WARN", 47 | "NOTICE", 48 | "INFO", 49 | "DEBUG", 50 | NULL 51 | }; 52 | 53 | struct { 54 | int facility; 55 | const char * str; 56 | } _facility_strmap[] = { 57 | { LOG_KERN, "kern" }, 58 | { LOG_USER, "user" }, 59 | { LOG_MAIL, "mail" }, 60 | { LOG_DAEMON, "daemon" }, 61 | { LOG_AUTH, "auth" }, 62 | { LOG_SYSLOG, "syslog" }, 63 | { LOG_LPR, "lptr" }, 64 | { LOG_NEWS, "news" }, 65 | { LOG_UUCP, "uucp" }, 66 | { LOG_CRON, "cron" }, 67 | { LOG_AUTHPRIV, "authpriv" }, 68 | { LOG_FTP, "ftp" }, 69 | { LOG_LOCAL0, "local0" }, 70 | { LOG_LOCAL1, "local1" }, 71 | { LOG_LOCAL2, "local2" }, 72 | { LOG_LOCAL3, "local3" }, 73 | { LOG_LOCAL4, "local4" }, 74 | { LOG_LOCAL5, "local5" }, 75 | { LOG_LOCAL6, "local6" }, 76 | { LOG_LOCAL7, "local7" }, 77 | { -1, NULL } 78 | }; 79 | 80 | struct { 81 | lzlog_level level; 82 | const char * str; 83 | } _level_strmap[] = { 84 | { lzlog_emerg, "emerg" }, 85 | { lzlog_alert, "alert" }, 86 | { lzlog_crit, "crit" }, 87 | { lzlog_err, "err" }, 88 | { lzlog_warn, "warn" }, 89 | { lzlog_notice, "notice" }, 90 | { lzlog_info, "info" }, 91 | { lzlog_debug, "debug" }, 92 | { -1, NULL } 93 | }; 94 | 95 | int 96 | lzlog_facilitystr_to_facility(const char * str) { 97 | int i; 98 | 99 | if (str == NULL) { 100 | return -1; 101 | } 102 | 103 | for (i = 0; _facility_strmap[i].str; i++) { 104 | if (!strcasecmp(_facility_strmap[i].str, str)) { 105 | return _facility_strmap[i].facility; 106 | } 107 | } 108 | 109 | return -1; 110 | } 111 | 112 | lzlog_level 113 | lzlog_levelstr_to_level(const char * str) { 114 | int i; 115 | 116 | if (str == NULL) { 117 | return -1; 118 | } 119 | 120 | for (i = 0; _level_strmap[i].str; i++) { 121 | if (!strcasecmp(_level_strmap[i].str, str)) { 122 | return _level_strmap[i].level; 123 | } 124 | } 125 | 126 | return -1; 127 | } 128 | 129 | void 130 | lzlog_vprintf(lzlog * log, lzlog_level level, const char * fmt, va_list ap) { 131 | if (log == NULL) { 132 | return; 133 | } 134 | 135 | if (level > log->level) { 136 | return; 137 | } 138 | 139 | if (log->vtbl->print) { 140 | (log->vtbl->print)(log, level, fmt, ap); 141 | } 142 | } 143 | 144 | void 145 | lzlog_write(lzlog * log, lzlog_level level, const char * fmt, ...) { 146 | va_list ap; 147 | 148 | if (log == NULL) { 149 | return; 150 | } 151 | 152 | if (level > log->level) { 153 | return; 154 | } 155 | 156 | va_start(ap, fmt); 157 | { 158 | lzlog_vprintf(log, level, fmt, ap); 159 | } 160 | va_end(ap); 161 | } 162 | 163 | static lzlog * 164 | lzlog_new(lzlog_vtbl * vtbl, const char * ident, int opts) { 165 | lzlog * log; 166 | 167 | if (!(log = calloc(vtbl->size, 1))) { 168 | return NULL; 169 | } 170 | 171 | pthread_mutex_init(&log->mutex, NULL); 172 | 173 | if (ident) { 174 | log->ident = strdup(ident); 175 | } else { 176 | log->ident = strdup("pname"); 177 | } 178 | 179 | log->vtbl = vtbl; 180 | log->level = lzlog_max; 181 | log->opts = opts; 182 | 183 | return log; 184 | } 185 | 186 | void 187 | lzlog_free(lzlog * log) { 188 | if (!log) { 189 | return; 190 | } 191 | 192 | if (log->vtbl->destroy) { 193 | (log->vtbl->destroy)(log); 194 | } 195 | 196 | if (log->ident) { 197 | free(log->ident); 198 | } 199 | 200 | pthread_mutex_destroy(&log->mutex); 201 | 202 | free(log); 203 | } 204 | 205 | void 206 | lzlog_set_level(lzlog * log, lzlog_level level) { 207 | if (!log) { 208 | return; 209 | } 210 | 211 | log->level = level; 212 | } 213 | 214 | lzlog_type 215 | lzlog_get_type(lzlog * log) { 216 | return log->type; 217 | } 218 | 219 | static char * 220 | _reformat(lzlog * log, const char * fmt, lzlog_level level) { 221 | int sres; 222 | char * buf = NULL; 223 | size_t len = strlen(fmt) + 4; 224 | int wrote = 0; 225 | 226 | if (log->opts == LZLOG_OPT_NONE) { 227 | return NULL; 228 | } 229 | 230 | if (!(buf = calloc(len, 1))) { 231 | abort(); 232 | } 233 | 234 | if (log->opts & LZLOG_OPT_WDATE) { 235 | char sbuf[255]; 236 | time_t tt = time(NULL); 237 | 238 | len += strftime(sbuf, 254, "%b %d %H:%M:%S ", localtime(&tt)); 239 | buf = realloc(buf, len); 240 | sres = snprintf(buf, len, "%s", sbuf); 241 | 242 | if (sres >= len || sres < 0) { 243 | abort(); 244 | } 245 | 246 | wrote = 1; 247 | } 248 | 249 | if (log->opts & LZLOG_OPT_WNAME) { 250 | len += strlen(log->ident); 251 | buf = realloc(buf, len); 252 | 253 | strncat(buf, log->ident, len); 254 | } 255 | 256 | if (log->opts & LZLOG_OPT_WPID) { 257 | char sbuf[10 + 3]; /* pid + [] + null */ 258 | pid_t pid = getpid(); 259 | 260 | sres = snprintf(sbuf, sizeof(sbuf), "[%u]", pid); 261 | 262 | if (sres >= sizeof(sbuf) || sres < 0) { 263 | abort(); 264 | } 265 | 266 | len += strlen(sbuf); 267 | buf = realloc(buf, len); 268 | 269 | strncat(buf, sbuf, len); 270 | 271 | wrote = 1; 272 | } 273 | 274 | if (log->opts & LZLOG_OPT_WLEVEL) { 275 | if (level < lzlog_max) { 276 | len += strlen(_level_str[level]) + 3; 277 | buf = realloc(buf, len); 278 | 279 | if (log->opts != LZLOG_OPT_WLEVEL) { 280 | strcat(buf, ": "); 281 | } 282 | 283 | strncat(buf, _level_str[level], len); 284 | 285 | wrote = 1; 286 | } 287 | } 288 | 289 | if (wrote == 1) { 290 | strncat(buf, ": ", len); 291 | } 292 | 293 | strncat(buf, fmt, len); 294 | 295 | if (log->opts & LZLOG_OPT_NEWLINE) { 296 | strncat(buf, "\n", len); 297 | } 298 | 299 | return buf; 300 | } /* _reformat */ 301 | 302 | static void 303 | _syslog_print(lzlog * log, lzlog_level level, const char * fmt, va_list ap) { 304 | int priority = 0; 305 | char * nfmt = NULL; 306 | 307 | nfmt = _reformat(log, fmt, level); 308 | 309 | switch (level) { 310 | case lzlog_emerg: 311 | priority = LOG_EMERG; 312 | break; 313 | case lzlog_alert: 314 | priority = LOG_ALERT; 315 | break; 316 | case lzlog_crit: 317 | priority = LOG_CRIT; 318 | break; 319 | case lzlog_err: 320 | priority = LOG_ERR; 321 | break; 322 | case lzlog_warn: 323 | priority = LOG_WARNING; 324 | break; 325 | case lzlog_notice: 326 | priority = LOG_NOTICE; 327 | break; 328 | case lzlog_info: 329 | priority = LOG_INFO; 330 | break; 331 | case lzlog_debug: 332 | priority = LOG_DEBUG; 333 | break; 334 | default: 335 | priority = LOG_ERR; 336 | break; 337 | } /* switch */ 338 | 339 | vsyslog(priority, nfmt ? nfmt : fmt, ap); 340 | 341 | if (nfmt) { 342 | free(nfmt); 343 | } 344 | } /* _syslog_print */ 345 | 346 | static void 347 | _syslog_destroy(lzlog * log) { 348 | return closelog(); 349 | } 350 | 351 | static lzlog_vtbl _syslzlog_vtbl = { 352 | sizeof(lzlog), 353 | _syslog_destroy, 354 | _syslog_print 355 | }; 356 | 357 | lzlog * 358 | lzlog_syslog_new(const char * ident, int opts, int facility) { 359 | #ifdef __APPLE__ 360 | return lzlog_asl_new(ident, opts, facility); 361 | #endif 362 | int syslog_opts = 0; 363 | lzlog * log; 364 | 365 | if (opts & LZLOG_OPT_WPID) { 366 | syslog_opts |= LOG_PID; 367 | } 368 | 369 | openlog(ident, syslog_opts, facility); 370 | 371 | log = lzlog_new(&_syslzlog_vtbl, ident, opts); 372 | log->type = lzlog_syslog; 373 | 374 | return log; 375 | } 376 | 377 | struct _log_file { 378 | lzlog parent; 379 | FILE * file; 380 | }; 381 | 382 | static void 383 | _file_print(lzlog * log, lzlog_level level, const char * fmt, va_list ap) { 384 | struct _log_file * this = (struct _log_file *)log; 385 | char * nfmt = NULL; 386 | 387 | 388 | nfmt = _reformat(log, fmt, level); 389 | 390 | pthread_mutex_lock(&log->mutex); 391 | { 392 | vfprintf(this->file, nfmt ? nfmt : fmt, ap); 393 | fflush(this->file); 394 | } 395 | pthread_mutex_unlock(&log->mutex); 396 | 397 | if (nfmt) { 398 | free(nfmt); 399 | } 400 | } 401 | 402 | static void 403 | _file_destroy(lzlog * log) { 404 | struct _log_file * this = (struct _log_file *)log; 405 | 406 | pthread_mutex_lock(&log->mutex); 407 | { 408 | fflush(this->file); 409 | fclose(this->file); 410 | } 411 | pthread_mutex_unlock(&log->mutex); 412 | 413 | free(this); 414 | } 415 | 416 | static lzlog_vtbl _file_vtbl = { 417 | sizeof(struct _log_file), 418 | _file_destroy, 419 | _file_print 420 | }; 421 | 422 | lzlog * 423 | lzlog_file_new(const char * file, const char * ident, int opts) { 424 | lzlog * result; 425 | struct _log_file * lfile; 426 | 427 | if (!(result = lzlog_new(&_file_vtbl, ident, opts))) { 428 | return NULL; 429 | } 430 | 431 | result->type = lzlog_file; 432 | lfile = (struct _log_file *)result; 433 | 434 | if (!(lfile->file = fopen(file, "a+"))) { 435 | return NULL; 436 | } 437 | 438 | return result; 439 | } 440 | 441 | lzlog * 442 | lzlog_from_template(const char * template, const char * ident, int opts) { 443 | char * scheme; 444 | char * path; 445 | char * levelstr; 446 | char * template_cpy; 447 | lzlog_level level; 448 | lzlog * log; 449 | 450 | if (template == NULL) { 451 | return NULL; 452 | } 453 | 454 | template_cpy = strdup(template); 455 | 456 | /* 457 | * templates are as follows: 458 | * 459 | * :# 460 | */ 461 | 462 | scheme = strtok(template_cpy, ":"); 463 | path = strtok(NULL, ":"); 464 | levelstr = strtok(path, "#"); 465 | levelstr = strtok(NULL, "#"); 466 | 467 | if (!scheme || !path) { 468 | free(template_cpy); 469 | return NULL; 470 | } 471 | 472 | if (!strcasecmp(scheme, "syslog")) { 473 | int facility; 474 | 475 | if (!(facility = lzlog_facilitystr_to_facility(path))) { 476 | free(template_cpy); 477 | return NULL; 478 | } 479 | 480 | log = lzlog_syslog_new(ident, opts, facility); 481 | } else if (!strcasecmp(scheme, "file")) { 482 | log = lzlog_file_new(path, ident, opts); 483 | } else { 484 | free(template_cpy); 485 | return NULL; 486 | } 487 | 488 | if (levelstr) { 489 | level = lzlog_levelstr_to_level(levelstr); 490 | } else { 491 | level = lzlog_notice; 492 | } 493 | 494 | lzlog_set_level(log, level); 495 | 496 | free(template_cpy); 497 | return log; 498 | } /* lzlog_from_template */ 499 | 500 | #ifdef __APPLE__ 501 | #include 502 | 503 | const char * 504 | asl_syslog_faciliy_num_to_name(int n) { 505 | if (n < 0) { 506 | return NULL; 507 | } 508 | 509 | if (n == LOG_AUTH) { 510 | return "auth"; 511 | } 512 | if (n == LOG_AUTHPRIV) { 513 | return "authpriv"; 514 | } 515 | if (n == LOG_CRON) { 516 | return "cron"; 517 | } 518 | if (n == LOG_DAEMON) { 519 | return "daemon"; 520 | } 521 | if (n == LOG_FTP) { 522 | return "ftp"; 523 | } 524 | if (n == LOG_INSTALL) { 525 | return "install"; 526 | } 527 | if (n == LOG_KERN) { 528 | return "kern"; 529 | } 530 | if (n == LOG_LPR) { 531 | return "lpr"; 532 | } 533 | if (n == LOG_MAIL) { 534 | return "mail"; 535 | } 536 | if (n == LOG_NETINFO) { 537 | return "netinfo"; 538 | } 539 | if (n == LOG_REMOTEAUTH) { 540 | return "remoteauth"; 541 | } 542 | if (n == LOG_NEWS) { 543 | return "news"; 544 | } 545 | if (n == LOG_AUTH) { 546 | return "security"; 547 | } 548 | if (n == LOG_SYSLOG) { 549 | return "syslog"; 550 | } 551 | if (n == LOG_USER) { 552 | return "user"; 553 | } 554 | if (n == LOG_UUCP) { 555 | return "uucp"; 556 | } 557 | if (n == LOG_LOCAL0) { 558 | return "local0"; 559 | } 560 | if (n == LOG_LOCAL1) { 561 | return "local1"; 562 | } 563 | if (n == LOG_LOCAL2) { 564 | return "local2"; 565 | } 566 | if (n == LOG_LOCAL3) { 567 | return "local3"; 568 | } 569 | if (n == LOG_LOCAL4) { 570 | return "local4"; 571 | } 572 | if (n == LOG_LOCAL5) { 573 | return "local5"; 574 | } 575 | if (n == LOG_LOCAL6) { 576 | return "local6"; 577 | } 578 | if (n == LOG_LOCAL7) { 579 | return "local7"; 580 | } 581 | if (n == LOG_LAUNCHD) { 582 | return "launchd"; 583 | } 584 | 585 | return NULL; 586 | } /* asl_syslog_faciliy_num_to_name */ 587 | 588 | struct _log_asl { 589 | lzlog parent; 590 | char * facility; 591 | aslclient asl_f; 592 | }; 593 | 594 | static void 595 | _asl_print(lzlog * log, lzlog_level level, const char * fmt, va_list ap) { 596 | aslmsg facmsg; 597 | int priority = 0; 598 | struct _log_asl * this = (struct _log_asl *)log; 599 | 600 | switch (level) { 601 | case lzlog_emerg: 602 | priority = ASL_LEVEL_EMERG; 603 | break; 604 | case lzlog_alert: 605 | priority = ASL_LEVEL_ALERT; 606 | break; 607 | case lzlog_crit: 608 | priority = ASL_LEVEL_CRIT; 609 | break; 610 | case lzlog_err: 611 | priority = ASL_LEVEL_ERR; 612 | break; 613 | case lzlog_warn: 614 | priority = ASL_LEVEL_WARNING; 615 | break; 616 | case lzlog_notice: 617 | priority = ASL_LEVEL_NOTICE; 618 | break; 619 | case lzlog_info: 620 | priority = ASL_LEVEL_INFO; 621 | break; 622 | case lzlog_debug: 623 | priority = ASL_LEVEL_DEBUG; 624 | break; 625 | default: 626 | priority = ASL_LEVEL_ERR; 627 | break; 628 | } /* switch */ 629 | 630 | facmsg = asl_new(ASL_TYPE_MSG); 631 | 632 | asl_set(facmsg, ASL_KEY_FACILITY, this->facility); 633 | asl_vlog(this->asl_f, facmsg, priority, fmt, ap); 634 | asl_free(facmsg); 635 | } /* _asl_print */ 636 | 637 | static lzlog_vtbl _asl_vtbl = { 638 | sizeof(struct _log_asl), 639 | NULL, 640 | _asl_print 641 | }; 642 | 643 | lzlog * 644 | lzlog_asl_new(const char * ident, int opts, int facility) { 645 | lzlog * result; 646 | struct _log_asl * lasl; 647 | const char * facility_str = 648 | asl_syslog_faciliy_num_to_name(facility); 649 | 650 | if (!(result = lzlog_new(&_asl_vtbl, ident, opts))) { 651 | return NULL; 652 | } 653 | 654 | result->type = lzlog_asl; 655 | lasl = (struct _log_asl *)result; 656 | 657 | lasl->asl_f = asl_open(ident, facility_str, 0); 658 | lasl->facility = strdup(facility_str); 659 | 660 | return result; 661 | } 662 | 663 | #endif 664 | -------------------------------------------------------------------------------- /src/rproxy.h: -------------------------------------------------------------------------------- 1 | /* Copyright [2012] [Mandiant, inc] 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #ifndef __RPROXY_H__ 17 | #define __RPROXY_H__ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "lzq.h" 32 | #include "lzlog.h" 33 | 34 | #define RPROXY_VERSION "2.0.17" 35 | 36 | #if EVHTP_VERSION_MAJOR <= 1 37 | #if EVHTP_VERSION_MINOR <= 2 38 | #if EVHTP_VERSION_PATCH < 3 39 | #error RProxy requires libevhtp v1.2.3 or greater 40 | #endif 41 | #endif 42 | #endif 43 | 44 | /************************************************ 45 | * configuration structure definitions 46 | ************************************************/ 47 | 48 | enum rule_type { 49 | rule_type_exact, 50 | rule_type_regex, 51 | rule_type_glob, 52 | rule_type_default 53 | }; 54 | 55 | enum lb_method { 56 | lb_method_rtt = 0, 57 | lb_method_rr, 58 | lb_method_rand, 59 | lb_method_most_idle, 60 | lb_method_none 61 | }; 62 | 63 | enum logger_type { 64 | logger_type_file = 0, 65 | logger_type_syslog 66 | }; 67 | 68 | typedef struct rproxy_rusage rproxy_rusage_t; 69 | typedef struct rproxy_cfg rproxy_cfg_t; 70 | typedef struct rule_cfg rule_cfg_t; 71 | typedef struct vhost_cfg vhost_cfg_t; 72 | typedef struct server_cfg server_cfg_t; 73 | typedef struct downstream_cfg downstream_cfg_t; 74 | typedef struct headers_cfg headers_cfg_t; 75 | typedef struct x509_ext_cfg x509_ext_cfg_t; 76 | typedef struct ssl_crl_cfg ssl_crl_cfg_t; 77 | typedef struct logger_cfg logger_cfg_t; 78 | 79 | typedef enum rule_type rule_type; 80 | typedef enum lb_method lb_method; 81 | typedef enum logger_type logger_type; 82 | 83 | struct logger_cfg { 84 | lzlog_level level; 85 | logger_type type; 86 | char * path; 87 | char * format; 88 | int facility; 89 | }; 90 | 91 | struct rule_cfg { 92 | char * name; /**< the name of the rule */ 93 | rule_type type; /**< what type of rule this is (regex/exact/glob) */ 94 | lb_method lb_method; /**< method of load-balacinging (defaults to RTT) */ 95 | char * matchstr; /**< the uri to match on */ 96 | headers_cfg_t * headers; /**< headers which are added to the backend request */ 97 | lztq * downstreams; /**< list of downstream names (as supplied by downstream_cfg_t->name */ 98 | logger_cfg_t * req_log; /**< request logging config */ 99 | logger_cfg_t * err_log; /**< error logging config */ 100 | bool passthrough; /**< if set to true, a pipe between the upstream and downstream is established */ 101 | bool allow_redirect; /**< if true, the downstream can send a redirect to connect to a different downstream */ 102 | lztq * redirect_filter; /**< a list of hostnames that redirects are can connect to */ 103 | int has_up_read_timeout; 104 | int has_up_write_timeout; 105 | struct timeval up_read_timeout; 106 | struct timeval up_write_timeout; 107 | }; 108 | 109 | /** 110 | * @brief a configuration structure representing a single x509 extension header. 111 | * 112 | */ 113 | struct x509_ext_cfg { 114 | char * name; /**< the name of the header */ 115 | char * oid; /**< the oid of the x509 extension to pull */ 116 | }; 117 | 118 | struct ssl_crl_cfg { 119 | char * filename; 120 | char * dirname; 121 | struct timeval reload_timer; 122 | }; 123 | 124 | /** 125 | * @brief which headers to add to the downstream request if avail. 126 | */ 127 | struct headers_cfg { 128 | bool x_forwarded_for; 129 | bool x_ssl_subject; 130 | bool x_ssl_issuer; 131 | bool x_ssl_notbefore; 132 | bool x_ssl_notafter; 133 | bool x_ssl_sha1; 134 | bool x_ssl_serial; 135 | bool x_ssl_cipher; 136 | bool x_ssl_certificate; 137 | lztq * x509_exts; 138 | }; 139 | 140 | /** 141 | * @brief configuration for a single downstream. 142 | */ 143 | struct downstream_cfg { 144 | bool enabled; /**< true if server is enabled */ 145 | char * name; /**< the name of this downstream. the name is used as an identifier for rules */ 146 | char * host; /**< the hostname of the downstream */ 147 | uint16_t port; /**< the port of the downstream */ 148 | int n_connections; /**< number of connections to keep established */ 149 | size_t high_watermark; /**< if the number of bytes pending on the output side 150 | * of the socket reaches this number, the proxy stops 151 | * reading from the upstream until all data has been written. */ 152 | struct timeval retry_ival; /**< retry timer if the downstream connection goes down */ 153 | struct timeval read_timeout; 154 | struct timeval write_timeout; 155 | }; 156 | 157 | 158 | struct vhost_cfg { 159 | evhtp_ssl_cfg_t * ssl_cfg; 160 | lztq * rule_cfgs; /**< list of rule_cfg_t's */ 161 | lztq * rules; /* list of rule_t's */ 162 | char * server_name; 163 | lztq * aliases; /**< other hostnames this vhost is associated with */ 164 | lztq * strip_hdrs; /**< headers to strip out from downstream responses */ 165 | logger_cfg_t * req_log; /* request logging configuration */ 166 | logger_cfg_t * err_log; /* error logging configuration */ 167 | headers_cfg_t * headers; /**< headers which are added to the backend request */ 168 | }; 169 | 170 | /** 171 | * @brief configuration for a single listening frontend server. 172 | */ 173 | struct server_cfg { 174 | char * bind_addr; /**< address to bind on */ 175 | uint16_t bind_port; /**< port to bind on */ 176 | int num_threads; /**< number of worker threads to start */ 177 | int max_pending; /**< max pending requests before new connections are dropped */ 178 | int listen_backlog; /**< listen backlog */ 179 | size_t high_watermark; /**< upstream high-watermark */ 180 | 181 | struct timeval read_timeout; /**< time to wait for reading before client is dropped */ 182 | struct timeval write_timeout; /**< time to wait for writing before client is dropped */ 183 | struct timeval pending_timeout; /**< time to wait for a downstream to become available for a connection */ 184 | 185 | rproxy_cfg_t * rproxy_cfg; /**< parent rproxy configuration */ 186 | evhtp_ssl_cfg_t * ssl_cfg; /**< if enabled, the ssl configuration */ 187 | lztq * downstreams; /**< list of downstream_cfg_t's */ 188 | lztq * vhosts; /**< list of vhost_cfg_t's */ 189 | logger_cfg_t * req_log_cfg; 190 | logger_cfg_t * err_log_cfg; 191 | 192 | int disable_server_nagle; /**< disable nagle for listening sockets */ 193 | int disable_client_nagle; /**< disable nagle for upstream sockets */ 194 | int disable_downstream_nagle; /**< disable nagle for downstream sockets */ 195 | }; 196 | 197 | 198 | 199 | /** 200 | * @brief This is a structure that is filled during the configuration parsing 201 | * stage. The information contains the various resources (file descriptors 202 | * and such) that would be needed for the proxy to run optimally. 203 | * 204 | * The idea is to warn the administrator that his system limits 205 | * may impact the performance of the service. 206 | */ 207 | struct rproxy_rusage { 208 | unsigned int total_num_connections; /**< the total of all downstream connections */ 209 | unsigned int total_num_threads; /**< the total threads which will spawn */ 210 | unsigned int total_max_pending; /**< the total of configured max-pending connections */ 211 | }; 212 | 213 | /** 214 | * @brief main configuration structure. 215 | */ 216 | struct rproxy_cfg { 217 | bool daemonize; /**< should proxy run in background */ 218 | int mem_trimsz; 219 | int max_nofile; /**< max number of open file descriptors */ 220 | char * rootdir; /**< root dir to daemonize */ 221 | char * user; /**< user to run as */ 222 | char * group; /**< group to run as */ 223 | lztq * servers; /**< list of server_cfg_t's */ 224 | logger_cfg_t * log; /**< generic log configuration */ 225 | rproxy_rusage_t rusage; /**< the needed resource totals */ 226 | }; 227 | 228 | /******************************************** 229 | * Main structures 230 | ********************************************/ 231 | 232 | /** 233 | * @brief a downstream's connection status. 234 | */ 235 | enum downstream_status { 236 | downstream_status_nil = 0, /**< connection has never been used */ 237 | downstream_status_active, /**< connection is actively processing */ 238 | downstream_status_idle, /**< connection is idle and available */ 239 | downstream_status_down /**< connection is down and cannot be used */ 240 | }; 241 | 242 | enum logger_argtype { 243 | logger_argtype_nil = 0, 244 | logger_argtype_src, 245 | logger_argtype_proxy, 246 | logger_argtype_ts, 247 | logger_argtype_ua, 248 | logger_argtype_meth, 249 | logger_argtype_uri, 250 | logger_argtype_proto, 251 | logger_argtype_status, 252 | logger_argtype_ref, 253 | logger_argtype_host, 254 | logger_argtype_ds_sport, 255 | logger_argtype_us_sport, 256 | logger_argtype_us_hdrval, 257 | logger_argtype_ds_hdrval, 258 | logger_argtype_rule, 259 | logger_argtype_printable 260 | }; 261 | 262 | typedef struct rproxy rproxy_t; 263 | typedef struct downstream downstream_t; 264 | typedef struct downstream_c downstream_c_t; 265 | typedef struct request request_t; 266 | typedef struct rule rule_t; 267 | typedef struct vhost vhost_t; 268 | typedef struct logger_arg logger_arg_t; 269 | typedef struct logger logger_t; 270 | typedef struct pending_request_q pending_request_q_t; 271 | typedef struct ssl_crl_ent ssl_crl_ent_t; 272 | 273 | typedef enum downstream_status downstream_status; 274 | typedef enum logger_argtype logger_argtype; 275 | 276 | #define REQUEST_HAS_ERROR(req) (req->error ? 1 : req->upstream_err ? 1 : 0) 277 | 278 | struct logger_arg { 279 | logger_argtype type; 280 | char * data; 281 | size_t len; 282 | size_t used; 283 | 284 | TAILQ_ENTRY(logger_arg) next; 285 | }; 286 | 287 | struct logger { 288 | logger_cfg_t * config; 289 | lzlog * log; 290 | 291 | TAILQ_HEAD(logger_args, logger_arg) args; 292 | }; 293 | 294 | struct ssl_crl_ent { 295 | ssl_crl_cfg_t * cfg; 296 | rproxy_t * rproxy; 297 | evhtp_t * htp; 298 | X509_STORE * crl; 299 | event_t * reload_timer_ev; 300 | #ifdef __APPLE__ 301 | struct timespec last_file_mod; 302 | struct timespec last_dir_mod; 303 | #else 304 | time_t last_file_mod; 305 | time_t last_dir_mod; 306 | #endif 307 | pthread_mutex_t lock; /**< lock to make sure we don't overwrite during a verification */ 308 | }; 309 | 310 | struct vhost { 311 | vhost_cfg_t * config; 312 | rproxy_t * rproxy; 313 | logger_t * req_log; 314 | logger_t * err_log; 315 | }; 316 | 317 | /** 318 | * @brief structure which represents a full proxy request 319 | * 320 | */ 321 | struct request { 322 | rproxy_t * rproxy; /**< the parent rproxy_t structure */ 323 | evhtp_request_t * upstream_request; /**< the client request */ 324 | evbev_t * upstream_bev; 325 | downstream_c_t * downstream_conn; /**< the downstream connection */ 326 | rule_t * rule; /**< the matched rule */ 327 | htparser * parser; /**< htparser for responses from the downstream */ 328 | event_t * pending_ev; /**< event timer for pending status */ 329 | evbev_t * downstream_bev; 330 | 331 | uint8_t error; /**< set of downstream returns some type of error */ 332 | uint8_t upstream_err; /**< set if the upstream encountered a socket error */ 333 | uint8_t done; /**< request fully proxied and completed */ 334 | uint8_t pending; /**< request is waiting for a downstream connection to be avail */ 335 | uint8_t hit_highwm; 336 | uint8_t hit_upstream_highwm; 337 | uint8_t reading; 338 | 339 | TAILQ_ENTRY(request) next; 340 | }; 341 | 342 | /** 343 | * @brief a structure representing a downstream connection. 344 | */ 345 | struct downstream_c { 346 | downstream_t * parent; /**< the parent downstream structure */ 347 | evbev_t * connection; /**< the bufferevent connection */ 348 | request_t * request; /**< the currently running request */ 349 | event_t * retry_timer; /**< the timer event for reconnecting if down */ 350 | downstream_status status; /**< the status of this downstream */ 351 | double rtt; /**< the last RTT for a request made to the connection */ 352 | uint16_t sport; /**< the source port of the connected socket */ 353 | uint8_t bootstrapped; /**< if not set to 1, the connection will immediately attempt the reconnect */ 354 | struct timeval tv_start; /**< the time which the connection was set to active, used to calculate RTT */ 355 | 356 | TAILQ_ENTRY(downstream_c) next; 357 | }; 358 | 359 | /** 360 | * @brief a container active/idle/downed downstream connections 361 | */ 362 | struct downstream { 363 | downstream_cfg_t * config; /**< this downstreams configuration */ 364 | evbase_t * evbase; 365 | rproxy_t * rproxy; 366 | uint16_t num_active; /**< number of ents in the active list */ 367 | uint16_t num_idle; /**< number of ents in the idle list */ 368 | uint16_t num_down; /**< number of ents in the down list */ 369 | 370 | TAILQ_HEAD(, downstream_c) active; /**< list of active connections */ 371 | TAILQ_HEAD(, downstream_c) idle; /**< list of idle and ready connections */ 372 | TAILQ_HEAD(, downstream_c) down; /**< list of connections which are down */ 373 | }; 374 | 375 | struct rule { 376 | rproxy_t * rproxy; 377 | rule_cfg_t * config; 378 | vhost_t * parent_vhost; /**< the vhost this rule is under */ 379 | lztq * downstreams; /**< list of downstream_t's configured for this rule */ 380 | lztq_elem * last_downstream_used; /**< the last downstream used to service a request. Used for round-robin loadbalancing */ 381 | logger_t * req_log; /**< rule specific request log */ 382 | logger_t * err_log; /**< rule specific error log */ 383 | }; 384 | 385 | TAILQ_HEAD(pending_request_q, request); 386 | 387 | struct rproxy { 388 | rproxy_cfg_t * config; 389 | evhtp_t * htp; 390 | evbase_t * evbase; 391 | struct evdns_base * dns_base; 392 | event_t * request_ev; 393 | server_cfg_t * server_cfg; 394 | logger_t * request_log; /* server specific request logging */ 395 | logger_t * error_log; /* server specific error logging */ 396 | lztq * rules; 397 | lztq * downstreams; /**< list of all downstream_t's */ 398 | int n_pending; /**< number of pending requests */ 399 | pending_request_q_t pending; /**< list of pending upstream request_t's */ 400 | logger_t * req_log; 401 | logger_t * err_log; 402 | }; 403 | 404 | /************************************************ 405 | * Configuration parsing function definitions. 406 | ************************************************/ 407 | rproxy_cfg_t * rproxy_cfg_parse(const char * filename); 408 | 409 | /*********************************************** 410 | * Downstream handling functions 411 | ***********************************************/ 412 | downstream_t * downstream_new(rproxy_t *, downstream_cfg_t *); 413 | void downstream_free(void *); 414 | 415 | downstream_c_t * downstream_connection_new(evbase_t *, downstream_t *); 416 | downstream_c_t * downstream_connection_get(rule_t *); 417 | void downstream_connection_free(downstream_c_t *); 418 | int downstream_connection_init(evbase_t *, downstream_t *); 419 | 420 | int downstream_connection_set_active(downstream_c_t *); 421 | int downstream_connection_set_idle(downstream_c_t *); 422 | int downstream_connection_set_down(downstream_c_t *); 423 | 424 | downstream_t * downstream_find_by_name(lztq *, const char *); 425 | 426 | /* downstream socket handling callbacks */ 427 | void downstream_connection_eventcb(evbev_t *, short, void *); 428 | void downstream_connection_retry(int, short, void *); 429 | 430 | /******************************************** 431 | * SSL verification callback functions 432 | ********************************************/ 433 | int ssl_x509_verifyfn(int, X509_STORE_CTX *); 434 | int ssl_x509_issuedcb(X509_STORE_CTX *, X509 *, X509 *); 435 | ssl_crl_ent_t * ssl_crl_ent_new(evhtp_t *, ssl_crl_cfg_t *); 436 | 437 | 438 | 439 | /*********************************************** 440 | * Request handling funcs (upstream/downstream) 441 | ***********************************************/ 442 | request_t * request_new(rproxy_t *); 443 | void request_free(request_t *); 444 | 445 | /*********************************************** 446 | * SSL helper functions. 447 | ************************************************/ 448 | unsigned char * ssl_subject_tostr(evhtp_ssl_t *); 449 | unsigned char * ssl_issuer_tostr(evhtp_ssl_t *); 450 | unsigned char * ssl_notbefore_tostr(evhtp_ssl_t *); 451 | unsigned char * ssl_notafter_tostr(evhtp_ssl_t *); 452 | unsigned char * ssl_sha1_tostr(evhtp_ssl_t *); 453 | unsigned char * ssl_serial_tostr(evhtp_ssl_t *); 454 | unsigned char * ssl_cipher_tostr(evhtp_ssl_t *); 455 | unsigned char * ssl_cert_tostr(evhtp_ssl_t *); 456 | unsigned char * ssl_x509_ext_tostr(evhtp_ssl_t *, const char *); 457 | 458 | /*********************************************** 459 | * Logging functions 460 | **********************************************/ 461 | logger_t * logger_init(logger_cfg_t * config, int opts); 462 | void logger_log(logger_t * logger, lzlog_level level, char * fmt, ...); 463 | void logger_log_request(logger_t * logger, request_t * request); 464 | void logger_log_request_error(logger_t * logger, request_t * request, char * fmt, ...); 465 | 466 | /*********************************************** 467 | * Utility functions. 468 | **********************************************/ 469 | void util_dropperms(const char * user, const char * group); 470 | int util_daemonize(char * root, int noclose); 471 | int util_set_rlimits(int nofiles); 472 | 473 | evbuf_t * util_request_to_evbuffer(evhtp_request_t * request); 474 | int util_write_header_to_evbuffer(evhtp_header_t * hdr, void * arg); 475 | 476 | int util_glob_match(const char * pattern, const char * string); 477 | int util_glob_match_lztq(lztq *, const char *); 478 | 479 | int util_rm_headers_via_lztq(lztq * tq, evhtp_headers_t * headers); 480 | 481 | #endif 482 | 483 | -------------------------------------------------------------------------------- /src/ssl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright [2012] [Mandiant, inc] 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "rproxy.h" 27 | 28 | #ifdef NO_STRNLEN 29 | static size_t 30 | strnlen(const char * s, size_t maxlen) { 31 | const char * e; 32 | size_t n; 33 | 34 | for (e = s, n = 0; *e && n < maxlen; e++, n++) { 35 | ; 36 | } 37 | 38 | return n; 39 | } 40 | 41 | #endif /* ifdef NO_STRNLEN */ 42 | 43 | #ifdef NO_STRNDUP 44 | static char * 45 | strndup(const char * s, size_t n) { 46 | size_t len = strnlen(s, n); 47 | char * ret; 48 | 49 | if (len < n) { 50 | return strdup(s); 51 | } 52 | 53 | ret = malloc(n + 1); 54 | ret[n] = '\0'; 55 | 56 | strncpy(ret, s, n); 57 | return ret; 58 | } 59 | 60 | #endif /* ifdef NO_STRNDUP */ 61 | 62 | unsigned char * 63 | ssl_subject_tostr(evhtp_ssl_t * ssl) { 64 | unsigned char * subj_str; 65 | char * p; 66 | X509 * cert; 67 | X509_NAME * name; 68 | 69 | if (!ssl) { 70 | return NULL; 71 | } 72 | 73 | if (!(cert = SSL_get_peer_certificate(ssl))) { 74 | return NULL; 75 | } 76 | 77 | if (!(name = X509_get_subject_name(cert))) { 78 | X509_free(cert); 79 | return NULL; 80 | } 81 | 82 | if (!(p = X509_NAME_oneline(name, NULL, 0))) { 83 | X509_free(cert); 84 | return NULL; 85 | } 86 | 87 | subj_str = strdup(p); 88 | 89 | OPENSSL_free(p); 90 | X509_free(cert); 91 | 92 | return subj_str; 93 | } 94 | 95 | unsigned char * 96 | ssl_issuer_tostr(evhtp_ssl_t * ssl) { 97 | X509 * cert; 98 | X509_NAME * name; 99 | char * p; 100 | unsigned char * issr_str; 101 | 102 | if (!ssl) { 103 | return NULL; 104 | } 105 | 106 | if (!(cert = SSL_get_peer_certificate(ssl))) { 107 | return NULL; 108 | } 109 | 110 | if (!(name = X509_get_issuer_name(cert))) { 111 | X509_free(cert); 112 | return NULL; 113 | } 114 | 115 | if (!(p = X509_NAME_oneline(name, NULL, 0))) { 116 | X509_free(cert); 117 | return NULL; 118 | } 119 | 120 | issr_str = strdup(p); 121 | 122 | OPENSSL_free(p); 123 | X509_free(cert); 124 | 125 | return issr_str; 126 | } 127 | 128 | unsigned char * 129 | ssl_notbefore_tostr(evhtp_ssl_t * ssl) { 130 | BIO * bio; 131 | X509 * cert; 132 | ASN1_TIME * time; 133 | size_t len; 134 | unsigned char * time_str; 135 | 136 | if (!ssl) { 137 | return NULL; 138 | } 139 | 140 | if (!(cert = SSL_get_peer_certificate(ssl))) { 141 | return NULL; 142 | } 143 | 144 | if (!(time = X509_get_notBefore(cert))) { 145 | X509_free(cert); 146 | return NULL; 147 | } 148 | 149 | if (!(bio = BIO_new(BIO_s_mem()))) { 150 | X509_free(cert); 151 | return NULL; 152 | } 153 | 154 | if (!ASN1_TIME_print(bio, time)) { 155 | BIO_free(bio); 156 | X509_free(cert); 157 | return NULL; 158 | } 159 | 160 | if ((len = BIO_pending(bio)) == 0) { 161 | BIO_free(bio); 162 | X509_free(cert); 163 | return NULL; 164 | } 165 | 166 | if (!(time_str = calloc(len + 1, 1))) { 167 | return NULL; 168 | } 169 | 170 | BIO_read(bio, time_str, len); 171 | 172 | BIO_free(bio); 173 | X509_free(cert); 174 | 175 | return time_str; 176 | } /* ssl_notbefore_tostr */ 177 | 178 | unsigned char * 179 | ssl_notafter_tostr(evhtp_ssl_t * ssl) { 180 | BIO * bio; 181 | X509 * cert; 182 | ASN1_TIME * time; 183 | size_t len; 184 | unsigned char * time_str; 185 | 186 | if (!ssl) { 187 | return NULL; 188 | } 189 | 190 | if (!(cert = SSL_get_peer_certificate(ssl))) { 191 | return NULL; 192 | } 193 | 194 | if (!(time = X509_get_notAfter(cert))) { 195 | X509_free(cert); 196 | return NULL; 197 | } 198 | 199 | if (!(bio = BIO_new(BIO_s_mem()))) { 200 | X509_free(cert); 201 | return NULL; 202 | } 203 | 204 | if (!ASN1_TIME_print(bio, time)) { 205 | BIO_free(bio); 206 | X509_free(cert); 207 | return NULL; 208 | } 209 | 210 | if ((len = BIO_pending(bio)) == 0) { 211 | BIO_free(bio); 212 | X509_free(cert); 213 | return NULL; 214 | } 215 | 216 | if (!(time_str = calloc(len + 1, 1))) { 217 | return NULL; 218 | } 219 | 220 | BIO_read(bio, time_str, len); 221 | 222 | BIO_free(bio); 223 | X509_free(cert); 224 | 225 | return time_str; 226 | } /* ssl_notafter_tostr */ 227 | 228 | unsigned char * 229 | ssl_sha1_tostr(evhtp_ssl_t * ssl) { 230 | EVP_MD * md_alg; 231 | X509 * cert; 232 | unsigned int n; 233 | unsigned char md[EVP_MAX_MD_SIZE]; 234 | unsigned char* buf = NULL; 235 | size_t offset; 236 | size_t nsz; 237 | int sz; 238 | int i; 239 | 240 | if (!ssl) { 241 | return NULL; 242 | } 243 | 244 | md_alg = EVP_sha1(); 245 | 246 | if (!md_alg) { 247 | return NULL; 248 | } 249 | 250 | if (!(cert = SSL_get_peer_certificate(ssl))) { 251 | return NULL; 252 | } 253 | 254 | n = 0; 255 | if (!X509_digest(cert, md_alg, md, &n)) { 256 | return NULL; 257 | } 258 | 259 | nsz = 3 * n + 1; 260 | buf = (unsigned char *)calloc(nsz, 1); 261 | if (buf) { 262 | offset = 0; 263 | for (i = 0; i < n; i++) { 264 | sz = snprintf(buf + offset, nsz - offset, "%02X%c", md[i], (i + 1 == n) ? 0 : ':'); 265 | offset += sz; 266 | 267 | if (sz < 0 || offset >= nsz) { 268 | free(buf); 269 | buf = NULL; 270 | break; 271 | } 272 | } 273 | } 274 | 275 | X509_free(cert); 276 | 277 | return buf; 278 | } /* ssl_sha1 */ 279 | 280 | unsigned char * 281 | ssl_serial_tostr(evhtp_ssl_t * ssl) { 282 | BIO * bio; 283 | X509 * cert; 284 | size_t len; 285 | unsigned char * ser_str; 286 | 287 | if (!ssl) { 288 | return NULL; 289 | } 290 | 291 | if (!(cert = SSL_get_peer_certificate(ssl))) { 292 | return NULL; 293 | } 294 | 295 | if (!(bio = BIO_new(BIO_s_mem()))) { 296 | X509_free(cert); 297 | return NULL; 298 | } 299 | 300 | i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert)); 301 | 302 | if ((len = BIO_pending(bio)) == 0) { 303 | BIO_free(bio); 304 | X509_free(cert); 305 | return NULL; 306 | } 307 | 308 | if (!(ser_str = calloc(len + 1, 1))) { 309 | return NULL; 310 | } 311 | 312 | BIO_read(bio, ser_str, len); 313 | 314 | X509_free(cert); 315 | BIO_free(bio); 316 | 317 | return ser_str; 318 | } 319 | 320 | unsigned char * 321 | ssl_cipher_tostr(evhtp_ssl_t * ssl) { 322 | const SSL_CIPHER * cipher; 323 | const char * p; 324 | unsigned char * cipher_str; 325 | 326 | if (!ssl) { 327 | return NULL; 328 | } 329 | 330 | if (!(cipher = SSL_get_current_cipher(ssl))) { 331 | return NULL; 332 | } 333 | 334 | if (!(p = SSL_CIPHER_get_name(cipher))) { 335 | return NULL; 336 | } 337 | 338 | cipher_str = strdup(p); 339 | 340 | return cipher_str; 341 | } 342 | 343 | unsigned char * 344 | ssl_cert_tostr(evhtp_ssl_t * ssl) { 345 | X509 * cert; 346 | BIO * bio; 347 | unsigned char * raw_cert_str; 348 | unsigned char * cert_str; 349 | unsigned char * p; 350 | size_t raw_cert_len; 351 | size_t cert_len; 352 | int i; 353 | 354 | if (!ssl) { 355 | return NULL; 356 | } 357 | 358 | if (!(cert = SSL_get_peer_certificate(ssl))) { 359 | return NULL; 360 | } 361 | 362 | if (!(bio = BIO_new(BIO_s_mem()))) { 363 | X509_free(cert); 364 | return NULL; 365 | } 366 | 367 | if (!PEM_write_bio_X509(bio, cert)) { 368 | BIO_free(bio); 369 | X509_free(cert); 370 | 371 | return NULL; 372 | } 373 | 374 | raw_cert_len = BIO_pending(bio); 375 | raw_cert_str = calloc(raw_cert_len + 1, 1); 376 | 377 | BIO_read(bio, raw_cert_str, raw_cert_len); 378 | 379 | cert_len = raw_cert_len - 1; 380 | 381 | for (i = 0; i < raw_cert_len - 1; i++) { 382 | if (raw_cert_str[i] == '\n') { 383 | /* 384 | * \n's will be converted to \r\n\t, so we must reserve 385 | * enough space for that much data. 386 | */ 387 | cert_len += 2; 388 | } 389 | } 390 | 391 | /* 2 extra chars, one for possible last char (if not '\n'), and one for NULL terminator */ 392 | cert_str = calloc(cert_len + 2, 1); 393 | p = cert_str; 394 | 395 | for (i = 0; i < raw_cert_len - 1; i++) { 396 | if (raw_cert_str[i] == '\n') { 397 | *p++ = '\r'; 398 | *p++ = '\n'; 399 | *p++ = '\t'; 400 | } else { 401 | *p++ = raw_cert_str[i]; 402 | } 403 | } 404 | 405 | /* Don't assume last character is '\n' */ 406 | if (raw_cert_str[i] != '\n') { 407 | *p++ = raw_cert_str[i]; 408 | } 409 | 410 | BIO_free(bio); 411 | X509_free(cert); 412 | free(raw_cert_str); 413 | 414 | return cert_str; 415 | } /* ssl_cert_tostr */ 416 | 417 | unsigned char * 418 | ssl_x509_ext_tostr(evhtp_ssl_t * ssl, const char * oid) { 419 | unsigned char * ext_str; 420 | X509 * cert; 421 | ASN1_OBJECT * oid_obj; 422 | 423 | STACK_OF(X509_EXTENSION) * exts; 424 | int oid_pos; 425 | X509_EXTENSION * ext; 426 | ASN1_OCTET_STRING * octet; 427 | const unsigned char * octet_data; 428 | long xlen; 429 | int xtag; 430 | int xclass; 431 | 432 | if (!ssl) { 433 | return NULL; 434 | } 435 | 436 | if (!(cert = SSL_get_peer_certificate(ssl))) { 437 | return NULL; 438 | } 439 | 440 | if (!(oid_obj = OBJ_txt2obj(oid, 1))) { 441 | X509_free(cert); 442 | return NULL; 443 | } 444 | 445 | ext_str = NULL; 446 | exts = cert->cert_info->extensions; 447 | oid_pos = X509v3_get_ext_by_OBJ(exts, oid_obj, -1); 448 | 449 | if (!(ext = X509_get_ext(cert, oid_pos))) { 450 | ASN1_OBJECT_free(oid_obj); 451 | X509_free(cert); 452 | return NULL; 453 | } 454 | 455 | if (!(octet = X509_EXTENSION_get_data(ext))) { 456 | ASN1_OBJECT_free(oid_obj); 457 | X509_free(cert); 458 | return NULL; 459 | } 460 | 461 | octet_data = octet->data; 462 | 463 | if (ASN1_get_object(&octet_data, &xlen, &xtag, &xclass, octet->length)) { 464 | ASN1_OBJECT_free(oid_obj); 465 | X509_free(cert); 466 | return NULL; 467 | } 468 | 469 | /* We're only supporting string data. Could optionally add support 470 | * for encoded binary data */ 471 | 472 | if (xlen > 0 && xtag == 0x0C && octet->type == V_ASN1_OCTET_STRING) { 473 | ext_str = strndup(octet_data, xlen); 474 | } 475 | 476 | ASN1_OBJECT_free(oid_obj); 477 | X509_free(cert); 478 | 479 | return ext_str; 480 | } /* ssl_x509_ext_tostr */ 481 | 482 | static int 483 | ssl_crl_ent_should_reload(ssl_crl_ent_t * crl_ent) { 484 | struct stat statb; 485 | 486 | if (crl_ent->cfg->filename) { 487 | if (stat(crl_ent->cfg->filename, &statb) == -1) { 488 | /* file doesn't exist, we need to error out of this */ 489 | return -1; 490 | } 491 | 492 | #if __APPLE__ 493 | if (statb.st_mtimespec.tv_sec > crl_ent->last_file_mod.tv_sec || 494 | statb.st_mtimespec.tv_nsec > crl_ent->last_file_mod.tv_nsec) { 495 | /* file has been modified, so return yes */ 496 | return 1; 497 | } 498 | #else 499 | if (statb.st_mtime > crl_ent->last_file_mod) { 500 | return 1; 501 | } 502 | #endif 503 | } 504 | 505 | if (crl_ent->cfg->dirname) { 506 | if (stat(crl_ent->cfg->dirname, &statb) == -1) { 507 | return -1; 508 | } 509 | 510 | #if __APPLE__ 511 | if (statb.st_mtimespec.tv_sec > crl_ent->last_dir_mod.tv_sec || 512 | statb.st_mtimespec.tv_nsec > crl_ent->last_dir_mod.tv_nsec) { 513 | return 1; 514 | } 515 | #else 516 | if (statb.st_mtime > crl_ent->last_file_mod) { 517 | return 1; 518 | } 519 | #endif 520 | } 521 | 522 | return 0; 523 | } /* ssl_crl_ent_should_reload */ 524 | 525 | int 526 | ssl_crl_ent_reload(ssl_crl_ent_t * crl_ent) { 527 | X509_LOOKUP * lookup; 528 | X509_STORE * old_crl; 529 | logger_t * logger; 530 | struct stat file_stat; 531 | int res; 532 | 533 | if (crl_ent == NULL) { 534 | return -1; 535 | } 536 | 537 | /* make sure we have either (or both) a crl file or directory */ 538 | if (crl_ent->cfg->filename == NULL && crl_ent->cfg->dirname == NULL) { 539 | return -1; 540 | } 541 | 542 | logger = (crl_ent->rproxy != NULL) ? crl_ent->rproxy->err_log : NULL; 543 | 544 | if ((res = ssl_crl_ent_should_reload(crl_ent)) <= 0) { 545 | /* we should error on a negative status here, but for right now we 546 | * figure the crl list does not need to be reloaded. 547 | */ 548 | if (res < 0) { 549 | logger_log(logger, lzlog_err, "SSL: CRL should reload error:%i", res); 550 | } 551 | return res; 552 | } 553 | 554 | /* if this crl_ent already has an allocated X509_STORE, we set it aside to 555 | * be free'd if all goes well with the new load. 556 | */ 557 | old_crl = NULL; 558 | 559 | if (crl_ent->crl != NULL) { 560 | old_crl = crl_ent->crl; 561 | crl_ent->crl = NULL; 562 | } 563 | 564 | /* initialize our own X509 storage stack. We utilize our own stack and our 565 | * own manual CRL verification because OpenSSL does not give us a method of 566 | * determining what a normal X509 is versus what a X509 CRL is. Since we 567 | * want to allow RProxy to dynamically read in CRL information as it comes 568 | * in, we must do it ourselves. 569 | */ 570 | if ((crl_ent->crl = X509_STORE_new()) == NULL) { 571 | /* something bad happened, so add the old crl back and return an error */ 572 | logger_log(logger, lzlog_err, "SSL: CRL X509 STORE alloc failure"); 573 | crl_ent->crl = old_crl; 574 | return -1; 575 | } 576 | 577 | if (crl_ent->cfg->filename != NULL) { 578 | if (stat(crl_ent->cfg->filename, &file_stat) == -1) { 579 | logger_log(logger, lzlog_err, "SSL: CRL stat failed: %s", crl_ent->cfg->filename); 580 | X509_STORE_free(crl_ent->crl); 581 | crl_ent->crl = old_crl; 582 | return -1; 583 | } 584 | 585 | if (!S_ISREG(file_stat.st_mode)) { 586 | logger_log(logger, lzlog_err, "SSL: CRL not a regular file: %s", crl_ent->cfg->filename); 587 | X509_STORE_free(crl_ent->crl); 588 | crl_ent->crl = old_crl; 589 | return -1; 590 | } 591 | 592 | /* attempt to add the filename to our x509 store */ 593 | if (!(lookup = X509_STORE_add_lookup(crl_ent->crl, X509_LOOKUP_file()))) { 594 | logger_log(logger, lzlog_err, "SSL: Cannot add CRL LOOKUP to store"); 595 | X509_STORE_free(crl_ent->crl); 596 | crl_ent->crl = old_crl; 597 | return -1; 598 | } 599 | 600 | /* copy over the value of the last time this file was modified which we 601 | * use to check whether we should roll this crl over or not when the 602 | * even timer is triggered. 603 | * */ 604 | #ifdef __APPLE__ 605 | memcpy(&crl_ent->last_file_mod, &file_stat.st_mtimespec, sizeof(struct timespec)); 606 | #else 607 | crl_ent->last_file_mod = file_stat.st_mtime; 608 | #endif 609 | 610 | if (!X509_LOOKUP_load_file(lookup, crl_ent->cfg->filename, X509_FILETYPE_PEM)) { 611 | logger_log(logger, lzlog_err, "SSL: Cannot add CRL to store: %s", crl_ent->cfg->filename); 612 | X509_STORE_free(crl_ent->crl); 613 | crl_ent->crl = old_crl; 614 | return -1; 615 | } else { 616 | logger_log(logger, lzlog_info, "SSL: CRL reloaded: %s ", crl_ent->cfg->filename); 617 | } 618 | } 619 | 620 | if (crl_ent->cfg->dirname != NULL) { 621 | if (stat(crl_ent->cfg->dirname, &file_stat) == -1) { 622 | logger_log(logger, lzlog_err, "SSL: CRL stat failed: %s", crl_ent->cfg->dirname); 623 | X509_STORE_free(crl_ent->crl); 624 | crl_ent->crl = old_crl; 625 | return -1; 626 | } 627 | 628 | if (!S_ISDIR(file_stat.st_mode)) { 629 | logger_log(logger, lzlog_err, "SSL: CRL not a directory: %s", crl_ent->cfg->dirname); 630 | X509_STORE_free(crl_ent->crl); 631 | crl_ent->crl = old_crl; 632 | return -1; 633 | } 634 | 635 | if (!(lookup = X509_STORE_add_lookup(crl_ent->crl, X509_LOOKUP_hash_dir()))) { 636 | logger_log(logger, lzlog_err, "SSL: Cannot add CRL LOOKUP to store"); 637 | X509_STORE_free(crl_ent->crl); 638 | crl_ent->crl = old_crl; 639 | return -1; 640 | } 641 | 642 | #ifdef __APPLE__ 643 | memcpy(&crl_ent->last_dir_mod, &file_stat.st_mtimespec, sizeof(struct timespec)); 644 | #else 645 | crl_ent->last_dir_mod = file_stat.st_mtime; 646 | #endif 647 | 648 | 649 | if (!X509_LOOKUP_add_dir(lookup, crl_ent->cfg->dirname, X509_FILETYPE_PEM)) { 650 | logger_log(logger, lzlog_err, "SSL: Cannot add CRL directory to store: %s", crl_ent->cfg->dirname); 651 | X509_STORE_free(crl_ent->crl); 652 | crl_ent->crl = old_crl; 653 | return -1; 654 | } else { 655 | logger_log(logger, lzlog_info, "SSL: CRL directory reloaded: %s ", crl_ent->cfg->dirname); 656 | } 657 | } 658 | 659 | if (old_crl) { 660 | X509_STORE_free(old_crl); 661 | } 662 | 663 | return 0; 664 | } /* ssl_crl_ent_reload */ 665 | 666 | static void 667 | ssl_reload_timercb(int sock, short which, void * arg) { 668 | ssl_crl_ent_t * crl_ent; 669 | 670 | if (!(crl_ent = (ssl_crl_ent_t *)arg)) { 671 | return; 672 | } 673 | 674 | /* TODO: log stuff here */ 675 | pthread_mutex_lock(&crl_ent->lock); 676 | { 677 | ssl_crl_ent_reload(crl_ent); 678 | event_add(crl_ent->reload_timer_ev, &crl_ent->cfg->reload_timer); 679 | } 680 | pthread_mutex_unlock(&crl_ent->lock); 681 | } 682 | 683 | ssl_crl_ent_t * 684 | ssl_crl_ent_new(evhtp_t * htp, ssl_crl_cfg_t * config) { 685 | ssl_crl_ent_t * crl_ent; 686 | 687 | if (htp == NULL || config == NULL) { 688 | return NULL; 689 | } 690 | 691 | if (!(crl_ent = calloc(sizeof(ssl_crl_ent_t), 1))) { 692 | return NULL; 693 | } 694 | 695 | crl_ent->cfg = config; 696 | crl_ent->htp = htp; 697 | crl_ent->reload_timer_ev = evtimer_new(htp->evbase, ssl_reload_timercb, crl_ent); 698 | pthread_mutex_init(&crl_ent->lock, NULL); 699 | 700 | ssl_crl_ent_reload(crl_ent); 701 | event_add(crl_ent->reload_timer_ev, &config->reload_timer); 702 | 703 | return crl_ent; 704 | } 705 | 706 | /* 707 | * ssl_verify_crl was inspired by Apache's mod_ssl. That code is licensed 708 | * under the Apache 2.0 license: 709 | * 710 | * http://www.apache.org/licenses/LICENSE-2.0 711 | * 712 | * Following are comments lifted from the original mod_ssl code: 713 | * 714 | * OpenSSL provides the general mechanism to deal with CRLs but does not 715 | * use them automatically when verifying certificates, so we do it 716 | * explicitly here. We will check the CRL for the currently checked 717 | * certificate, if there is such a CRL in the store. 718 | * 719 | * We come through this procedure for each certificate in the certificate 720 | * chain, starting with the root-CA's certificate. At each step we've to 721 | * both verify the signature on the CRL (to make sure it's a valid CRL) 722 | * and it's revocation list (to make sure the current certificate isn't 723 | * revoked). But because to check the signature on the CRL we need the 724 | * public key of the issuing CA certificate (which was already processed 725 | * one round before), we've a little problem. But we can both solve it and 726 | * at the same time optimize the processing by using the following 727 | * verification scheme (idea and code snippets borrowed from the GLOBUS 728 | * project): 729 | * 730 | * 1. We'll check the signature of a CRL in each step when we find a CRL 731 | * through the _subject_ name of the current certificate. This CRL 732 | * itself will be needed the first time in the next round, of course. 733 | * But we do the signature processing one round before this where the 734 | * public key of the CA is available. 735 | * 736 | * 2. We'll check the revocation list of a CRL in each step when 737 | * we find a CRL through the _issuer_ name of the current certificate. 738 | * This CRLs signature was then already verified one round before. 739 | * 740 | * This verification scheme allows a CA to revoke its own certificate as 741 | * well, of course. 742 | */ 743 | 744 | 745 | static int 746 | ssl_verify_crl(int ok, X509_STORE_CTX * ctx, ssl_crl_ent_t * crl_ent, logger_t * logger) { 747 | const size_t kSz = 255; 748 | char buf[kSz + 1]; 749 | X509 * cert; 750 | X509_NAME * subject; 751 | X509_NAME * issuer; 752 | X509_CRL * crl; 753 | X509_STORE_CTX store_ctx; 754 | EVP_PKEY * public_key; 755 | X509_OBJECT x509_obj = { 0 }; 756 | int res; 757 | int timestamp_res; 758 | 759 | if (crl_ent == NULL) { 760 | return ok; 761 | } 762 | 763 | cert = X509_STORE_CTX_get_current_cert(ctx); 764 | subject = X509_get_subject_name(cert); 765 | issuer = X509_get_issuer_name(cert); 766 | 767 | /* lookup the CRL using the subject of the cert */ 768 | X509_STORE_CTX_init(&store_ctx, crl_ent->crl, NULL, NULL); 769 | res = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, subject, &x509_obj); 770 | crl = x509_obj.data.crl; 771 | 772 | if (res > 0 && crl) { 773 | public_key = X509_get_pubkey(cert); 774 | 775 | res = X509_CRL_verify(crl, public_key); 776 | 777 | if (public_key != NULL) { 778 | EVP_PKEY_free(public_key); 779 | } 780 | 781 | if (res <= 0) { 782 | X509_OBJECT_free_contents(&x509_obj); 783 | logger_log(logger, lzlog_err, "SSL: CRL validation failed:%i:%s", res, X509_NAME_oneline(subject, buf, kSz)); 784 | return 0; 785 | } 786 | 787 | timestamp_res = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl)); 788 | 789 | if (timestamp_res == 0) { 790 | /* invalid nextupdate found */ 791 | X509_OBJECT_free_contents(&x509_obj); 792 | logger_log(logger, lzlog_err, "SSL: CRL nextupdate invalid:%s", X509_NAME_oneline(subject, buf, kSz)); 793 | return 0; 794 | } 795 | 796 | if (timestamp_res < 0) { 797 | /* CRL is expired */ 798 | X509_OBJECT_free_contents(&x509_obj); 799 | logger_log(logger, lzlog_err, "SSL: CRL expired:%s", X509_NAME_oneline(subject, buf, kSz)); 800 | return 0; 801 | } 802 | 803 | X509_OBJECT_free_contents(&x509_obj); 804 | } 805 | 806 | memset((void *)&x509_obj, 0, sizeof(x509_obj)); 807 | 808 | X509_STORE_CTX_init(&store_ctx, crl_ent->crl, NULL, NULL); 809 | res = X509_STORE_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &x509_obj); 810 | crl = x509_obj.data.crl; 811 | 812 | if ((res > 0) && crl != NULL) { 813 | int num_revoked; 814 | int i; 815 | 816 | 817 | num_revoked = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); 818 | 819 | for (i = 0; i < num_revoked; i++) { 820 | X509_REVOKED * revoked; 821 | ASN1_INTEGER * asn1_serial; 822 | 823 | revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); 824 | asn1_serial = revoked->serialNumber; 825 | 826 | if (!ASN1_INTEGER_cmp(asn1_serial, X509_get_serialNumber(cert))) { 827 | X509_OBJECT_free_contents(&x509_obj); 828 | logger_log(logger, lzlog_warn, "SSL: Certificate revoked by CRL:%s", X509_NAME_oneline(subject, buf, kSz)); 829 | return 0; 830 | } 831 | } 832 | 833 | X509_OBJECT_free_contents(&x509_obj); 834 | } 835 | 836 | #ifdef RPROXY_DEBUG 837 | if (ok) { 838 | logger_log(logger, lzlog_debug, "SSL: CRL ok", X509_NAME_oneline(subject, buf, kSz)); 839 | } 840 | #endif 841 | 842 | return ok; 843 | } /* ssl_verify_crl */ 844 | 845 | int 846 | ssl_x509_verifyfn(int ok, X509_STORE_CTX * store) { 847 | char buf[256]; 848 | X509 * err_cert; 849 | int err; 850 | int depth; 851 | SSL * ssl; 852 | evhtp_connection_t * connection; 853 | evhtp_ssl_cfg_t * ssl_cfg; 854 | rproxy_t * rproxy; 855 | 856 | err_cert = X509_STORE_CTX_get_current_cert(store); 857 | err = X509_STORE_CTX_get_error(store); 858 | depth = X509_STORE_CTX_get_error_depth(store); 859 | ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()); 860 | 861 | X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); 862 | 863 | connection = SSL_get_app_data(ssl); 864 | ssl_cfg = connection->htp->ssl_cfg; 865 | rproxy = evthr_get_aux(connection->thread); 866 | assert(rproxy != NULL); 867 | 868 | if (depth > ssl_cfg->verify_depth) { 869 | ok = 0; 870 | err = X509_V_ERR_CERT_CHAIN_TOO_LONG; 871 | 872 | X509_STORE_CTX_set_error(store, err); 873 | } 874 | 875 | 876 | if (!ok) { 877 | logger_log(rproxy->err_log, lzlog_err, 878 | "SSL: verify error:num=%d:%s:depth=%d:%s", err, 879 | X509_verify_cert_error_string(err), depth, buf); 880 | } 881 | 882 | 883 | /* right now the only thing using the evhtp argument is the crl_ent_t's, in 884 | * the future this will become more generic. So here we check to see if the 885 | * CRL checking is enabled, and if it is, do CRL verification. 886 | */ 887 | if (connection->htp->arg) { 888 | ssl_crl_ent_t * crl_ent = (ssl_crl_ent_t *)connection->htp->arg; 889 | 890 | pthread_mutex_lock(&crl_ent->lock); 891 | { 892 | ok = ssl_verify_crl(ok, store, (ssl_crl_ent_t *)connection->htp->arg, rproxy->err_log); 893 | } 894 | pthread_mutex_unlock(&crl_ent->lock); 895 | } 896 | 897 | return ok; 898 | } /* ssl_x509_verifyfn */ 899 | 900 | int 901 | ssl_x509_issuedcb(X509_STORE_CTX * ctx, X509 * x, X509 * issuer) { 902 | return 1; 903 | } 904 | 905 | --------------------------------------------------------------------------------