├── tools └── .gitkeep ├── bud.gyp ├── test ├── keys │ ├── ca2-serial │ ├── ca2-database.txt.old │ ├── ca1-cert.srl │ ├── ca2-cert.srl │ ├── ca2-database.txt.attr │ ├── ca2-database.txt │ ├── ec-key.pem │ ├── ec-csr.pem │ ├── ca2-crl.pem │ ├── agent2.cnf │ ├── agent3.cnf │ ├── ca1.cnf │ ├── agent4.cnf │ ├── ec.cnf │ ├── agent2-csr.pem │ ├── agent3-csr.pem │ ├── agent4-csr.pem │ ├── agent1-csr.pem │ ├── agent1.cnf │ ├── ca2.cnf │ ├── agent2-key.pem │ ├── agent3-key.pem │ ├── agent4-key.pem │ ├── agent1-key.pem │ ├── ca1-cert.pem │ ├── ca2-cert.pem │ ├── agent3-cert.pem │ ├── agent2-cert.pem │ ├── agent4-cert.pem │ ├── ca1-key.pem │ ├── ca2-key.pem │ ├── ec-cert.pem │ ├── issuer-cert.pem │ ├── good-cert.pem │ ├── agent1-cert.pem │ ├── good-key.pem │ ├── issuer-key.pem │ └── Makefile ├── x-forward-test.js ├── ticket-test.js ├── ipc-test.js ├── ocsp-test.js ├── proxyline-test.js ├── basic-test.js └── sni-test.js ├── src ├── error.h ├── config │ ├── ocsp.h │ ├── ticket.h │ ├── tracing.h │ ├── context.h │ ├── files.h │ ├── utils.h │ ├── ocsp.c │ ├── files.c │ ├── tracing.c │ └── ticket.c ├── version.h ├── bio.h ├── worker.h ├── ocsp.h ├── sni.h ├── xforward.h ├── server.h ├── avail.h ├── logger.h ├── master.h ├── client-common.c ├── tracing.h ├── bud.d ├── ipc.h ├── client.h ├── bud.c ├── http-pool.h ├── server.c ├── bio.c ├── bud_provider.d ├── sni.c ├── common.h ├── client-common.h ├── worker.c ├── queue.h ├── xforward.c ├── logger.c ├── ocsp.c ├── config.h ├── avail.c └── ipc.c ├── benchmarks ├── big2-rps.png ├── big2-resp.png ├── normal-rps.png ├── normal-resp.png └── index.md ├── Makefile ├── .gitmodules ├── options.gypi ├── deps └── parson │ └── parson.gyp ├── .npmignore ├── config.gypi ├── .gitignore ├── include └── bud │ ├── common.h │ ├── logger.h │ ├── ipc.h │ ├── tracing.h │ └── error.h ├── .travis.yml ├── binding.gyp ├── keys ├── cert.pem └── key.pem ├── package.json ├── npm └── release.js ├── default-config.json ├── lib └── bud.js └── bud.gyp.json /tools/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bud.gyp: -------------------------------------------------------------------------------- 1 | bud.gyp.json -------------------------------------------------------------------------------- /test/keys/ca2-serial: -------------------------------------------------------------------------------- 1 | 01 2 | -------------------------------------------------------------------------------- /test/keys/ca2-database.txt.old: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/keys/ca1-cert.srl: -------------------------------------------------------------------------------- 1 | 870FD579650933AC 2 | -------------------------------------------------------------------------------- /test/keys/ca2-cert.srl: -------------------------------------------------------------------------------- 1 | A77F59E8709D9957 2 | -------------------------------------------------------------------------------- /src/error.h: -------------------------------------------------------------------------------- 1 | #include "include/bud/error.h" 2 | -------------------------------------------------------------------------------- /test/keys/ca2-database.txt.attr: -------------------------------------------------------------------------------- 1 | unique_subject = yes 2 | -------------------------------------------------------------------------------- /benchmarks/big2-rps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indutny/bud/HEAD/benchmarks/big2-rps.png -------------------------------------------------------------------------------- /benchmarks/big2-resp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indutny/bud/HEAD/benchmarks/big2-resp.png -------------------------------------------------------------------------------- /benchmarks/normal-rps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indutny/bud/HEAD/benchmarks/normal-rps.png -------------------------------------------------------------------------------- /benchmarks/normal-resp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indutny/bud/HEAD/benchmarks/normal-resp.png -------------------------------------------------------------------------------- /src/config/ocsp.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CONFIG_OCSP_H_ 2 | #define SRC_CONFIG_OCSP_H_ 3 | 4 | #endif /* SRC_CONFIG_OCSP_H_ */ 5 | -------------------------------------------------------------------------------- /test/keys/ca2-database.txt: -------------------------------------------------------------------------------- 1 | R 410913170331Z 140429170331Z A77F59E8709D9957 unknown /C=US/ST=CA/L=SF/O=Joyent/OU=Node.js/CN=agent4/emailAddress=ry@tinyclouds.org 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @echo "run \`make publish\` to publish package" 3 | @exit 1 4 | 5 | publish: 6 | rm -rf bin/ && mkdir bin && touch bin/bud 7 | npm publish 8 | 9 | .PHONY: all publish 10 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_VERSION_H_ 2 | #define SRC_VERSION_H_ 3 | 4 | #define BUD_VERSION_MAJOR 4 5 | #define BUD_VERSION_MINOR 0 6 | #define BUD_VERSION_PATCH 28 7 | 8 | #endif /* SRC_VERSION_H_ */ 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/uv"] 2 | path = deps/uv 3 | url = git://github.com/joyent/libuv.git 4 | [submodule "deps/parson/parson"] 5 | path = deps/parson/parson 6 | url = git://github.com/kgabis/parson.git 7 | -------------------------------------------------------------------------------- /options.gypi: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "uv_library": "static_library", 4 | "bud_asan": "false", 5 | 6 | # node-gyp 7 | "node_root_dir": "", 8 | "module_root_dir": ".", 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /src/bio.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_BIO_H_ 2 | #define SRC_BIO_H_ 3 | 4 | #include "openssl/bio.h" 5 | #include "ringbuffer.h" 6 | 7 | BIO* bud_bio_new(); 8 | ringbuffer* bud_bio_get_buffer(BIO* bio); 9 | 10 | #endif /* SRC_BIO_H_ */ 11 | -------------------------------------------------------------------------------- /src/worker.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_WORKER_H_ 2 | #define SRC_WORKER_H_ 3 | 4 | #include "src/config.h" 5 | #include "src/error.h" 6 | 7 | bud_error_t bud_worker(bud_config_t* config); 8 | bud_error_t bud_worker_finalize(bud_config_t* config); 9 | 10 | #endif /* SRC_WORKER_H_ */ 11 | -------------------------------------------------------------------------------- /deps/parson/parson.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [{ 3 | "target_name": "parson", 4 | "type": "<(library)", 5 | "direct_dependent_settings": { 6 | "include_dirs": [ "parson" ], 7 | }, 8 | "sources": [ 9 | "parson/parson.c", 10 | ], 11 | }] 12 | } 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | benchmarks/ 2 | tools/gyp 3 | *.json 4 | !bud.gyp.json 5 | !package.json 6 | *.sh 7 | build/ 8 | out/ 9 | npm/bud 10 | npm/bud.exe 11 | npm-debug.log 12 | node_modules/ 13 | test/keys/ 14 | 15 | # ringbuffer 16 | deps/ringbuffer/tools/gyp 17 | deps/ringbuffer/out 18 | gypkg_deps/ 19 | -------------------------------------------------------------------------------- /config.gypi: -------------------------------------------------------------------------------- 1 | { 2 | # NOTE: BIG FAT HACK FOR NODE-GYP 3 | "variables": { 4 | "library": "static_library", 5 | "openssl_fips": "" 6 | }, 7 | 8 | "target_defaults": { 9 | "type": "static_library", 10 | "include_dirs": [ 11 | "deps/uv/include", 12 | "deps/openssl/openssl/include", 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/keys/ec-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BggqhkjOPQMBBw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MHcCAQEEINb2wnyl/0qAjF5MBnnsznk7NohFYy3o0GoLVTWUcy84oAoGCCqGSM49 6 | AwEHoUQDQgAEuHSSFUgE7gCw3kpxeJJ0v/Po5c/3LZ8sMZhIXA4B0FykX8glRHA4 7 | MhEyiOYY8DTZ6nGxnRBP1n97KBZ234L1ig== 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | tools/gyp 3 | *.json 4 | !bud.gyp.json 5 | !package.json 6 | *.sh 7 | build/ 8 | out/ 9 | npm/bud 10 | npm/bud.exe 11 | npm-debug.log 12 | node_modules/ 13 | gypkg_deps/ 14 | 15 | # ringbuffer 16 | deps/ringbuffer/tools/gyp 17 | deps/ringbuffer/out 18 | 19 | # Text editors swap files/backups 20 | *.swp 21 | *~ 22 | .DS_Store 23 | -------------------------------------------------------------------------------- /src/ocsp.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_OCSP_H_ 2 | #define SRC_OCSP_H_ 3 | 4 | #include "openssl/ssl.h" 5 | 6 | #include "src/error.h" 7 | 8 | /* Forward declaration */ 9 | struct bud_client_s; 10 | 11 | bud_error_t bud_client_ocsp_stapling(struct bud_client_s* client); 12 | int bud_client_stapling_cb(SSL* ssl, void* arg); 13 | 14 | #endif /* SRC_OCSP_H_ */ 15 | -------------------------------------------------------------------------------- /src/sni.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_SNI_H_ 2 | #define SRC_SNI_H_ 3 | 4 | #include "parson.h" 5 | 6 | #include "src/error.h" 7 | #include "src/config.h" 8 | 9 | bud_error_t bud_sni_from_json(bud_config_t* config, 10 | struct json_value_t* json, 11 | bud_context_t* ctx); 12 | 13 | #endif /* SRC_SNI_H_ */ 14 | -------------------------------------------------------------------------------- /include/bud/common.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_BUD_COMMON_H_ 2 | #define INCLUDE_BUD_COMMON_H_ 3 | 4 | #if defined(__GNUC__) && ((__GNUC__ >= 4) || \ 5 | (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) 6 | # ifdef BUILDING_V8_SHARED 7 | # define BUD_EXPORT __attribute__ ((visibility("default"))) 8 | # else 9 | # define BUD_EXPORT 10 | # endif 11 | #else 12 | # define BUD_EXPORT 13 | #endif 14 | 15 | #endif /* INCLUDE_BUD_COMMON_H_ */ 16 | -------------------------------------------------------------------------------- /src/xforward.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_XFORWARD_H_ 2 | #define SRC_XFORWARD_H_ 3 | 4 | #include "src/client-common.h" 5 | 6 | /* Forward declarations */ 7 | struct bud_client_s; 8 | 9 | int bud_client_xforward_done(struct bud_client_s* client); 10 | void bud_client_xforward_skip(struct bud_client_s* client, size_t size); 11 | bud_client_error_t bud_client_prepend_xforward(struct bud_client_s* client); 12 | 13 | #endif /* SRC_XFORWARD_H_ */ 14 | -------------------------------------------------------------------------------- /src/config/ticket.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CONFIG_TICKET_H_ 2 | #define SRC_CONFIG_TICKET_H_ 3 | 4 | #include "src/config.h" 5 | #include "src/common.h" 6 | 7 | bud_error_t bud_context_set_ticket(bud_context_t* context, 8 | const char* ticket, 9 | size_t size, 10 | bud_encoding_t enc); 11 | void bud_context_rotate_cb(uv_timer_t* timer); 12 | 13 | #endif /* SRC_CONFIG_TICKET_H_ */ 14 | -------------------------------------------------------------------------------- /src/config/tracing.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CONFIG_TRACING_H_ 2 | #define SRC_CONFIG_TRACING_H_ 3 | 4 | #include "parson.h" 5 | 6 | #include "src/config.h" 7 | #include "src/common.h" 8 | 9 | bud_error_t bud_config_load_tracing(bud_config_trace_t* trace, 10 | JSON_Object* obj); 11 | bud_error_t bud_config_init_tracing(bud_config_trace_t* trace); 12 | void bud_config_trace_free(bud_config_trace_t* trace); 13 | 14 | #endif /* SRC_CONFIG_TRACING_H_ */ 15 | -------------------------------------------------------------------------------- /src/server.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_SERVER_H_ 2 | #define SRC_SERVER_H_ 3 | 4 | #include "uv.h" 5 | 6 | #include "src/config.h" 7 | #include "src/error.h" 8 | 9 | typedef struct bud_server_s bud_server_t; 10 | 11 | struct bud_server_s { 12 | bud_config_t* config; 13 | uv_tcp_t tcp; 14 | int pending_accept; 15 | 16 | bud_server_t* prev; 17 | }; 18 | 19 | bud_error_t bud_create_servers(bud_config_t* config); 20 | void bud_free_servers(bud_config_t* config); 21 | 22 | #endif /* SRC_SERVER_H_ */ 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "7" 5 | before_install: 6 | - mkdir -p gypkg_deps 7 | - gpg --no-default-keyring --keyring gypkg_deps/.gpg-scope-gypkg --keyserver hkp://keys.gnupg.net --recv-keys 6A4781FB 8 | - gpg --no-default-keyring --keyring gypkg_deps/.gpg-scope-nodejs --keyserver hkp://keys.gnupg.net --recv-keys 6A4781FB 9 | - gpg --no-default-keyring --keyring gypkg_deps/.gpg-scope-indutny --keyserver hkp://keys.gnupg.net --recv-keys 6A4781FB 10 | branches: 11 | only: 12 | - master 13 | -------------------------------------------------------------------------------- /test/keys/ec-csr.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIBNzCB3wIBADB9MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcT 3 | AlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMT 4 | BmFnZW50MjEgMB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwWTATBgcq 5 | hkjOPQIBBggqhkjOPQMBBwNCAAQnAcJzawJ/tu1tosUeTOW4uYXMi2nXlML+j5mc 6 | d0rohPaGF0V/tyFdn6zI9jNsklJ4VchaWEHEDu6IFrqN0tm3oAAwCQYHKoZIzj0E 7 | AQNIADBFAiEAtTMfxCDw5RFMDBXezXrWKPPHWL01xmREY4Q4ygUOoRkCIAi/haHS 8 | jQw4dkBfwF0qZ432XmzcHgxygohzPH6PQNLP 9 | -----END CERTIFICATE REQUEST----- 10 | -------------------------------------------------------------------------------- /test/keys/ca2-crl.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN X509 CRL----- 2 | MIIBXTCBxzANBgkqhkiG9w0BAQQFADB6MQswCQYDVQQGEwJVUzELMAkGA1UECBMC 3 | Q0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAOBgNVBAsTB05vZGUu 4 | anMxDDAKBgNVBAMTA2NhMjEgMB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5v 5 | cmcXDTE0MDQyOTE3MDMzMVoXDTE3MDEyMjE3MDMzMVowHDAaAgkAp39Z6HCdmVcX 6 | DTE0MDQyOTE3MDMzMVowDQYJKoZIhvcNAQEEBQADgYEAYmI2XohTB6JHTSdWhLsJ 7 | +tupqyvfUuXRmsPjTRzmIOFPBu+j0xVDpFkM7J2CE9HU8LJpTqw3AXMW5lQ4SKrB 8 | ytCbDK36xxDdR4Bdn8mnbftLj/9Zb1Nnv+QfDb5M7frvy3KNbplGj8exje0ackBS 9 | U3P9TvfHfE/I1GbxGT3mEA8= 10 | -----END X509 CRL----- 11 | -------------------------------------------------------------------------------- /src/config/context.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CONFIG_CONTEXT_H_ 2 | #define SRC_CONFIG_CONTEXT_H_ 3 | 4 | #include "src/config.h" 5 | #include "src/config/utils.h" 6 | #include "src/common.h" 7 | 8 | bud_error_t bud_context_load_cert(bud_context_t* context, 9 | const char* cert_file); 10 | bud_error_t bud_context_load_key(bud_context_t* context, 11 | const char* key_file, 12 | const char* key_pass); 13 | bud_error_t bud_context_load_keys(bud_context_t* context); 14 | 15 | #endif /* SRC_CONFIG_CONTEXT_H_ */ 16 | -------------------------------------------------------------------------------- /test/keys/agent2.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | default_bits = 1024 3 | days = 999 4 | distinguished_name = req_distinguished_name 5 | attributes = req_attributes 6 | prompt = no 7 | 8 | [ req_distinguished_name ] 9 | C = US 10 | ST = CA 11 | L = SF 12 | O = Joyent 13 | OU = Node.js 14 | CN = agent2 15 | emailAddress = ry@tinyclouds.org 16 | 17 | [ req_attributes ] 18 | challengePassword = A challenge password 19 | 20 | -------------------------------------------------------------------------------- /test/keys/agent3.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | default_bits = 1024 3 | days = 999 4 | distinguished_name = req_distinguished_name 5 | attributes = req_attributes 6 | prompt = no 7 | 8 | [ req_distinguished_name ] 9 | C = US 10 | ST = CA 11 | L = SF 12 | O = Joyent 13 | OU = Node.js 14 | CN = agent3 15 | emailAddress = ry@tinyclouds.org 16 | 17 | [ req_attributes ] 18 | challengePassword = A challenge password 19 | 20 | -------------------------------------------------------------------------------- /test/keys/ca1.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | default_bits = 1024 3 | days = 999 4 | distinguished_name = req_distinguished_name 5 | attributes = req_attributes 6 | prompt = no 7 | output_password = password 8 | 9 | [ req_distinguished_name ] 10 | C = US 11 | ST = CA 12 | L = SF 13 | O = Joyent 14 | OU = Node.js 15 | CN = ca1 16 | emailAddress = ry@tinyclouds.org 17 | 18 | [ req_attributes ] 19 | challengePassword = A challenge password 20 | 21 | -------------------------------------------------------------------------------- /test/keys/agent4.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | default_bits = 1024 3 | days = 999 4 | distinguished_name = req_distinguished_name 5 | attributes = req_attributes 6 | prompt = no 7 | 8 | [ req_distinguished_name ] 9 | C = US 10 | ST = CA 11 | L = SF 12 | O = Joyent 13 | OU = Node.js 14 | CN = agent4 15 | emailAddress = ry@tinyclouds.org 16 | 17 | [ req_attributes ] 18 | challengePassword = A challenge password 19 | 20 | [ ext_key_usage ] 21 | extendedKeyUsage = clientAuth 22 | -------------------------------------------------------------------------------- /src/avail.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_AVAIL_H_ 2 | #define SRC_AVAIL_H_ 3 | 4 | #include "uv.h" 5 | 6 | #include "src/client-common.h" 7 | #include "src/config.h" 8 | 9 | /* Forward declarations */ 10 | struct bud_client_s; 11 | 12 | bud_config_backend_t* bud_select_backend(struct bud_client_s* client); 13 | 14 | /* Client helpers */ 15 | bud_client_error_t bud_client_connect(struct bud_client_s* client); 16 | void bud_client_connect_cb(uv_connect_t* req, int status); 17 | void bud_client_connect_close_cb(uv_handle_t* handle); 18 | bud_client_error_t bud_client_retry(struct bud_client_s* client); 19 | void bud_client_retry_cb(uv_timer_t* timer); 20 | 21 | #endif /* SRC_AVAIL_H_ */ 22 | -------------------------------------------------------------------------------- /src/config/files.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CONFIG_FILES_H_ 2 | #define SRC_CONFIG_FILES_H_ 3 | 4 | #include "src/config.h" 5 | #include "src/common.h" 6 | 7 | bud_error_t bud_config_files_reduce_size(bud_hashmap_item_t* item, 8 | void* arg); 9 | bud_error_t bud_config_files_reduce_copy(bud_hashmap_item_t* item, 10 | void* arg); 11 | bud_error_t bud_config_files_reduce_reload(bud_hashmap_item_t* item, 12 | void* arg); 13 | bud_error_t bud_config_free_files(bud_hashmap_item_t* item, 14 | void* arg); 15 | 16 | #endif /* SRC_CONFIG_FILES_H_ */ 17 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "target_defaults": { 3 | "type": "loadable_module", 4 | "product_prefix": "", 5 | "product_extension": "bud", 6 | 7 | "include_dirs": [ 8 | "include/", 9 | "deps/openssl/openssl/include", 10 | ], 11 | "conditions": [ 12 | [ 'OS=="mac"', { 13 | 'defines': [ '_DARWIN_USE_64_BIT_INODE=1' ], 14 | 'libraries': [ '-undefined dynamic_lookup' ], 15 | 'xcode_settings': { 16 | 'DYLIB_INSTALL_NAME_BASE': '@rpath' 17 | }, 18 | }], 19 | [ 'OS=="freebsd" or OS=="openbsd" or OS=="solaris" or (OS=="linux" and target_arch!="ia32")', { 20 | 'cflags': [ '-fPIC' ], 21 | }] 22 | ] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/keys/ec.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | default_bits = 1024 3 | days = 999 4 | distinguished_name = req_distinguished_name 5 | attributes = req_attributes 6 | prompt = no 7 | x509_extensions = v3_ca 8 | 9 | [ req_distinguished_name ] 10 | C = US 11 | ST = CA 12 | L = SF 13 | O = Joyent 14 | OU = Node.js 15 | CN = agent2 16 | emailAddress = ry@tinyclouds.org 17 | 18 | [ req_attributes ] 19 | 20 | [ v3_ca ] 21 | authorityInfoAccess = @issuer_info 22 | 23 | [ issuer_info ] 24 | OCSP;URI.0 = http://ocsp.nodejs.org/ 25 | caIssuers;URI.0 = http://ca.nodejs.org/ca.cert 26 | -------------------------------------------------------------------------------- /src/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_LOGGER_H_ 2 | #define SRC_LOGGER_H_ 3 | 4 | #include "include/bud/logger.h" 5 | #include "src/error.h" 6 | #include "src/config.h" 7 | 8 | typedef struct bud_logger_s bud_logger_t; 9 | 10 | struct bud_logger_s { 11 | bud_log_level_t level; 12 | int stdio_enabled; 13 | int syslog_enabled; 14 | }; 15 | 16 | bud_logger_t* bud_logger_new(bud_config_t* config, bud_error_t* err); 17 | void bud_logger_free(bud_logger_t* logger); 18 | 19 | void bud_clog(bud_config_t* config, 20 | bud_log_level_t level, 21 | const char* fmt, 22 | ...); 23 | void bud_clogva(bud_config_t* config, 24 | bud_log_level_t level, 25 | const char* fmt, 26 | va_list ap); 27 | 28 | #endif /* SRC_LOGGER_H_ */ 29 | -------------------------------------------------------------------------------- /test/keys/agent2-csr.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIB4jCCAUsCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQH 3 | EwJTRjEPMA0GA1UEChMGSm95ZW50MRAwDgYDVQQLEwdOb2RlLmpzMQ8wDQYDVQQD 4 | EwZhZ2VudDIxIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMIGfMA0G 5 | CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYZuYl1V0RNBeEr3XmmzbxRzM58WjWTo/Y 6 | 314aGNTY+UlAY7N0T2/+8EDT1ApWz0pQ18qh1ALHY01H1Sg0sy2xx4ZFH2XkNGmy 7 | 0iYxFCbLLBsQYk1ySnlzfRJLmfNI+pIQ1i6X7ksn7k17P9z4TGa3fQ1SWEikm7+i 8 | FKsi7Ys73QIDAQABoCUwIwYJKoZIhvcNAQkHMRYTFEEgY2hhbGxlbmdlIHBhc3N3 9 | b3JkMA0GCSqGSIb3DQEBBQUAA4GBACnTkYi5jUwhivf81koxxTpmxfIGZCU6wsHT 10 | dIEGDCbDCKc6dGTj65SMcT+Cuqf0lx0b7kHxlzaSO999US6s7ejbLQBcx/3Q6GQS 11 | OjVqsMaWdkESBebxdXSbZuyDibERT6U8xYg3YghNH2IF9ED26+eJopRvo3Urrxyc 12 | ShVxF0L0 13 | -----END CERTIFICATE REQUEST----- 14 | -------------------------------------------------------------------------------- /test/keys/agent3-csr.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIB4jCCAUsCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQH 3 | EwJTRjEPMA0GA1UEChMGSm95ZW50MRAwDgYDVQQLEwdOb2RlLmpzMQ8wDQYDVQQD 4 | EwZhZ2VudDMxIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMIGfMA0G 5 | CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBampwW63JEkzx26KOYCvOQUS8dbSprTCW 6 | a4kouXFTKLhvSXwfIt/lhUGlTnOecYsuGT5Pdb4FvlCEJrHnzexSNKmnnDOYzNBs 7 | FLB8WX0FLxojsSXoTtN4ZsZ7jIdV+ICpevXBI4h2k5KkL4WjGaj+x6iiSnOPhTWa 8 | IhplkWzJiwIDAQABoCUwIwYJKoZIhvcNAQkHMRYTFEEgY2hhbGxlbmdlIHBhc3N3 9 | b3JkMA0GCSqGSIb3DQEBBQUAA4GBADVAVVdgJeGtJfdsTjnPT+pFz2whcYBqY9QT 10 | zpUw7jCyXB1vkdribyXYWnLoUWzVwK4PVDAco+KTICoj5ChC/GPZLaHNh3pPTC3I 11 | pI5Cyit2BTqWeOT8T5ngqaofjsoakdm1BSWnfTGkDHGuQ6jN4WPlFCYw1OLdbv2J 12 | PVl+rXBJ 13 | -----END CERTIFICATE REQUEST----- 14 | -------------------------------------------------------------------------------- /test/keys/agent4-csr.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIIB4jCCAUsCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQH 3 | EwJTRjEPMA0GA1UEChMGSm95ZW50MRAwDgYDVQQLEwdOb2RlLmpzMQ8wDQYDVQQD 4 | EwZhZ2VudDQxIDAeBgkqhkiG9w0BCQEWEXJ5QHRpbnljbG91ZHMub3JnMIGfMA0G 5 | CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDprUuDIAw5TQ8h8hdU6M7oQSM4s62/RQjN 6 | EMR4bhdjkt5BEgt6ZQYTICNw/YbKMzNK8oBa4tMmnor5PIY/DnEvnFwnrfzmlJkc 7 | f/q/x/6TGYofhwSYyuI4fvfgultooZPABHOvcIeQJ2lPGbqDZRwjWnXMhWYmulxh 8 | QdW/KAiDIQIDAQABoCUwIwYJKoZIhvcNAQkHMRYTFEEgY2hhbGxlbmdlIHBhc3N3 9 | b3JkMA0GCSqGSIb3DQEBBQUAA4GBALPvfoctuPmxNHqB58lBkgVMUDJItu8B87MI 10 | 2UQFi3/aj4xFy8GHxNisyRFqeB3DrD/W5Fv5oS7XBX6ZjyRa/E4DrzBIvukXZVSP 11 | X80ztIG+C4Y0FYt5zssNaiOfosFWebeLiaGTUzsovGG5Y3132z/nEPOXrwuiBLEZ 12 | zerKS+D3 13 | -----END CERTIFICATE REQUEST----- 14 | -------------------------------------------------------------------------------- /test/keys/agent1-csr.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICAjCCAWsCAQAwgZwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTELMAkGA1UE 3 | BwwCU0YxDzANBgNVBAoMBkpveWVudDEQMA4GA1UECwwHTm9kZS5qczEPMA0GA1UE 4 | AwwGYWdlbnQxMR0wGwYDVQQxDBRkaXN0aW5ndWlzaGVkLWFnZW50MTEgMB4GCSqG 5 | SIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0A 6 | MIGJAoGBAOKPRarrSjLGy5kNcXUnmoy+pz9A2pwxq5TyK5TvqQ9BUxwRoOnCYnah 7 | brDdr0w4xuLpBVg53mARgyOAMrt2HNRDRFhC6+lk5KS40+4LajjnS1z7mcZ9an5u 8 | JZmkZPEVinweI0e/45LH/gFc8FRZ7Idi96IL/w8KUQ6jLfE0NtabAgMBAAGgJTAj 9 | BgkqhkiG9w0BCQcxFgwUQSBjaGFsbGVuZ2UgcGFzc3dvcmQwDQYJKoZIhvcNAQEL 10 | BQADgYEABD4lMtBeiCsHpp7tH4gICAvXX+BtDugHoEZyjMLpJoE9+dKHLHaMXGYS 11 | uDsAQnDTiF6zLQtxiejtsqWTAxwvoDsaX4FFOEzurUzPqwEK6AQcemsTIb5HSyzA 12 | 8TL2+MQRw9uxvpXUrR4qOKAbFg+tF5pLtQu0fJ88MXNrKJcQhyU= 13 | -----END CERTIFICATE REQUEST----- 14 | -------------------------------------------------------------------------------- /test/keys/agent1.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | default_bits = 1024 3 | days = 999 4 | distinguished_name = req_distinguished_name 5 | attributes = req_attributes 6 | prompt = no 7 | x509_extensions = v3_ca 8 | 9 | [ req_distinguished_name ] 10 | C = US 11 | ST = CA 12 | L = SF 13 | O = Joyent 14 | OU = Node.js 15 | CN = agent1 16 | distinguishedName = distinguished-agent1 17 | emailAddress = ry@tinyclouds.org 18 | 19 | [ req_attributes ] 20 | challengePassword = A challenge password 21 | 22 | [ v3_ca ] 23 | authorityInfoAccess = @issuer_info 24 | 25 | [ issuer_info ] 26 | OCSP;URI.0 = http://ocsp.nodejs.org/ 27 | caIssuers;URI.0 = http://ca.nodejs.org/ca.cert 28 | -------------------------------------------------------------------------------- /include/bud/logger.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_BUD_LOGGER_H_ 2 | #define INCLUDE_BUD_LOGGER_H_ 3 | 4 | #include 5 | 6 | #include "bud/common.h" 7 | 8 | /* Forward declarations */ 9 | struct bud_logger_s; 10 | 11 | typedef enum bud_log_level_e bud_log_level_t; 12 | 13 | enum bud_log_level_e { 14 | kBudLogDebug = 0, 15 | kBudLogNotice = 1, 16 | kBudLogInfo = 2, 17 | kBudLogWarning = 3, 18 | kBudLogFatal = 4 19 | }; 20 | 21 | BUD_EXPORT void bud_log(struct bud_logger_s* logger, 22 | bud_log_level_t level, 23 | const char* fmt, 24 | ...); 25 | BUD_EXPORT void bud_logva(struct bud_logger_s* logger, 26 | bud_log_level_t level, 27 | const char* fmt, 28 | va_list ap); 29 | 30 | #endif /* INCLUDE_BUD_LOGGER_H_ */ 31 | -------------------------------------------------------------------------------- /test/keys/ca2.cnf: -------------------------------------------------------------------------------- 1 | [ ca ] 2 | default_ca = CA_default 3 | 4 | [ CA_default ] 5 | serial = ca2-serial 6 | crl = ca2-crl.pem 7 | database = ca2-database.txt 8 | name_opt = CA_default 9 | cert_opt = CA_default 10 | default_crl_days = 999 11 | default_md = md5 12 | 13 | 14 | [ req ] 15 | default_bits = 1024 16 | days = 999 17 | distinguished_name = req_distinguished_name 18 | attributes = req_attributes 19 | prompt = no 20 | output_password = password 21 | 22 | [ req_distinguished_name ] 23 | C = US 24 | ST = CA 25 | L = SF 26 | O = Joyent 27 | OU = Node.js 28 | CN = ca2 29 | emailAddress = ry@tinyclouds.org 30 | 31 | [ req_attributes ] 32 | challengePassword = A challenge password 33 | 34 | -------------------------------------------------------------------------------- /include/bud/ipc.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_BUD_IPC_H_ 2 | #define INCLUDE_BUD_IPC_H_ 3 | 4 | typedef enum bud_ipc_type_e bud_ipc_type_t; 5 | typedef struct bud_ipc_msg_header_s bud_ipc_msg_header_t; 6 | typedef struct bud_ipc_msg_s bud_ipc_msg_t; 7 | 8 | enum bud_ipc_type_e { 9 | /* Empty message just to balance the handle (internal) */ 10 | kBudIPCBalance = 0x0, 11 | 12 | /* Contents of the files used in the config, sent ahead of time (internal) */ 13 | kBudIPCConfigFileCache = 0x1, 14 | 15 | /* EOF (internal) */ 16 | kBudIPCEOF = 0x2, 17 | 18 | /* Set TLS Ticket */ 19 | /* Message data format: [4-byte BE context index] [48-byte key] */ 20 | kBudIPCSetTicket = 0x3 21 | }; 22 | 23 | struct bud_ipc_msg_header_s { 24 | uint8_t type; 25 | uint32_t size; 26 | }; 27 | 28 | struct bud_ipc_msg_s { 29 | uint8_t type; 30 | uint32_t size; 31 | char data[1]; 32 | }; 33 | 34 | #endif /* INCLUDE_BUD_IPC_H_ */ 35 | -------------------------------------------------------------------------------- /test/keys/agent2-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQDYZuYl1V0RNBeEr3XmmzbxRzM58WjWTo/Y314aGNTY+UlAY7N0 3 | T2/+8EDT1ApWz0pQ18qh1ALHY01H1Sg0sy2xx4ZFH2XkNGmy0iYxFCbLLBsQYk1y 4 | SnlzfRJLmfNI+pIQ1i6X7ksn7k17P9z4TGa3fQ1SWEikm7+iFKsi7Ys73QIDAQAB 5 | AoGAWcH8be7cxljz9E4OuhLhLBybK8ps8I7vbUXgszRffIuRjYqNEY4tZvHiAbqu 6 | eylJqtSGsK7OZENU95l7LrivF42Fl7osTAH7EN8UqRnl/xFE8IhLqEYBrI1uLE3a 7 | gjS4ywvH6wDLZ5IqwA7aHCipnao9X27C20bzirJiyhIblGUCQQD6rd4u7EfoM+XK 8 | QeLmC6rpRsPGm5Gs6u/hW5rKyuavjaDV44/Mz9MQBXwLGaaKRXYIt94GciwxBg/i 9 | 51Ztq5fHAkEA3P7G1QNQUD/oTY7/pBuzaMqx8wXeOf5oy5OkAqe9AG3251FcyewJ 10 | 3Fj79fR/jJB+b2E4WMkH8ZZcnjktFoC3OwJARBe6hLB2D+MZBknDYo3EzT0FrQtR 11 | FNF0e5iwgWmAJCuqhq8bvpytTmrO+Qzsqh6YI2Iek02sJ1zoL9B7L6suvQJBAKlV 12 | y7Mi8+JZ5b2wKfBsjV9pmUr3Hx3e9BuxiIXEU81FdWAv9uJ+K9wcjwwaibWCbm8X 13 | 1p4phB24skVG2GaawEsCQCn/r0e4M/dH0fA+pBhfCdPVg36BJ0mzK1UX8Rzhmj7L 14 | 8E1cC+Qo8NwOV95k/p/HplO8GosIt7aqm93OsTBAcpc= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /test/keys/agent3-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDBampwW63JEkzx26KOYCvOQUS8dbSprTCWa4kouXFTKLhvSXwf 3 | It/lhUGlTnOecYsuGT5Pdb4FvlCEJrHnzexSNKmnnDOYzNBsFLB8WX0FLxojsSXo 4 | TtN4ZsZ7jIdV+ICpevXBI4h2k5KkL4WjGaj+x6iiSnOPhTWaIhplkWzJiwIDAQAB 5 | AoGAH9B9SZYK8/QKrA3Qo1K2Yerif41s9iwCvftaSFSYi3E7RTL1M7ns0jglICfT 6 | ZNCQcDi+wxs+8nrni5VB1jtND783qtMsb+6d7FQ4zan6jOHNvfvRIsktA9QGRiP9 7 | zeFPtheI2FOYzCNCcQ8HyNApNvdHzJ/jSpQ7PpJSzLhgPYECQQDtI8l1aT3KXOH5 8 | xwRP10kbBP3LbmvV++4SL8xHCdaghIE4gCuru0VZ43U6DGnQKL1ADr8JQGz2fAWn 9 | +Hx/ZcPbAkEA0Mxluxanf+PX8lL1dFEgABJ+S/Kah3mqaaBq0wcYNGuwx3i4y0WN 10 | RR3vYAh5N5g24QRHkoOIlsF+HfV6PF7YEQJBAIAr3ZFmRo1/0mvMk5oLxGmkMwCn 11 | OAW6WxzdVM34lVbBB47gR7pUR9GjzRKI7xAhVNiwn7SCa+J1eiElfzTkbg0CQH4V 12 | n6ztTjJtTN7Y973Iq1E/2aJVGrmKX7yvETLjittE2JhrrvOYyWkeXYmZNFG9Cg6x 13 | PeMcqKOYrma68Jh6EXECQQDGscjpSa5v4RuZdELLVmrDbGGCPHxwb6MBCbd0jeV2 14 | jNyFhKhUdUd+sDzKJoM0UjojZ2riRYL13ptyYtQVxrHz 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /test/keys/agent4-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDprUuDIAw5TQ8h8hdU6M7oQSM4s62/RQjNEMR4bhdjkt5BEgt6 3 | ZQYTICNw/YbKMzNK8oBa4tMmnor5PIY/DnEvnFwnrfzmlJkcf/q/x/6TGYofhwSY 4 | yuI4fvfgultooZPABHOvcIeQJ2lPGbqDZRwjWnXMhWYmulxhQdW/KAiDIQIDAQAB 5 | AoGBAMqdGSGMEy1IFLvxRqu35VfayuhqW1fCSvxL8p8djigne5DeTZeFqvvAXRqm 6 | qU3/kRUGKackj8MnqwaqpRhdtGLUwYCyDfHdaMNpakdSYPDR3OcsqQnnDELLA8NU 7 | 8UtB9NFzwXr1fQMMwtS7h08veAm9so4+FiZ+gw7+10VbJWwlAkEA9UQDqEOMv7Hp 8 | IEq6fjeljdkvPNxNHfJk9WndWJ0gXwatXjHttOarPQP2N2bantfA1Om35Ec2ZdMB 9 | 0ReXdTd8/wJBAPPncGFvZW464OKlhBturWrCq53UM549lr/THofW+mS1RQgMaeS2 10 | +ua9AklFj0pU+d2mu5fOn93roDk+Z3qwX98CQDHtZ4UwQw2ezOMq8y3QbB4lEQ2l 11 | lvk6p7yhkVKBod5WQKKFLWt+7WrdTJjYkjZ1myZ8cVfB+1vdCWikEcvlwMcCQQCi 12 | L0TIVaWx7c76hytYfnksY7Rwp9mHINcsSTulAmvQKcV0e6SypIYkhoyNooaM3AKm 13 | xCc74/bkP7l0Ny3iJIQrAkBaQKClfFYZSbI1msXiNGZgENA7/LfLXH+lYTBYhPNO 14 | dBAeQTJNlHRvbr2wl+eBpAc86LLkhqcYaNGifIGjDRCc 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /src/master.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_MASTER_H_ 2 | #define SRC_MASTER_H_ 3 | 4 | #include "src/config.h" 5 | #include "src/error.h" 6 | #include "src/ipc.h" 7 | 8 | /* Forward declaration */ 9 | struct bud_server_s; 10 | 11 | typedef struct bud_worker_s bud_worker_t; 12 | typedef bud_error_t (*bud_worker_kill_cb)(bud_worker_t* worker); 13 | 14 | enum { 15 | kBudWorkerStateNone = 0x0, 16 | kBudWorkerStateActive = 0x1, 17 | kBudWorkerStateStale = 0x2, 18 | kBudWorkerStateDead = 0x4 19 | }; 20 | 21 | struct bud_worker_s { 22 | unsigned int state; 23 | int index; 24 | 25 | bud_config_t* config; 26 | uv_process_t proc; 27 | bud_ipc_t ipc; 28 | uv_timer_t restart_timer; 29 | int close_waiting; 30 | 31 | bud_worker_kill_cb kill_cb; 32 | }; 33 | 34 | bud_error_t bud_master(bud_config_t* config); 35 | bud_error_t bud_master_finalize(bud_config_t* config); 36 | void bud_master_balance(struct bud_server_s* server); 37 | 38 | #endif /* SRC_MASTER_H_ */ 39 | -------------------------------------------------------------------------------- /test/keys/agent1-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXgIBAAKBgQDij0Wq60oyxsuZDXF1J5qMvqc/QNqcMauU8iuU76kPQVMcEaDp 3 | wmJ2oW6w3a9MOMbi6QVYOd5gEYMjgDK7dhzUQ0RYQuvpZOSkuNPuC2o450tc+5nG 4 | fWp+biWZpGTxFYp8HiNHv+OSx/4BXPBUWeyHYveiC/8PClEOoy3xNDbWmwIDAQAB 5 | AoGATQ5RsapJGDAI+0tZNXF83TXTCG50a/9wzj33mLZSl7+uFrPgOOFGP/b0AO/F 6 | OXVuH6sFiFZWTfJm2eI2Xw/zCZLdAzVY77rLdbDNq8ZbLEqPk07u2idHqX5uW05f 7 | OCQXzg+ic5ZuDoLjRdUlTyI3ze24mZcaTVVvBpYonF+VjXECQQD3lDexkAXrbhG2 8 | xDhki3RMInE1m938y5iQWCzqrLhg8XZWP9b+HEJJC++fwgfPvaPh85VP4WuFr4qt 9 | wICkftxfAkEA6kQHpCtuvosI7sYhVakaT1xOZobjcpCxkBLpa8Dr1T0YWNKoinK0 10 | kKhrXOaVZ+a3J4q1G6BDh5zl9TT/FnAvRQJBANqjVhvE6LVk2tfgqTyBuw6RTBDf 11 | uUbKNc0jUWddaOaT1sGMVzzJj/AaTl9zZpqbCuM7zZxhoibJQ1r2KaUbM1kCQQCw 12 | Y9zlDhbqvcFSGDkNSLCYxMX7iOFFaBFqi/LZwX9RJhlo9+UTqd6vdUCxyKUCgMOz 13 | sEtBq+BtAgRcJzVS7dGJAkEA1PScjVqHsihB1BYk8TLrw5uhBELZZllcJswsRvAo 14 | KysciR5GIY36dmOMZNxDAzBQzDfCSRPwIRN8srmifk5Lsw== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /test/keys/ca1-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICazCCAdQCCQCTC4TvIHcTnDANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO 4 | BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA 5 | dGlueWNsb3Vkcy5vcmcwHhcNMTQwNDI5MTcwMzMwWhcNNDEwOTEzMTcwMzMwWjB6 6 | MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK 7 | EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqG 8 | SIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0A 9 | MIGJAoGBALlhdGO/OtvupciHXf/wmRCJEgxw1NTl1O51g2k7i75OoLmtzsixRAEQ 10 | CwQP+t1ngT6c09NVWPcR1dMmlfPsen0TIU5EXfoF1u1+5FYlZjDVQ3JNF4FQvSJw 11 | yjk1acDnr2CXzUIus+5RcnX9oUmpTWU8abROaNnvEg5vbfmqjeirAgMBAAEwDQYJ 12 | KoZIhvcNAQEFBQADgYEABolinn++jW8jKn7X4iggXa859+Wi24t43uhpYZiPnWgc 13 | LOGfsVty5Xa/1ktVbgmTxo87W+mh84vHcuenWmX2lMrv4m2ty/bqTolDVEdq5sw6 14 | 83tEw2XvVvoyT21wq6hXXF8nuJ8vPUIUml5ucRk1JR3SbpqcFyoGmk5pPl/VZe0= 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /test/keys/ca2-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICazCCAdQCCQDWCV+9xVEZVTANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO 4 | BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMjEgMB4GCSqGSIb3DQEJARYRcnlA 5 | dGlueWNsb3Vkcy5vcmcwHhcNMTQwNDI5MTcwMzMxWhcNNDEwOTEzMTcwMzMxWjB6 6 | MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK 7 | EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMjEgMB4GCSqG 8 | SIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0A 9 | MIGJAoGBAON4AP0nH/Ydbaj+R5D8jkDd0WiMH3tP5/HOeLHWPxjwvHp8O/ED+Oqd 10 | BvJcKUL/j+TnsvFJicUNC/CmTeCvHLFGwN10BolvdL1YWEj+u2jzlbTDyd3s0QGD 11 | My0qy90LsB2yvQJvSdRiL1rXVr4X6wzYcFFx6VnCSMKUp79rRseRAgMBAAEwDQYJ 12 | KoZIhvcNAQEFBQADgYEAkQwEzcdFcFUTon8PxtDyksU+DdyukntIOEExJ9nl5BoD 13 | EaDfDL9SK+SYfqeUVynkzMwrEoVfycde2g649H+7uK53Ff/iZsbVhVJ8nFfSdu1j 14 | whIbPk0RGXtFD7kxexEzRQts/uGfnMlj45a/RdXTOXH0RClrEx4fDfwreqL2WKc= 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /test/keys/agent3-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICbjCCAdcCCQCnf1nocJ2ZVjANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO 4 | BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMjEgMB4GCSqGSIb3DQEJARYRcnlA 5 | dGlueWNsb3Vkcy5vcmcwHhcNMTQwNDI5MTcwMzMxWhcNNDEwOTEzMTcwMzMxWjB9 6 | MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK 7 | EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MzEgMB4G 8 | CSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQAD 9 | gY0AMIGJAoGBAMFqanBbrckSTPHboo5gK85BRLx1tKmtMJZriSi5cVMouG9JfB8i 10 | 3+WFQaVOc55xiy4ZPk91vgW+UIQmsefN7FI0qaecM5jM0GwUsHxZfQUvGiOxJehO 11 | 03hmxnuMh1X4gKl69cEjiHaTkqQvhaMZqP7HqKJKc4+FNZoiGmWRbMmLAgMBAAEw 12 | DQYJKoZIhvcNAQEFBQADgYEATzGIilY/1fNrGWJDnL4OHILpIJw8VAT8ssvGfeOR 13 | 7H6FsbQv2ftkYYiVsgCCztngwDXqKwMW7HF5Z691wMWnldjP0b5dCMGyDdGs/DQL 14 | EM5ykevYPIv/EaTGiQRkM0scyroztk9AMNXq30U3kZjlPqATRXXxnKuZ4KOVUthY 15 | 2xk= 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /test/keys/agent2-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICcTCCAdoCCQCA0JD9sDZJ+TANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJV 3 | UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO 4 | BgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEgMB4GCSqGSIb3DQEJARYR 5 | cnlAdGlueWNsb3Vkcy5vcmcwHhcNMTQwNDI5MTcwMzMxWhcNNDEwOTEzMTcwMzMx 6 | WjB9MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYD 7 | VQQKEwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MjEg 8 | MB4GCSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEB 9 | BQADgY0AMIGJAoGBANhm5iXVXRE0F4SvdeabNvFHMznxaNZOj9jfXhoY1Nj5SUBj 10 | s3RPb/7wQNPUClbPSlDXyqHUAsdjTUfVKDSzLbHHhkUfZeQ0abLSJjEUJsssGxBi 11 | TXJKeXN9EkuZ80j6khDWLpfuSyfuTXs/3PhMZrd9DVJYSKSbv6IUqyLtizvdAgMB 12 | AAEwDQYJKoZIhvcNAQEFBQADgYEAiIsKenDsjct/6+TCHpsiJO0mr32gF2WF6bvP 13 | GAIlYYXRo+y36HKdfaq3I9kxL876Ljpd0ICMOYe9Mv2F31M7AjabhUPKAOr82K+N 14 | OrdBYJpVrZuHEUsciN2syxYP6Lgdyhwis97qU22ZF+6tUlfMSFmGxjvAyn96STqk 15 | hIu+rg0= 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /test/keys/agent4-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICjDCCAfWgAwIBAgIJAKd/WehwnZlXMA0GCSqGSIb3DQEBBQUAMHoxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzANBgNVBAoTBkpveWVu 4 | dDEQMA4GA1UECxMHTm9kZS5qczEMMAoGA1UEAxMDY2EyMSAwHgYJKoZIhvcNAQkB 5 | FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xNDA0MjkxNzAzMzFaFw00MTA5MTMxNzAz 6 | MzFaMH0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzAN 7 | BgNVBAoTBkpveWVudDEQMA4GA1UECxMHTm9kZS5qczEPMA0GA1UEAxMGYWdlbnQ0 8 | MSAwHgYJKoZIhvcNAQkBFhFyeUB0aW55Y2xvdWRzLm9yZzCBnzANBgkqhkiG9w0B 9 | AQEFAAOBjQAwgYkCgYEA6a1LgyAMOU0PIfIXVOjO6EEjOLOtv0UIzRDEeG4XY5Le 10 | QRILemUGEyAjcP2GyjMzSvKAWuLTJp6K+TyGPw5xL5xcJ6385pSZHH/6v8f+kxmK 11 | H4cEmMriOH734LpbaKGTwARzr3CHkCdpTxm6g2UcI1p1zIVmJrpcYUHVvygIgyEC 12 | AwEAAaMXMBUwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEA 13 | i0OXWLIodW1CF0pzIhHeYJEWWbTpSTBSRx3YsoViD8LKuvQurCi3pQs80Owg2T3b 14 | Iwg7V3wAbZD+iGePrB5PSMD6JfoLl1AWdP8a1ZnwDrkikP82Z3yj1aEBIVFZ68Yx 15 | 9tAVxa2aVgYBioTgXIHBOxCgVC494As8A9eiehOjoz4= 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /test/keys/ca1-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,4ED212E948F2BC86 4 | 5 | KrCXcbeWyesZ6av+fC5FawiNDetFtCj0jDMBbv9dMX5Q7B9zcmrk5sk3d/teTVjC 6 | UexyZDBrbEtpe+VRwLN3SWFyhAtCW4wBb/3q6dIEipyzbrgOJYbp4Rl7YHvAnDaF 7 | /7hC3kzJI//EcAGf2Z+nGY8XYV8DzYc96ApwrdBzpNWK8aNdVJoZLcBVSvufNuuS 8 | wMkVDtOl7Gv85gWoUQoGICo8GcofhBMT5oN/Gspvqapb4SRQ36Y66UDDQMK8yjX9 9 | VkgZskgjHPFz4k8x0/Z1cbFl0u4FzaGQRWSSuNzFEW7t7os3KrwEhkUBv20+X2eB 10 | kd8NCLmx3AJj5D2ejiLA41nMIM0QCceOpnntp6bd0jXM5o2aCvgE+58jjNc2DrOr 11 | NoDSLG9e9n7DRSqilh6wB/7NE0zaGY3+72dfbHtQKy5NXR5fR32+uHuLuzBbqrYL 12 | NOuaYDRSl9w1wtzFNf4FbbhO5Y7ePI+FZn03+/GQ6ix8joC0iccau1iFj97KQadG 13 | +kUXdnPIFbHQQDR/Nt/QgpRa7bv2mYLsQRcev5A4vasCoVw4LojKNGahKa5Ns/zG 14 | 5yGOCmzUBLJ3U4xgmm8NIt7GQSGU5WU5l0aIeb55JCeVM9SPP9vPCD0rK+uq7W+D 15 | ocLtY0gBgqQ1i2/ukXjFAoZgqaY1wK0ViNK0m1Dzqgci4gsM4q4lseBb/VcNwl+M 16 | p6uIhGDNHGeaX+ZobJnuQk2v6eqHqmWdO7WoBMlzYY/Bbm1SFa423B6sJggZi5qS 17 | Bk47Je3xar16hNVdErvI+CpgU9y6FbEREeFuuzeRyI3yb7ck/Wv4Vg== 18 | -----END RSA PRIVATE KEY----- 19 | -------------------------------------------------------------------------------- /test/keys/ca2-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,1CF22DD361875801 4 | 5 | 243OBsXTAXMMnAY5gNJ6sKEaB/8LKaS4H2YEelCFWvHFlTNE3SiSl3yX3e/xzSw9 6 | WGRQIsnebZQ7wrfEUW5j/Mu3bMcpztEqvgcP/RqFrGhV49w0UYSQkfgxELMluNGA 7 | vaAa0VLenLNdeVmYUH3wqCsW9K8O+A3NAMWvZR9sNoTyWo67TeYBfCUff8FpHljA 8 | ma8MRKw5t7dGBMo1X6AdmxejwEeD7IITKIR6dRtfDdcbbm9iMiZ1P9/BgsgACjB4 9 | x5JdNOW/DYN6K/ggdSq/7A4WgoAz+yx8gdZ29rTLaxZ1CMJWDNHxvqZp1Mbvp3GB 10 | WP6flTS+Qm91YkbIrFjmVCHu7pXpO7x/JWBwEpbT12d688ajDpgshsPmUAkDoAew 11 | yDUNzocQijiHBWZvbb9b8QqajQixKz1MnZfIYbARJcJTpDVddsXMAgPC8x25PD3R 12 | WlPklavNIcjaDboGh7x0+aJshOAEgJSM5Wlcy82iCduu3btCpMO55PUOzktPbR6G 13 | PtrUz3u5XwneMqFDQcGGtnQznXIUSHgqOaF03TzEMaPSdWkqP7Gc6dJhGWQgvzlL 14 | MS6HBAx27cA3Sule6T1I82WUsae1EdEnqtITwEt2dGSVdUK/55WqJV6WrojiJUf4 15 | BAZcYOM+6ru+YHTMXdJor47v3pI7Mi4m/pSMgsGcic0QCPKQl85Ezb2gD3umfVFD 16 | YNkLNr5UsYbQ4V7omlTWYFt8E9161OvwrxKpVO6C18zSVoa44R/r5QkNbJLP1RR3 17 | bFsgvqHiSsnUTPmflEVkYjs3CWC27nT1eyWE8M6KX50i35+vZPIhZw== 18 | -----END RSA PRIVATE KEY----- 19 | -------------------------------------------------------------------------------- /test/keys/ec-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICqDCCAZCgAwIBAgIJAP+ZjEzPwsg5MA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV 3 | BAMWB21lZ2EuY2EwHhcNMTUwNTMwMTEwOTQ0WhcNNDIxMDE0MTEwOTQ0WjB9MQsw 4 | CQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMQ8wDQYDVQQKDAZK 5 | b3llbnQxEDAOBgNVBAsMB05vZGUuanMxDzANBgNVBAMMBmFnZW50MjEgMB4GCSqG 6 | SIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwWTATBgcqhkjOPQIBBggqhkjOPQMB 7 | BwNCAAS4dJIVSATuALDeSnF4knS/8+jlz/ctnywxmEhcDgHQXKRfyCVEcDgyETKI 8 | 5hjwNNnqcbGdEE/Wf3soFnbfgvWKo2EwXzBdBggrBgEFBQcBAQRRME8wIwYIKwYB 9 | BQUHMAGGF2h0dHA6Ly9vY3NwLm5vZGVqcy5vcmcvMCgGCCsGAQUFBzAChhxodHRw 10 | Oi8vY2Eubm9kZWpzLm9yZy9jYS5jZXJ0MA0GCSqGSIb3DQEBCwUAA4IBAQBhQBjF 11 | VwvBmq064j+QebuaasY5DUMcp1UnUaU3zIp+wUyipkZZy5mJCZwwhhyArYdSqUh4 12 | tvpTI0A6ohiySDbO/q1HNy0JjqsSCCIOAec3QtJpErU3LGcZoqygCPCltOFXkPK6 13 | 74fVDLg/eudMSfTfmEP35YsS8pajY+JFSJ+hIhoOM7Aa4DuquNGinMTV+hucz4PE 14 | xvAISMgVsWHp0sQehtRlC4Ia6Va7H+uhyRokNsVeMkDhy+d1PZFgkFJsPROrHMVy 15 | K6mIWf+wvizlze+pDIfqIoxo5chEX03d9yUlf/PNR45T4d3BQeCuf1yW0bxDjC/l 16 | qkGwqDzbz5sgEMqa 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /src/client-common.c: -------------------------------------------------------------------------------- 1 | #include "uv.h" 2 | 3 | #include "src/client.h" 4 | #include "src/client-common.h" 5 | #include "src/error.h" 6 | 7 | 8 | const char* bud_side_str(bud_client_side_type_t side) { 9 | if (side == kBudFrontend) 10 | return "frontend"; 11 | else 12 | return "backend"; 13 | } 14 | 15 | 16 | bud_client_error_t bud_client_error(bud_error_t err, bud_client_side_t* side) { 17 | bud_client_error_t cerr; 18 | 19 | cerr.err = err; 20 | cerr.side = side; 21 | 22 | return cerr; 23 | } 24 | 25 | 26 | bud_client_error_t bud_client_ok(bud_client_side_t* side) { 27 | return bud_client_error(bud_ok(), side); 28 | } 29 | 30 | 31 | bud_client_error_t bud_client_read_start(bud_client_t* client, 32 | bud_client_side_t* side) { 33 | int r; 34 | 35 | r = uv_read_start((uv_stream_t*) &side->tcp, 36 | bud_client_alloc_cb, 37 | bud_client_read_cb); 38 | if (r == 0) 39 | return bud_client_ok(side); 40 | 41 | return bud_client_error(bud_error_num(kBudErrClientReadStart, r), side); 42 | } 43 | -------------------------------------------------------------------------------- /test/keys/issuer-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC0zCCAb2gAwIBAgIBKjALBgkqhkiG9w0BAQswEjEQMA4GA1UEAxYHbWVnYS5j 3 | YTAeFw03MDAxMDEwMDAwMDBaFw0yNTA1MTkxNDA0MTdaMBIxEDAOBgNVBAMWB21l 4 | Z2EuY2EwggEgMAsGCSqGSIb3DQEBAQOCAQ8AMIIBCgKCAQEA6BwXNSYbr2/tlzqf 5 | kEfuoBrMtIauZ0DDjv1EuSQV7nisMA3PUXzHWHlvhO2fy0Ucsvu+a5MDSEdJFzmV 6 | KxFHQKOBOfSbjEJGTegknTWHGAcha7Yih1rF6/gd2AnjxM5m2loQz7Vdp5XbuCga 7 | ANnxwLqjalQiBOfbugj99yMytRBkCA8R2/K84pj4WSL1grQJ/lQTiFFy/RrUeznp 8 | vRPD88jwroK35AijIET6Rc6RGRwLPZViKiycxDBIwT0hE1PbJIU/u2vjVy/Jnuv7 9 | y7gAWaA76rCjO3ueBruc6ATHKxVJmY48+IACGgW2/Wo4PR75eFAEY3IBhIKwa6cK 10 | 4u3AeQIDAQABozowODA2BggrBgEFBQcBAQQqMCgwJgYIKwYBBQUHMAGGGmh0dHA6 11 | Ly8xMjcuMC4wLjE6ODAwMC9vY3NwMAsGCSqGSIb3DQEBCwOCAQEAA/H7REwuCCW3 12 | tXMfxt1h/IxFA7IfaqbGAyVkcmaybIcU5ivT0srwegx+zWQxWf5gldfpbMCLlhAc 13 | UQfht2feT3FkwyKUDDaCGX0fIa2P79fLB7AvBTJlNorMJmJuJgOPlkirefFpC8+a 14 | 2oQiw0YC+fGvb+FqEiFnwfYbrd20frXwDmNl8GOI63oOXUdegWnmmmuUllVrvr4A 15 | g0zRAQrFmmFxCMfWqrwKZ3RyGOSnu6jEAPc9QjLC5O/QzoPT82zkxYS3nauwB69K 16 | J4Y1eSLsgdrqHa7ApmEd6xiU8U07/AmkfK9XtC0gXtTNkeVDim+wiXnQKnranFJb 17 | eUEaeAeWeQ== 18 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /src/tracing.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_TRACING_H_ 2 | #define SRC_TRACING_H_ 3 | 4 | #include "include/bud/tracing.h" /* public dso API */ 5 | 6 | /* Forward declarations */ 7 | struct bud_client_s; 8 | struct bud_config_backend_s; 9 | 10 | #define BUD_TRACE_CLIENT_DECL(V) \ 11 | void bud_trace_##V(struct bud_client_s* client); \ 12 | 13 | #define BUD_TRACE_BACKEND_DECL(V) \ 14 | void bud_trace_##V(struct bud_client_s* client, \ 15 | struct bud_config_backend_s* backend); \ 16 | 17 | #define BUD_TRACE_CLOSE_DECL(V) \ 18 | void bud_trace_##V(struct bud_client_s* client, bud_error_t err); \ 19 | 20 | BUD_TRACING_CLIENT_ENUM(BUD_TRACE_CLIENT_DECL) 21 | BUD_TRACING_BACKEND_ENUM(BUD_TRACE_BACKEND_DECL) 22 | BUD_TRACING_CLOSE_ENUM(BUD_TRACE_CLOSE_DECL) 23 | 24 | #undef BUD_TRACE_CLIENT_DECL 25 | #undef BUD_TRACE_BACKEND_DECL 26 | #undef BUD_TRACE_CLOSE_DECL 27 | 28 | #endif /* SRC_TRACING_H_ */ 29 | -------------------------------------------------------------------------------- /test/keys/good-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC1jCCAcCgAwIBAgIBKzALBgkqhkiG9w0BAQswEjEQMA4GA1UEAxYHbWVnYS5j 3 | YTAeFw03MDAxMDEwMDAwMDBaFw0yNTA1MTkxNDA0MjJaMBUxEzARBgNVBAMWCmxv 4 | Y2FsLmhvc3QwggEgMAsGCSqGSIb3DQEBAQOCAQ8AMIIBCgKCAQEAweWdBll2b7eS 5 | kXMHSae0rJGLauM4Mufmd4P8HxSf0CMJbFO9lU2d/YQvS8ndllvl2zhDpvkfKGuG 6 | rj8JhGgWE5poN6S53c3kAEdawj72Kv3CyGc3B3wimEm/WwySd6GjMO8SKkktp5y5 7 | HH8CRhPTGnju9Ctx7DR5rvoQvZnClT5x484jWONk6xsjRoIVuB/F5/sdEz84G4SX 8 | qrvQ/PV8wmAJRNugrhYDLrg63cQ6rLX5kKJrmWh/XYreQNFrVrxA9k6ofuG9ebl5 9 | TYcZRz2a+lRqkIs7O+hEwhNjBQ6EcyiJIzXKC9fvB3xD3Aq/aR1Yq/+wc3bwAutz 10 | 47a27UtkeQIDAQABozowODA2BggrBgEFBQcBAQQqMCgwJgYIKwYBBQUHMAGGGmh0 11 | dHA6Ly8xMjcuMC4wLjE6ODAwMC9vY3NwMAsGCSqGSIb3DQEBCwOCAQEAbk6gk7qE 12 | Z0ryPN5PR6CYA01R8Ili8tKYSMjMc7NXdJeYWUL4TWw7iTcQ5ZBGVjyc+LWO5TKA 13 | hOdrVfpudV+ijp/4wF/3pYMsCo0HayFe4HEKIR31kuCK5QizVca49IPlNba0tlAy 14 | A01As1B4sXL+fTDjwLlsZ83bJHbGwewZcZCKJE8/W1cXEIOarpovlNf5BZzYOYik 15 | AMQ1H7r8z3eicsJy+UPM0jSvdrdTenxkR/iUTONAgigSHW0hP1TAkWF51wGxTZP5 16 | A0oKre/yPuo0AbLyl/lBo6cgR9HeKUJNc8ioin+bhVfNl4qWbhbOaRZPEJajJn+E 17 | T5FSPotsqqshsg== 18 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /src/config/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CONFIG_UTILS_H_ 2 | #define SRC_CONFIG_UTILS_H_ 3 | 4 | #include "openssl/bio.h" 5 | #include "openssl/x509.h" 6 | 7 | #include "parson.h" 8 | 9 | #include "src/config.h" 10 | #include "src/common.h" 11 | 12 | int bud_context_use_certificate_chain(bud_context_t* ctx, BIO *in); 13 | int bud_config_verify_cert(int status, X509_STORE_CTX* s); 14 | bud_config_balance_t bud_config_balance_to_enum(const char* balance); 15 | 16 | bud_error_t bud_config_load_file(bud_config_t* config, 17 | const char* path, 18 | const char** out); 19 | void bud_config_load_addr(JSON_Object* obj, 20 | bud_config_addr_t* addr); 21 | bud_error_t bud_config_load_ca_arr(X509_STORE** store, 22 | const JSON_Array* ca); 23 | bud_error_t bud_config_load_ca_file(X509_STORE** store, 24 | const char* filename); 25 | 26 | bud_error_t bud_config_verify_all_strings(const JSON_Array* npn, 27 | const char* name); 28 | 29 | #endif /* SRC_CONFIG_UTILS_H_ */ 30 | -------------------------------------------------------------------------------- /test/keys/agent1-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC9jCCAl+gAwIBAgIJAIcP1XllCTOsMA0GCSqGSIb3DQEBCwUAMHoxCzAJBgNV 3 | BAYTAlVTMQswCQYDVQQIEwJDQTELMAkGA1UEBxMCU0YxDzANBgNVBAoTBkpveWVu 4 | dDEQMA4GA1UECxMHTm9kZS5qczEMMAoGA1UEAxMDY2ExMSAwHgYJKoZIhvcNAQkB 5 | FhFyeUB0aW55Y2xvdWRzLm9yZzAeFw0xNjA0MjgwMTE5MDBaFw00MzA5MTMwMTE5 6 | MDBaMIGcMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExCzAJBgNVBAcMAlNGMQ8w 7 | DQYDVQQKDAZKb3llbnQxEDAOBgNVBAsMB05vZGUuanMxDzANBgNVBAMMBmFnZW50 8 | MTEdMBsGA1UEMQwUZGlzdGluZ3Vpc2hlZC1hZ2VudDExIDAeBgkqhkiG9w0BCQEW 9 | EXJ5QHRpbnljbG91ZHMub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDi 10 | j0Wq60oyxsuZDXF1J5qMvqc/QNqcMauU8iuU76kPQVMcEaDpwmJ2oW6w3a9MOMbi 11 | 6QVYOd5gEYMjgDK7dhzUQ0RYQuvpZOSkuNPuC2o450tc+5nGfWp+biWZpGTxFYp8 12 | HiNHv+OSx/4BXPBUWeyHYveiC/8PClEOoy3xNDbWmwIDAQABo2EwXzBdBggrBgEF 13 | BQcBAQRRME8wIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLm5vZGVqcy5vcmcvMCgG 14 | CCsGAQUFBzAChhxodHRwOi8vY2Eubm9kZWpzLm9yZy9jYS5jZXJ0MA0GCSqGSIb3 15 | DQEBCwUAA4GBAEEsVluEV3cpXSnjlnft2r1XQYUt90hfHig9Wlpsu+WBEqyGWHpq 16 | in48URW7vBoecfO2SFE194HW1y+4TouzhQdyK8yWaTxJbfD9X31vrfO21BSB9UK1 17 | J0NjfMcU6a6UEixwi+/GYXZgGmtWqyBIuiGDPaMWNYopfBS2f9ZfAuip 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /keys/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDBjCCAe4CCQCC8qgopCwXKDANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMB4XDTEzMDEwODEyMzYyM1oXDTIzMDEwNjEyMzYyM1owRTELMAkG 5 | A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0 6 | IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB 7 | ALYWwPVTdWu+55Y9WKcZKi+iC/BVoV6uejF/7twOQZRAMAs2rFhCnxmds8d7jcX6 8 | wBHOejKFYqbm9WPORckieOVmHRHYLhzCXHrhYMHSYW5NxwTCxdka+QEzoFJqBSwO 9 | AkBQaF9ETA/GDa22TGN0DqUAtH6AiT3FAf4lSng1gLnMaa6+jYDmHmGHf2epRw+N 10 | U8Kb4g8iNLPP10M3x1DIlVtB2MvXgHFY5/fz4BSD8QweqUGaoIuAcLdJ6qJmbztm 11 | FWCWmr3uNHo2V6KxjHMMpziCa+280im6OgsdsgMuQO949hk+5k+PZ/WR+Ia4NylE 12 | FI6cVXpZwALAVWxC9VO1oocCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAluAKUeNS 13 | brX8+Jr7hOEftd+maTb8ERV4CU2wFSeCqupoSGh4Er37FO+eL1cAzaewpQzx8Uvp 14 | q7oO+utrOKGA42P0bkdX6vqXZ73seorKl4Mv6Lnq1z1uTRTQyAbvJpv6mYrr+oz5 15 | QgTGvPqUWqpv+OPf2R9J00IVLlfIjdKGLt0iZ3QEeS6SDkV/MHT4fEc9fImsg6Rq 16 | xcb8kmcv9GAHo7YzxOw2F85h93epl6zUUlW2QCzUbvEF2S5NQowdkDQz0py6o4tv 17 | S5Dh5XYLDm7gjcRElLNLaTmdhq9IGBEdbXy6a9kzJqZz4+AOn5+WPnA/KhVhWoTE 18 | nJHIzjR3CQEkbA== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bud", 3 | "version": "4.0.28", 4 | "description": "Bud - TLS Terminator", 5 | "bin": { 6 | "bud": "./bin/bud" 7 | }, 8 | "main": "./lib/bud.js", 9 | "preferGlobal": true, 10 | "files": [ 11 | "src", 12 | "deps", 13 | "lib", 14 | "bin", 15 | "include", 16 | "npm", 17 | "binding.gyp", 18 | "bud.gyp", 19 | "bud.gyp.json", 20 | "config.gypi", 21 | "options.gypi" 22 | ], 23 | "scripts": { 24 | "install": "node_modules/.bin/gypkg build bud.gyp.json --insecure", 25 | "test": "mocha --reporter spec test/*-test.js" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "git@github.com:indutny/bud.git" 30 | }, 31 | "keywords": [ 32 | "bud", 33 | "tls", 34 | "openssl" 35 | ], 36 | "author": "Fedor Indutny ", 37 | "license": "MIT", 38 | "gypfile": true, 39 | "bugs": { 40 | "url": "https://github.com/indutny/bud/issues" 41 | }, 42 | "homepage": "https://github.com/indutny/bud", 43 | "devDependencies": { 44 | "asn1.js": "^4.5.2", 45 | "asn1.js-rfc2560": "^4.0.0", 46 | "git-secure-tag": "^2.2.0", 47 | "mocha": "^2.4.5", 48 | "ocsp": "^1.0.3", 49 | "semver": "^5.1.0", 50 | "spdy": "^3.2.3", 51 | "utile": "^0.3.0" 52 | }, 53 | "dependencies": { 54 | "gypkg": "^2.7.1" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/x-forward-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const fixtures = require('./fixtures'); 5 | const request = fixtures.request; 6 | const malformedRequest = fixtures.malformedRequest; 7 | const spdyRequest = fixtures.spdyRequest; 8 | 9 | describe('Bud TLS Terminator/x-forward', () => { 10 | const sh = fixtures.getServers({ 11 | frontend: { 12 | npn: [ 'spdy/3.1' , 'spdy/3' , 'spdy/2' , 'http/1.1' ] 13 | }, 14 | backends: [{ 15 | 'x-forward': true 16 | }] 17 | }); 18 | 19 | it('should work with http', (cb) => { 20 | request(sh, '/hello', (res, body) => { 21 | assert.equal(sh.backends[0].requests, 1); 22 | assert.equal(res.headers['x-got-forwarded-for'], '127.0.0.1'); 23 | assert.equal(res.headers['x-got-forwarded-proto'], 'https'); 24 | cb(); 25 | }); 26 | }); 27 | 28 | it('should work with LF-only http', (cb) => { 29 | malformedRequest(sh, '/hello', (body) => { 30 | assert.equal(sh.backends[0].requests, 1); 31 | assert(/X-Got-Forwarded-For: 127.0.0.1/.test(body)); 32 | cb(); 33 | }); 34 | }); 35 | 36 | it('should work with spdy', (cb) => { 37 | spdyRequest(sh, '/hello', (res, body) => { 38 | assert.equal(sh.backends[0].requests, 1); 39 | assert.equal(res.headers['x-got-forwarded-for'], '127.0.0.1'); 40 | cb(); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /benchmarks/index.md: -------------------------------------------------------------------------------- 1 | # Bud vs Stud benchmarks 2 | 3 | I used two [SmartOS][0] in a [Joyent Cloud][1]: one 16 core (server) and one 4 | 4 core (client). Both servers were initialized with a [bootstrap repo][2]. 5 | 6 | ## Boostrap instructions 7 | 8 | On both machines: 9 | 10 | ```bash 11 | pkgin -y update && pkgin -y install scmgit 12 | git clone git@github.com:indutny/bud-benchmark-comparison.git 13 | cd bud-benchmark-comparison 14 | ``` 15 | 16 | On server: 17 | 18 | ```bash 19 | ./init-server.sh 20 | ``` 21 | 22 | On client: 23 | 24 | ```bash 25 | ./init-client.sh 26 | node process.js > normal.csv 27 | ./init-client.sh big 28 | node process.js > big.csv 29 | ``` 30 | 31 | `normal.csv` - will contain [CSV][3] data for a "hello world" response endpoint, 32 | `big.csv` - will contain outputs for a 128kb "AAA..." responses. 33 | 34 | ## Charts 35 | 36 | Normal response: 37 | 38 | ![Normal RPS](https://raw.github.com/indutny/bud/master/benchmarks/normal-rps.png) 39 | ![Normal Response](https://raw.github.com/indutny/bud/master/benchmarks/normal-resp.png) 40 | 41 | Big response: 42 | 43 | ![Big RPS](https://raw.github.com/indutny/bud/master/benchmarks/big2-rps.png) 44 | ![Big Response](https://raw.github.com/indutny/bud/master/benchmarks/big2-resp.png) 45 | 46 | [0]: http://smartos.org/ 47 | [1]: http://www.joyent.com/ 48 | [2]: https://github.com/indutny/bud-benchmark-comparison 49 | [3]: http://en.wikipedia.org/wiki/Comma-separated_values 50 | -------------------------------------------------------------------------------- /npm/release.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const spawn = require('child_process').spawn; 7 | const semver = require('semver'); 8 | const gitSecureTag = require('git-secure-tag'); 9 | 10 | const root = path.join(__dirname, '..'); 11 | 12 | // Update version and save package.json 13 | const packageFile = path.join(root, 'package.json'); 14 | const pkg = require(packageFile); 15 | pkg.version = semver.inc(pkg.version, process.argv[2]); 16 | fs.writeFileSync(packageFile, 17 | JSON.stringify(pkg, null, 2) + '\n'); 18 | 19 | // Update src/version.h 20 | const v = semver.parse(pkg.version); 21 | const versionFile = path.join(root, 'src', 'version.h'); 22 | let header = fs.readFileSync(versionFile).toString(); 23 | 24 | header = header.replace(/(BUD_VERSION_MAJOR )\d+/, function(all, key) { 25 | return key + v.major; 26 | }); 27 | header = header.replace(/(BUD_VERSION_MINOR )\d+/, function(all, key) { 28 | return key + v.minor; 29 | }); 30 | header = header.replace(/(BUD_VERSION_PATCH )\d+/, function(all, key) { 31 | return key + v.patch; 32 | }); 33 | 34 | fs.writeFileSync(versionFile, header); 35 | 36 | // git tag 37 | const tag = 'v' + pkg.version; 38 | const commitProc = spawn('git', [ 'commit', '-asS', '-m', tag ], { 39 | stdio: 'inherit' 40 | }); 41 | 42 | commitProc.once('exit', function(code) { 43 | if (code !== 0) 44 | return; 45 | const api = new gitSecureTag.API(root); 46 | api.sign(tag, 'HEAD'); 47 | }); 48 | -------------------------------------------------------------------------------- /src/bud.d: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | int fd; 3 | uint16_t port; 4 | uint64_t host; 5 | uint64_t cipher; 6 | uint64_t protocol; 7 | uint64_t servername; 8 | } bud_dtrace_handshake_t; 9 | 10 | typedef struct { 11 | int fd; 12 | uint16_t port; 13 | string host; 14 | string cipher; 15 | string protocol; 16 | string servername; 17 | } bud_handshake_t; 18 | 19 | translator bud_handshake_t { 20 | fd = *(int32_t*) copyin((uintptr_t) &h->fd, sizeof(h->fd)); 21 | port = *(uint16_t*) copyin((uintptr_t) &h->port, sizeof(h->port)); 22 | host = copyinstr((uintptr_t) *(uint64_t*) 23 | copyin((uintptr_t) &h->host, sizeof(h->host))); 24 | cipher = copyinstr((uintptr_t) *(uint64_t*) 25 | copyin((uintptr_t) &h->cipher, sizeof(h->cipher))); 26 | protocol = copyinstr((uintptr_t) *(uint64_t*) 27 | copyin((uintptr_t) &h->protocol, sizeof(h->protocol))); 28 | servername = copyinstr((uintptr_t) *(uint64_t*) 29 | copyin((uintptr_t) &h->servername, sizeof(h->servername))); 30 | }; 31 | 32 | typedef struct { 33 | int fd; 34 | uint16_t port; 35 | uint64_t host; 36 | } bud_dtrace_connection_t; 37 | 38 | typedef struct { 39 | int fd; 40 | uint16_t port; 41 | string host; 42 | } bud_connection_t; 43 | 44 | translator bud_connection_t { 45 | fd = *(int32_t*) copyin((uintptr_t) &c->fd, sizeof(c->fd)); 46 | port = *(uint16_t*) copyin((uintptr_t) &c->port, sizeof(c->port)); 47 | host = copyinstr((uintptr_t) *(uint64_t*) 48 | copyin((uintptr_t) &c->host, sizeof(c->host))); 49 | }; 50 | -------------------------------------------------------------------------------- /keys/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAthbA9VN1a77nlj1YpxkqL6IL8FWhXq56MX/u3A5BlEAwCzas 3 | WEKfGZ2zx3uNxfrAEc56MoVipub1Y85FySJ45WYdEdguHMJceuFgwdJhbk3HBMLF 4 | 2Rr5ATOgUmoFLA4CQFBoX0RMD8YNrbZMY3QOpQC0foCJPcUB/iVKeDWAucxprr6N 5 | gOYeYYd/Z6lHD41TwpviDyI0s8/XQzfHUMiVW0HYy9eAcVjn9/PgFIPxDB6pQZqg 6 | i4Bwt0nqomZvO2YVYJaave40ejZXorGMcwynOIJr7bzSKbo6Cx2yAy5A73j2GT7m 7 | T49n9ZH4hrg3KUQUjpxVelnAAsBVbEL1U7WihwIDAQABAoIBACdvHBDFJ0vTRzI5 8 | TOa7Q3CXZoCA+vaXUK1BqIgNqlQh5oW3LHHc07nndlTARD7ZBBmXHs2sJ2Y/5Grd 9 | 9C0QAyCjEa6Yo7vkt8SA5MR0/Fa4D17Pk6tl9QE2ngTbIw2cZw5om4HuN46+9J1n 10 | OnnbW4SOd4hh69btwHW6u7r2007pQXme0AMlAxhgfIEiD6tHjqZDCtK4G0d+GSq6 11 | Ks/IpXckLcrluqJ2gHVB2sbBbjh1m7dHCVzBKynCwaHrWbNla8P6j3SdabP4Umcb 12 | tu3hP3nLigMJmA16KdkayUg05OvUTBAkReLcVZYZQ80LmM+NioB6UvwYy677h4ma 13 | XFihvLkCgYEA3aLOY2rve6DxvTXDJe8D91V//+5B9XBcyLP2cMEXIP+3W9Gto9tz 14 | vTIOcwj8MLKh22KBBMCP7OQlXFalBoG/d5Y9YKr6X2XHBcEw96Nm1HZctUPRPQdd 15 | +C8S5ikwTre9QMfLmLGKzgfIpDRYyijv0ZQZw+8qlNATtVrAMQGu6D0CgYEA0lI9 16 | zXK+4P28+SkkCUIG/Y31wF4KeM8V5s8eUYej8LV67GQIGs9XPCEIJY2cHZpn7qD8 17 | H90lucr90rwAHN2rLUSdq28NVL0+RTYKNBFRRvPNloLlKOR9RvorsFz5U4z48i9x 18 | yZ6gk+WL0ycc2oUFfihmQpHhRUiV46IB7deEXhMCgYEA1rW+3V8eC5VaOuOXXutS 19 | 20vwCX7GVUB6ElENICRfBK/V8NSLM98IG7QffV+p+H9E/+RIetMVWveWHgMuMcSG 20 | ORLJ+RkKHlrZ2IBUsMKSfqb/nvbJACdf6GuqEmC6lLe5VsV3PkBY6MlvnWu8zHOm 21 | CFFCOKc8iBef0CPPZmpsCD0CgYAOtS+bOWX9x+C6L9VUTGi+vHmuDSWAU0L91AgT 22 | vX+KaraA53HlphA8pTazoZaEP3L7LgjTlZx4xKhBX2JGon3A+aZpAagV//Hl1ySZ 23 | hYiAhLYgy2CJHolgOEhr2eSZoicakJTNe6lRDmFbz8Vlxp2et+aGyzrMpInO1Fp8 24 | LnEUPwKBgExMk6THLz8CzjJhaKDrNn1fSs6jUxgK12+1cIJxb5JEgRJs4W+yFuLw 25 | exCE5YKPwnZNrbji+bdkk9FwHPLuf20aH5fIB0UlMMVcvLGKyxYl7BG1pShN4k8C 26 | kKXaClwkngJ2eSs/AsNAe9hhbEpFjAHt+3D7Sbg8EvgeCwdjnIxw 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/keys/good-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEAweWdBll2b7eSkXMHSae0rJGLauM4Mufmd4P8HxSf0CMJbFO9 3 | lU2d/YQvS8ndllvl2zhDpvkfKGuGrj8JhGgWE5poN6S53c3kAEdawj72Kv3CyGc3 4 | B3wimEm/WwySd6GjMO8SKkktp5y5HH8CRhPTGnju9Ctx7DR5rvoQvZnClT5x484j 5 | WONk6xsjRoIVuB/F5/sdEz84G4SXqrvQ/PV8wmAJRNugrhYDLrg63cQ6rLX5kKJr 6 | mWh/XYreQNFrVrxA9k6ofuG9ebl5TYcZRz2a+lRqkIs7O+hEwhNjBQ6EcyiJIzXK 7 | C9fvB3xD3Aq/aR1Yq/+wc3bwAutz47a27UtkeQIDAQABAoIBAA1UFFQwaCTL0KZw 8 | 6spUXeYS5yuEzQMqz7vddcJJL6cmEWrB4uFVgYvAv2ONT88bKBTGrFfY/hxQL+Tg 9 | bm4MnZ/hWFKi3yfxFEA0p3d8zn0sDmyqM60vCk2/nAjNN3BDyyz88hm4CAxiiJL9 10 | 5BEfIb7LG1nrdpxPCyJNDEOMQ0Tw9D6SBDZ7JwNjBdes039kC+Ydr8b4IcyE4Wxr 11 | yB+GDID91ErQOar3hVmq6pVK6/677gUL/Qa7LLagWLR3rF6AN92ngZdvY3HrhObP 12 | m10GaicaeQ+LUrdLF4pMKN4aO0nuZReWlEVjqf37UW8CDc40KC0m0qo5Jgu5zoMn 13 | 1QEuVFUCgYEA7huws6JBVaHb76WsP7uKSSnWllGMX7nnFT00+I+Jwf2yjqYEkrIK 14 | p7Sz2i35pDi8ef+hn2vzcccnt+zLxjIaDlaboSEGdKpP4Tm5nwyIvcSSCKAJHGS0 15 | 9tQxkmqVCQ+ykX91JEbDx+CDQ6SrenR5/SfIjvZLx+Y7GwpFZ/FEezcCgYEA0Hd2 16 | 5HZJGW1i0YHiU+ZyovosmgpFL0mnLFHXR545b+0s7ErSezFWEK7oHvv3SxCXNCj0 17 | Z8hLxbbU1tbtVTx3jr7gImtEwoNwQLeowhP2xZkOHzmfB61xw+8mOUL1DkYZkT2Z 18 | N+1tvtcdZw1oM5vvbPHGgzoC2hjyXXRUfSBR1c8CgYBcSktRNSDRyizvVG3RPiW5 19 | egf8t5sLN4lECDdR6IDiuizNcbfwDCZpHndfp9ZNOtiDCPxiSHtRrHnOpm/NYHoM 20 | +LDV4XEZIzJXAALcjQUrWNeplC4z3SzvRwv3zmv52SXDk32aE/uqq3D1dQwqKJ9n 21 | 6lWoY/q3fpuGPZLtWOzMPQKBgAN0V1YF18RCGcca/eMiCHhG6QJMdUUJVWnYmVao 22 | eo8uQPZRbxd3g9OW8mXBCyIFTlwg6Zvmp8rI8tH0kc49yEVnSFOJr4QBpZSM9pDw 23 | v8Aafy9pRwdbim0Sw3FjeKgZ5UaQ3L8eghkNe4E/qaBhTyBhCUcXaQCwNSPzT4sb 24 | Q//lAoGBAMmmIVHeTkeSuIO3/5iXJwHitocD4ZOSR//srcjakKi8/zGx+ILPPZ+z 25 | S3lkLGRDP49tOg2qEhIRQTLwAckRWq4qhDzIzy61j/Via/SX5uO1xVYqDLCQYXKi 26 | wqkTvLvemYFWxCFiZ0UsCQR+dlH6DCHY1nO7uDD9CoNp/74MHhw+ 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /test/keys/issuer-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpQIBAAKCAQEA6BwXNSYbr2/tlzqfkEfuoBrMtIauZ0DDjv1EuSQV7nisMA3P 3 | UXzHWHlvhO2fy0Ucsvu+a5MDSEdJFzmVKxFHQKOBOfSbjEJGTegknTWHGAcha7Yi 4 | h1rF6/gd2AnjxM5m2loQz7Vdp5XbuCgaANnxwLqjalQiBOfbugj99yMytRBkCA8R 5 | 2/K84pj4WSL1grQJ/lQTiFFy/RrUeznpvRPD88jwroK35AijIET6Rc6RGRwLPZVi 6 | KiycxDBIwT0hE1PbJIU/u2vjVy/Jnuv7y7gAWaA76rCjO3ueBruc6ATHKxVJmY48 7 | +IACGgW2/Wo4PR75eFAEY3IBhIKwa6cK4u3AeQIDAQABAoIBADEM2qSrMHeTHjDy 8 | vQyW/ukLsM3HlmyQhaMeh5rectoJ201uZazxQwlB5FzBgJ7KtKVoSNXf4/YuxlXn 9 | n8vRo26X/vOZ6xMpYEUPdUlZqfpca2YhP8u91YynytrAQEdzXM3w8ZOLXsbPBvc0 10 | VkZuyEVzy5vpOqUxwD/grZa/8/PKI2ozuMYMn8PS/6GmOTCBQppmnyX+IwkKcBUc 11 | ceQQVRChUAUmpafBjMaO0jVwKScgvYnB8kFz79T2qLWhHeVE11p+cP+EBVb1zoYi 12 | oNGkHTjt4erwlMf1F2TPSw9zzyDSdF0MVjO8nqc3FImQyPvePLQXEM9fAjvwQFMD 13 | 9dtl3k8CgYEA8mFvxX6eGLyYzjvi3x2qKccTHK9D40E980dCilFweQdvugd367Kl 14 | Iv1nsk2KK9uoA4gv/fArvrBdPIHg31T4blYQRP5+ig9O81xZ5PogD+BsKDcp+mOw 15 | Tu6cJCSqer1lplgfr6AKxYDOaY2KQ6hb5s23Dw5fGpWT9ap4bYWXsX8CgYEA9Sbp 16 | LlTk9jYDNL11H0zJtuQUNmnmn54vwvYpwuAv8DFyDcQBEbST/A3DO02ctlzjUl4A 17 | gzVVH5tHRlH6NwiydOfbvAfHiqJwowW1ODluVVxRz93iEfaQWK4jmJ3QMy+Awf7B 18 | PNEZjlb0TRCLcZaiU5GCWDAsTDg99UfV+Zq/GgcCgYEAwLBvkD1x6E5+2G3JlgDa 19 | V6445nP7b35dhrtfDeFingUocGchkhKKr/9u4dQ9BVLyBiTNqn61+DnzVXgXUtUV 20 | T3pCIueDp7Q12sjPenbS6GxM78ezKqj38DhHj4BKu5s4ZX7nadK83XjiIA3zHoiI 21 | zzs5zdFqEBNb9lDYvpEXIHcCgYEArSqoKe8TsIskpptxYQOntMXVNT6HBpcBm9Cw 22 | XRRpKFx0+R5Ltwr8QV4jWv+OxA5HEMaPzB7fGy1fImADw94j2Ipy35YLMHVf5PfC 23 | M431ztwgehl8eD3XZpzTThmjmJRkeiQOsXvudmobtVgLd9f0n9032j1k/Q35TgpJ 24 | LkBN3v8CgYEAwh08hotjpcSaoTnv0l+gMTthq6PsBdUxTfqhdMvC8YZE1QufblKF 25 | 0jIPv0Czv3ywbT0Z5cjFBvebON3eMunhAE4XlZMMrkHRFZRPxt0yyp7nMNy+McdE 26 | RqN7TG+5S3th9s8mefl6WD3crM6ThTykcxZMAbn2M8fOI8T1EECZpT0= 27 | -----END RSA PRIVATE KEY----- -------------------------------------------------------------------------------- /test/ticket-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const tls = require('tls'); 5 | const crypto = require('crypto'); 6 | const fixtures = require('./fixtures'); 7 | 8 | describe('Bud TLS Terminator/IPC', () => { 9 | describe('ticket rotation', () => { 10 | const sh = fixtures.getServers({ 11 | frontend: { 12 | ticket_rotate: 1 13 | } 14 | }); 15 | 16 | it('should change the ticket key', (cb) => { 17 | let peer = tls.connect(sh.frontend.port, () => { 18 | const session = peer.getSession(); 19 | const ticket = peer.getTLSTicket(); 20 | peer.destroy(); 21 | 22 | // It should reconnect and have the same ticket 23 | peer = tls.connect({ 24 | port: sh.frontend.port, 25 | session: session 26 | }, () => { 27 | assert.equal(ticket.toString('hex'), 28 | peer.getTLSTicket().toString('hex')); 29 | 30 | peer.destroy(); 31 | setTimeout(() => next(session, ticket), 1500); 32 | }); 33 | }); 34 | 35 | function next(session, ticket) { 36 | let peer = tls.connect({ 37 | port: sh.frontend.port, 38 | session: session 39 | }, () => { 40 | assert.notEqual(ticket.toString('hex'), 41 | peer.getTLSTicket().toString('hex')); 42 | 43 | ticket = peer.getTLSTicket(); 44 | session = peer.getSession(); 45 | peer.destroy(); 46 | 47 | // It should reconnect and have the same ticket 48 | peer = tls.connect({ 49 | port: sh.frontend.port, 50 | session: session 51 | }, () => { 52 | assert.equal(ticket.toString('hex'), 53 | peer.getTLSTicket().toString('hex')); 54 | 55 | peer.destroy(); 56 | cb(); 57 | }); 58 | }); 59 | } 60 | }); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /src/ipc.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_IPC_H_ 2 | #define SRC_IPC_H_ 3 | 4 | #include 5 | 6 | #include "uv.h" 7 | #include "ringbuffer.h" 8 | 9 | #include "include/bud/ipc.h" 10 | 11 | #include "src/error.h" 12 | 13 | /* Forward declarations */ 14 | struct bud_config_s; 15 | 16 | typedef enum bud_ipc_state_e bud_ipc_state_t; 17 | typedef enum bud_ipc_ready_e bud_ipc_ready_t; 18 | typedef struct bud_ipc_s bud_ipc_t; 19 | 20 | typedef void (*bud_ipc_client_cb)(bud_ipc_t* ipc); 21 | typedef void (*bud_ipc_msg_cb)(bud_ipc_t* ipc, bud_ipc_msg_t* msg); 22 | 23 | enum bud_ipc_state_e { 24 | kBudIPCType, 25 | kBudIPCHeader, 26 | kBudIPCBody 27 | }; 28 | 29 | enum bud_ipc_ready_e { 30 | kBudIPCReadyNone, 31 | kBudIPCReadyNextTick, 32 | kBudIPCReadyDone 33 | }; 34 | 35 | #define BUD_IPC_HEADER_SIZE 5 36 | 37 | struct bud_ipc_s { 38 | struct bud_config_s* config; 39 | uv_pipe_t* handle; 40 | 41 | ringbuffer buffer; 42 | bud_ipc_state_t state; 43 | size_t waiting; 44 | bud_ipc_ready_t ready; 45 | 46 | bud_ipc_msg_t pending; 47 | 48 | bud_ipc_client_cb client_cb; 49 | bud_ipc_msg_cb msg_cb; 50 | }; 51 | 52 | bud_error_t bud_ipc_init(bud_ipc_t* ipc, struct bud_config_s* config); 53 | bud_error_t bud_ipc_open(bud_ipc_t* ipc, uv_file file); 54 | bud_error_t bud_ipc_start(bud_ipc_t* ipc); 55 | void bud_ipc_wait(bud_ipc_t* ipc); 56 | void bud_ipc_continue(bud_ipc_t* ipc); 57 | bud_error_t bud_ipc_balance(bud_ipc_t* ipc, uv_stream_t* server); 58 | bud_error_t bud_ipc_send(bud_ipc_t* ipc, 59 | bud_ipc_msg_header_t* header, 60 | const char* body); 61 | uv_stream_t* bud_ipc_get_stream(bud_ipc_t* ipc); 62 | void bud_ipc_close(bud_ipc_t* ipc); 63 | 64 | void bud_ipc_parse_set_ticket(bud_ipc_msg_t* msg, 65 | uint32_t* index, 66 | const char** data, 67 | uint32_t* size); 68 | bud_error_t bud_ipc_set_ticket(bud_ipc_t* ipc, 69 | uint32_t index, 70 | const char* data, 71 | uint32_t size); 72 | 73 | #endif /* SRC_IPC_H_ */ 74 | -------------------------------------------------------------------------------- /default-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "daemon": false, 3 | "workers": 1, 4 | "restart_timeout": 250, 5 | "master_ipc": false, 6 | "log": { 7 | "level": "info", 8 | "facility": "user", 9 | "stdio": true, 10 | "syslog": false 11 | }, 12 | "availability": { 13 | "death_timeout": 1000, 14 | "revive_interval": 2500, 15 | "retry_interval": 250, 16 | "max_retries": 5 17 | }, 18 | "frontend": { 19 | "port": 1443, 20 | "host": "0.0.0.0", 21 | "keepalive": 3600, 22 | "security": "ssl23", 23 | "server_preference": true, 24 | "max_send_fragment": 1400, 25 | "allow_half_open": false, 26 | "npn": ["http/1.1", "http/1.0"], 27 | "ciphers": "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA256:DHE-RSA-AES256-GCM-SHA256:ECDHE-ECDSA-AES256-SHA256:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES128-SHA:DES-CBC3-SHA", 28 | "ecdh": null, 29 | "dh": null, 30 | "cert": "keys/cert.pem", 31 | "key": "keys/key.pem", 32 | "passphrase": null, 33 | "ticket_key": null, 34 | "ticket_timeout": 3600, 35 | "ticket_rotate": 3600, 36 | "request_cert": false, 37 | "optional_cert": false, 38 | "ca": null, 39 | "reneg_window": 600, 40 | "reneg_limit": 0 41 | }, 42 | "balance": "roundrobin", 43 | "user": null, 44 | "group": null, 45 | "backend": [{ 46 | "port": 8000, 47 | "host": "127.0.0.1", 48 | "keepalive": 3600, 49 | "proxyline": false, 50 | "x-forward": false 51 | }], 52 | "sni": { 53 | "enabled": false, 54 | "port": 9000, 55 | "host": "127.0.0.1", 56 | "url": "/bud/sni/%s" 57 | }, 58 | "stapling": { 59 | "enabled": false, 60 | "port": 9000, 61 | "host": "127.0.0.1", 62 | "url": "/bud/stapling/%s" 63 | }, 64 | "contexts": [], 65 | "tracing": { 66 | "dso": [] 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/client.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CLIENT_H_ 2 | #define SRC_CLIENT_H_ 3 | 4 | #include "uv.h" 5 | #include "openssl/ssl.h" 6 | 7 | #include "src/client-common.h" 8 | #include "src/server.h" 9 | #include "src/http-pool.h" 10 | 11 | /* Forward declaration */ 12 | struct bud_config_s; 13 | struct bud_config_backend_s; 14 | struct bud_config_backend_list_s; 15 | 16 | typedef struct bud_client_s bud_client_t; 17 | typedef struct bud_client_host_s bud_client_host_t; 18 | 19 | struct bud_client_host_s { 20 | char host[INET6_ADDRSTRLEN]; 21 | unsigned int host_len; 22 | unsigned char family; 23 | uint16_t port; 24 | }; 25 | 26 | struct bud_client_s { 27 | struct bud_config_s* config; 28 | 29 | SSL* ssl; 30 | uint64_t id; 31 | 32 | /* Renegotiation attack prevention */ 33 | uint64_t last_handshake; 34 | int handshakes; 35 | 36 | /* Compact representation of both sides */ 37 | bud_client_side_t frontend; 38 | bud_client_side_t backend; 39 | 40 | /* State */ 41 | uv_connect_t connect_req; 42 | bud_client_progress_t connect; 43 | bud_client_progress_t close; 44 | bud_client_progress_t cycle; 45 | int recycle; 46 | int destroy_waiting; 47 | 48 | /* TLS Hello Data */ 49 | bud_client_progress_t async_hello; 50 | struct { 51 | const char* servername; 52 | size_t servername_len; 53 | unsigned int ocsp_request:1; 54 | } hello; 55 | 56 | /* SNI */ 57 | bud_http_request_t* sni_req; 58 | bud_context_t sni_ctx; 59 | 60 | /* Stapling */ 61 | bud_http_request_t* stapling_cache_req; 62 | bud_http_request_t* stapling_req; 63 | bud_context_pkey_type_t stapling_type; 64 | char* stapling_ocsp_resp; 65 | size_t stapling_ocsp_resp_len; 66 | 67 | /* Availability */ 68 | bud_client_progress_t retry; 69 | bud_config_balance_t balance; 70 | struct bud_config_backend_list_s* backend_list; 71 | struct bud_config_backend_s* selected_backend; 72 | uv_timer_t retry_timer; 73 | int retry_count; 74 | 75 | /* Balancing */ 76 | bud_client_host_t local; 77 | 78 | /* Tracing and proxyline */ 79 | bud_client_host_t remote; 80 | unsigned int proxyline_waiting; 81 | 82 | /* XForward */ 83 | struct { 84 | size_t skip; 85 | unsigned char crlf; 86 | } xforward; 87 | }; 88 | 89 | void bud_client_create(bud_config_t* config, uv_stream_t* stream); 90 | 91 | #endif /* SRC_CLIENT_H_ */ 92 | -------------------------------------------------------------------------------- /test/ipc-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const tls = require('tls'); 5 | const crypto = require('crypto'); 6 | const fixtures = require('./fixtures'); 7 | 8 | describe('Bud TLS Terminator/IPC', () => { 9 | describe('set ticket', () => { 10 | const sh = fixtures.getServers({ 11 | master_ipc: true 12 | }); 13 | 14 | function changeKey(index, cb) { 15 | const head = new Buffer(9); 16 | 17 | // type = setTicket 18 | head.writeUInt8(0x3, 0); 19 | 20 | head.writeUInt32BE(52, 1); 21 | head.writeUInt32BE(index, 5); 22 | 23 | const msg = Buffer.concat([ 24 | head, 25 | crypto.randomBytes(48) 26 | ]); 27 | 28 | sh.frontend.server.proc.stdin.write(msg, cb); 29 | } 30 | 31 | it('should change the ticket key', (cb) => { 32 | let peer = tls.connect(sh.frontend.port, () => { 33 | const session = peer.getSession(); 34 | const ticket = peer.getTLSTicket(); 35 | peer.destroy(); 36 | 37 | // It should reconnect and have the same ticket 38 | peer = tls.connect({ 39 | port: sh.frontend.port, 40 | session: session 41 | }, () => { 42 | assert.equal(ticket.toString('hex'), 43 | peer.getTLSTicket().toString('hex')); 44 | 45 | peer.destroy(); 46 | next(session, ticket); 47 | }); 48 | }); 49 | 50 | function next(session, ticket) { 51 | changeKey(0, () => { 52 | let peer = tls.connect({ 53 | port: sh.frontend.port, 54 | session: session 55 | }, () => { 56 | assert.notEqual(ticket.toString('hex'), 57 | peer.getTLSTicket().toString('hex')); 58 | 59 | ticket = peer.getTLSTicket(); 60 | session = peer.getSession(); 61 | peer.destroy(); 62 | 63 | // It should reconnect and have the same ticket 64 | peer = tls.connect({ 65 | port: sh.frontend.port, 66 | session: session 67 | }, () => { 68 | assert.equal(ticket.toString('hex'), 69 | peer.getTLSTicket().toString('hex')); 70 | 71 | peer.destroy(); 72 | cb(); 73 | }); 74 | }); 75 | }); 76 | } 77 | }); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/ocsp-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const fixtures = require('./fixtures'); 5 | const ocsp = require('ocsp'); 6 | const request = fixtures.request; 7 | const caRequest = fixtures.caRequest; 8 | const sniRequest = fixtures.sniRequest; 9 | const spdyRequest = fixtures.spdyRequest; 10 | const agentRequest = fixtures.agentRequest; 11 | 12 | describe('Bud TLS Terminator/OCSP', () => { 13 | const sh = fixtures.getServers({ 14 | frontend: { 15 | key: [ 16 | fixtures.goodKey, 17 | fixtures.ecKey 18 | ], 19 | cert: [ 20 | fixtures.goodCert + '\n' + fixtures.issuerCert, 21 | fixtures.ecCert + '\n' + fixtures.issuerCert 22 | ] 23 | }, 24 | stapling: { 25 | enabled: true, 26 | port: 9000 27 | } 28 | }); 29 | 30 | let ocspBackend; 31 | beforeEach((cb) => { 32 | ocspBackend = fixtures.ocspBackend().listen(9000, cb); 33 | }); 34 | 35 | afterEach((cb) => { 36 | ocspBackend.close(cb); 37 | }); 38 | 39 | const agent = new ocsp.Agent({ 40 | port: sh.frontend.port, 41 | ciphers: 'RSA' 42 | }); 43 | 44 | it('should work', (cb) => { 45 | agentRequest(sh, agent, '/hello', (res, body, info) => { 46 | assert(!/ECDSA/i.test(info.cipher.name)); 47 | assert.equal(sh.backends[0].requests, 1); 48 | assert.equal(ocspBackend.cacheHits, 0); 49 | assert.equal(ocspBackend.cacheMisses, 1); 50 | cb(); 51 | }); 52 | }); 53 | 54 | it('should use cached results', (cb) => { 55 | agentRequest(sh, agent, '/hello', (res, body) => { 56 | agentRequest(sh, agent, '/hello', (res, body, info) => { 57 | assert(!/ECDSA/i.test(info.cipher.name)); 58 | assert.equal(sh.backends[0].requests, 2); 59 | assert.equal(ocspBackend.cacheHits, 1); 60 | assert.equal(ocspBackend.cacheMisses, 1); 61 | cb(); 62 | }); 63 | }); 64 | }); 65 | 66 | it('should get ECC OCSP stapling', (cb) => { 67 | const eccAgent = new ocsp.Agent({ 68 | port: sh.frontend.port, 69 | cipher: 'ECDSA' 70 | }); 71 | 72 | agentRequest(sh, eccAgent, '/hello', (res, body, info) => { 73 | assert(/ECDSA/i.test(info.cipher.name)); 74 | assert.equal(sh.backends[0].requests, 1); 75 | assert.equal(ocspBackend.cacheHits, 0); 76 | assert.equal(ocspBackend.cacheMisses, 1); 77 | cb(); 78 | }); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /lib/bud.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const path = require('path'); 5 | const spawn = require('child_process').spawn; 6 | const util = require('util'); 7 | const EventEmitter = require('events').EventEmitter; 8 | 9 | const binary = path.resolve(__dirname, '..', 'bin', 'bud'); 10 | 11 | function Server(options) { 12 | EventEmitter.call(this); 13 | 14 | this.options = options ? util._extend({}, options) : {}; 15 | this.proc = null; 16 | } 17 | util.inherits(Server, EventEmitter); 18 | module.exports = Server; 19 | 20 | Server.createServer = function createServer(options) { 21 | return new Server(options); 22 | }; 23 | 24 | Server.prototype.listen = function listen(port, host, cb) { 25 | assert(!this.proc, 'Server is already listening'); 26 | 27 | if (!cb && typeof host === 'function') { 28 | cb = host; 29 | host = null; 30 | } 31 | if (!cb && typeof port === 'function') { 32 | cb = port; 33 | port = 0; 34 | } 35 | if (!port) 36 | port = 1443; 37 | if (!cb) 38 | cb = () => {}; 39 | 40 | if (host) 41 | this.options.host = host; 42 | if (!this.options.frontend) 43 | this.options.frontend = {}; 44 | this.options.frontend.port = port; 45 | 46 | if (cb) 47 | this.once('listening', cb); 48 | 49 | this.proc = spawn(binary, [ '-i', JSON.stringify(this.options) ], { 50 | stdio: [ 'pipe', 1, 'pipe' ] 51 | }); 52 | 53 | // Wait for "bud listening on [%s]:%d" 54 | let chunks = ''; 55 | let once = false; 56 | this.proc.stderr.on('readable', () => { 57 | const chunk = this.proc.stderr.read(); 58 | if (!chunk) 59 | return; 60 | 61 | if (once) 62 | return; 63 | chunks += chunk; 64 | assert(chunks.length < 16 * 1024); 65 | 66 | const match = chunks.match(/listening on \[([^\]]*)\]:(\d+)/); 67 | if (!match) 68 | return; 69 | 70 | once = true; 71 | this.emit('listening'); 72 | }); 73 | 74 | this.proc.once('close', (code) => { 75 | this.emit('error', new Error('Process died: ' + code)); 76 | }); 77 | }; 78 | 79 | Server.prototype.close = function close(cb) { 80 | this.proc.kill(); 81 | this.proc.removeAllListeners('close'); 82 | this.proc.once('close', () => { 83 | // Allow reuse 84 | this.proc = null; 85 | 86 | // Notify event listeners and invoke callback 87 | this.emit('close'); 88 | }); 89 | 90 | if (cb) 91 | this.once('close', cb); 92 | }; 93 | -------------------------------------------------------------------------------- /src/bud.c: -------------------------------------------------------------------------------- 1 | #ifndef _WIN32 2 | # include /* signal */ 3 | #endif /* !_WIN32 */ 4 | #include /* stderr */ 5 | #include /* NULL */ 6 | 7 | #include "openssl/rand.h" 8 | #include "openssl/ssl.h" 9 | #include "openssl/err.h" 10 | 11 | #include "src/config.h" 12 | #include "src/server.h" 13 | #include "src/master.h" 14 | #include "src/worker.h" 15 | 16 | static void bud_init_openssl(); 17 | 18 | 19 | int main(int argc, char** argv) { 20 | bud_config_t* config; 21 | bud_error_t err; 22 | 23 | #ifdef BUD_FIPS_ENABLED 24 | if (!FIPS_mode_set(1)) { 25 | int r; 26 | r = ERR_get_error(); 27 | fprintf(stderr, "openssl fips failed: %s\n", ERR_error_string(r, NULL)); 28 | return 1; 29 | } 30 | #endif /* BUD_FIPS_ENABLED */ 31 | 32 | /* Get enough entropy to start RAND */ 33 | for (;;) { 34 | int status; 35 | status = RAND_status(); 36 | ASSERT(status >= 0, "RAND_status() returned negative"); 37 | 38 | if (status != 0) 39 | break; 40 | 41 | /* Give up, RAND_poll() not supported. */ 42 | if (RAND_poll() == 0) 43 | break; 44 | } 45 | 46 | #ifndef _WIN32 47 | /* Ignore SIGPIPE */ 48 | signal(SIGPIPE, SIG_IGN); 49 | #endif /* !_WIN32 */ 50 | 51 | bud_init_openssl(); 52 | 53 | config = NULL; 54 | err = bud_config_new(argc, argv, &config); 55 | 56 | /* NOTE: bud_config_load will print everything itself */ 57 | if (!bud_is_ok(err)) 58 | goto fatal; 59 | 60 | if (config->is_worker) 61 | err = bud_worker(config); 62 | else 63 | err = bud_master(config); 64 | 65 | if (bud_is_ok(err)) 66 | uv_run(config->loop, UV_RUN_DEFAULT); 67 | 68 | /* Finalize server */ 69 | if (config->server != NULL) { 70 | bud_error_t ierr; 71 | if (config->is_worker) 72 | ierr = bud_worker_finalize(config); 73 | else 74 | ierr = bud_master_finalize(config); 75 | 76 | if (bud_is_ok(err) && !bud_is_ok(ierr)) 77 | err = ierr; 78 | } 79 | 80 | if (config->loop != NULL) 81 | uv_run(config->loop, UV_RUN_NOWAIT); 82 | 83 | fatal: 84 | if (config != NULL) 85 | bud_config_free(config); 86 | 87 | if (err.code == kBudErrSkip) 88 | return 0; 89 | 90 | if (!bud_is_ok(err)) { 91 | bud_error_print(stderr, err); 92 | return -1; 93 | } 94 | return 0; 95 | } 96 | 97 | 98 | void bud_init_openssl() { 99 | SSL_library_init(); 100 | OpenSSL_add_all_algorithms(); 101 | OpenSSL_add_all_digests(); 102 | SSL_load_error_strings(); 103 | ERR_load_crypto_strings(); 104 | } 105 | -------------------------------------------------------------------------------- /src/http-pool.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_HTTP_POOL_H_ 2 | #define SRC_HTTP_POOL_H_ 3 | 4 | #include "uv.h" 5 | #include "http_parser.h" 6 | #include "ringbuffer.h" 7 | #include "parson.h" 8 | 9 | #include "src/config.h" 10 | #include "src/queue.h" 11 | #include "src/error.h" 12 | 13 | #define BUD_HTTP_REQUEST_BUF_SIZE 8096 14 | 15 | typedef struct bud_http_pool_s bud_http_pool_t; 16 | typedef struct bud_http_request_s bud_http_request_t; 17 | typedef enum bud_http_request_state_e bud_http_request_state_t; 18 | typedef enum bud_http_method_e bud_http_method_t; 19 | typedef void (*bud_http_cb)(bud_http_request_t* req, bud_error_t err); 20 | 21 | enum bud_http_request_state_e { 22 | kBudHttpConnecting, 23 | kBudHttpConnected, 24 | kBudHttpRunning, 25 | kBudHttpDisconnected 26 | }; 27 | 28 | enum bud_http_method_e { 29 | kBudHttpGet, 30 | kBudHttpPost 31 | }; 32 | 33 | struct bud_http_pool_s { 34 | QUEUE pool; 35 | QUEUE reqs; 36 | 37 | bud_config_t* config; 38 | char* host; 39 | size_t host_len; 40 | struct sockaddr_storage addr; 41 | uint16_t port; 42 | }; 43 | 44 | struct bud_http_request_s { 45 | QUEUE member; 46 | 47 | /* Pool state */ 48 | bud_http_pool_t* pool; 49 | bud_http_request_state_t state; 50 | int complete; 51 | 52 | /* Connection */ 53 | uv_tcp_t tcp; 54 | uv_connect_t connect; 55 | uv_write_t write; 56 | char buf[BUD_HTTP_REQUEST_BUF_SIZE]; 57 | ringbuffer response_buf; 58 | http_parser parser; 59 | 60 | /* Request */ 61 | bud_http_method_t method; 62 | char* url; 63 | size_t url_len; 64 | char* body; 65 | size_t body_len; 66 | bud_http_cb cb; 67 | void* data; 68 | int code; 69 | JSON_Value* response; 70 | }; 71 | 72 | bud_http_pool_t* bud_http_pool_new(bud_config_t* config, 73 | const char* host, 74 | uint16_t port, 75 | bud_error_t* err); 76 | void bud_http_pool_free(bud_http_pool_t* pool); 77 | 78 | bud_http_request_t* bud_http_get(bud_http_pool_t* pool, 79 | const char* fmt, 80 | const char* arg, 81 | size_t arg_len, 82 | bud_http_cb cb, 83 | bud_error_t* err); 84 | bud_http_request_t* bud_http_post(bud_http_pool_t* pool, 85 | const char* fmt, 86 | const char* arg, 87 | size_t arg_len, 88 | const char* data, 89 | size_t data_len, 90 | bud_http_cb cb, 91 | bud_error_t* err); 92 | void bud_http_request_cancel(bud_http_request_t* request); 93 | 94 | #endif /* SRC_HTTP_POOL_H_ */ 95 | -------------------------------------------------------------------------------- /src/server.c: -------------------------------------------------------------------------------- 1 | #include /* ntohs */ 2 | #include /* calloc, free */ 3 | #include /* snprintf */ 4 | 5 | #include "uv.h" 6 | 7 | #include "src/master.h" 8 | #include "src/server.h" 9 | #include "src/client.h" 10 | 11 | static bud_error_t bud_server_new(bud_config_t* config, 12 | bud_config_addr_t* addr); 13 | static void bud_server_free(bud_server_t* server); 14 | static void bud_server_close_cb(uv_handle_t* handle); 15 | static void bud_server_connection_cb(uv_stream_t* stream, int status); 16 | 17 | bud_error_t bud_create_servers(bud_config_t* config) { 18 | bud_error_t err; 19 | int i; 20 | 21 | if (config->frontend.interface.count == 0) { 22 | err = bud_server_new(config, (bud_config_addr_t*) &config->frontend); 23 | if (!bud_is_ok(err)) 24 | goto fatal; 25 | } 26 | 27 | for (i = 0; i < config->frontend.interface.count; i++) { 28 | err = bud_server_new(config, &config->frontend.interface.list[i]); 29 | if (!bud_is_ok(err)) 30 | goto fatal; 31 | } 32 | return bud_ok(); 33 | 34 | fatal: 35 | bud_free_servers(config); 36 | return err; 37 | } 38 | 39 | 40 | bud_error_t bud_server_new(bud_config_t* config, bud_config_addr_t* addr) { 41 | int r; 42 | bud_error_t err; 43 | bud_server_t* server; 44 | 45 | server = calloc(1, sizeof(*server)); 46 | 47 | server->config = config; 48 | 49 | /* Initialize tcp handle */ 50 | r = uv_tcp_init(config->loop, &server->tcp); 51 | if (r != 0) { 52 | err = bud_error_num(kBudErrTcpServerInit, r); 53 | goto failed_tcp_init; 54 | } 55 | 56 | r = uv_tcp_bind(&server->tcp, (struct sockaddr*) &addr->addr, 0); 57 | if (r != 0) { 58 | err = bud_error_num(kBudErrTcpServerBind, r); 59 | goto failed_bind; 60 | } 61 | 62 | r = uv_listen((uv_stream_t*) &server->tcp, 256, bud_server_connection_cb); 63 | if (r != 0) { 64 | err = bud_error_num(kBudErrServerListen, r); 65 | goto failed_bind; 66 | } 67 | 68 | server->prev = config->server; 69 | config->server = server; 70 | return bud_ok(); 71 | 72 | failed_bind: 73 | uv_close((uv_handle_t*) &server->tcp, bud_server_close_cb); 74 | return err; 75 | 76 | failed_tcp_init: 77 | free(server); 78 | 79 | return err; 80 | } 81 | 82 | 83 | void bud_free_servers(bud_config_t* config) { 84 | bud_server_t* server; 85 | 86 | server = config->server; 87 | while (server != NULL) { 88 | bud_server_t* prev; 89 | 90 | prev = server->prev; 91 | bud_server_free(server); 92 | server = prev; 93 | } 94 | 95 | config->server = NULL; 96 | } 97 | 98 | 99 | void bud_server_free(bud_server_t* server) { 100 | server->config = NULL; 101 | uv_close((uv_handle_t*) &server->tcp, bud_server_close_cb); 102 | } 103 | 104 | 105 | void bud_server_close_cb(uv_handle_t* handle) { 106 | bud_server_t* server; 107 | 108 | server = container_of(handle, bud_server_t, tcp); 109 | free(server); 110 | } 111 | 112 | 113 | void bud_server_connection_cb(uv_stream_t* stream, int status) { 114 | bud_server_t* server; 115 | 116 | server = container_of(stream, bud_server_t, tcp); 117 | 118 | /* Create client and let it go */ 119 | bud_master_balance(server); 120 | } 121 | -------------------------------------------------------------------------------- /src/bio.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "openssl/bio.h" 4 | #include "ringbuffer.h" 5 | 6 | #include "src/bio.h" 7 | #include "src/common.h" 8 | 9 | static int bud_bio_init(BIO* bio); 10 | static int bud_bio_destroy(BIO* bio); 11 | static int bud_bio_write(BIO* bio, const char* data, int len); 12 | static int bud_bio_read(BIO* bio, char* out, int len); 13 | static long bud_bio_ctrl(BIO* bio, int cmd, long num, void* ptr); 14 | 15 | 16 | static const BIO_METHOD method = { 17 | BIO_TYPE_MEM, 18 | "Bud SSL BIO", 19 | bud_bio_write, 20 | bud_bio_read, 21 | NULL, 22 | NULL, 23 | bud_bio_ctrl, 24 | bud_bio_init, 25 | bud_bio_destroy, 26 | NULL 27 | }; 28 | 29 | 30 | BIO* bud_bio_new(ringbuffer* buffer) { 31 | BIO* bio = BIO_new((BIO_METHOD*) &method); 32 | if (bio == NULL) 33 | return NULL; 34 | 35 | bio->ptr = buffer; 36 | 37 | return bio; 38 | } 39 | 40 | 41 | int bud_bio_init(BIO* bio) { 42 | bio->shutdown = 1; 43 | bio->init = 1; 44 | bio->num = -1; 45 | 46 | return 1; 47 | } 48 | 49 | 50 | int bud_bio_destroy(BIO* bio) { 51 | bio->ptr = NULL; 52 | 53 | return 1; 54 | } 55 | 56 | 57 | int bud_bio_write(BIO* bio, const char* data, int len) { 58 | ringbuffer* buffer; 59 | 60 | BIO_clear_retry_flags(bio); 61 | 62 | buffer = bio->ptr; 63 | 64 | if (ringbuffer_write_into(buffer, data, len) == 0) 65 | return len; 66 | 67 | return -1; 68 | } 69 | 70 | 71 | int bud_bio_read(BIO* bio, char* out, int len) { 72 | int r; 73 | ringbuffer* buffer; 74 | 75 | BIO_clear_retry_flags(bio); 76 | 77 | buffer = bio->ptr; 78 | 79 | r = (int) ringbuffer_read_into(buffer, out, len); 80 | 81 | if (r == 0) { 82 | r = bio->num; 83 | if (r != 0) 84 | BIO_set_retry_read(bio); 85 | } 86 | 87 | return r; 88 | } 89 | 90 | 91 | long bud_bio_ctrl(BIO* bio, int cmd, long num, void* ptr) { 92 | ringbuffer* buffer; 93 | long ret; 94 | 95 | buffer = bio->ptr; 96 | ret = 1; 97 | 98 | switch (cmd) { 99 | case BIO_CTRL_EOF: 100 | ret = ringbuffer_is_empty(buffer); 101 | break; 102 | case BIO_C_SET_BUF_MEM_EOF_RETURN: 103 | bio->num = num; 104 | break; 105 | case BIO_CTRL_INFO: 106 | ret = (long) ringbuffer_size(buffer); 107 | if (ptr != NULL) 108 | *(void**)(ptr) = NULL; 109 | break; 110 | case BIO_CTRL_RESET: 111 | ASSERT(0, "BIO_CTRL_RESET Unsupported"); 112 | break; 113 | case BIO_C_SET_BUF_MEM: 114 | ASSERT(0, "BIO_C_SET_BUF_MEM Unsupported"); 115 | break; 116 | case BIO_C_GET_BUF_MEM_PTR: 117 | ASSERT(0, "BIO_C_GET_BUF_MEM Unsupported"); 118 | break; 119 | case BIO_CTRL_GET_CLOSE: 120 | ret = bio->shutdown; 121 | break; 122 | case BIO_CTRL_SET_CLOSE: 123 | bio->shutdown = num; 124 | break; 125 | case BIO_CTRL_WPENDING: 126 | ret = 0; 127 | break; 128 | case BIO_CTRL_PENDING: 129 | ret = (long) ringbuffer_size(buffer); 130 | break; 131 | case BIO_CTRL_DUP: 132 | case BIO_CTRL_FLUSH: 133 | ret = 1; 134 | break; 135 | case BIO_CTRL_PUSH: 136 | case BIO_CTRL_POP: 137 | default: 138 | ret = 0; 139 | break; 140 | } 141 | return ret; 142 | } 143 | -------------------------------------------------------------------------------- /src/config/ocsp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "openssl/ocsp.h" 5 | 6 | #include "src/config/ocsp.h" 7 | #include "src/common.h" 8 | #include "src/config.h" 9 | 10 | const char* bud_context_get_ocsp_id(bud_context_t* context, 11 | bud_context_pkey_type_t type, 12 | size_t* size) { 13 | char* encoded; 14 | unsigned char* pencoded; 15 | size_t encoded_len; 16 | char* base64; 17 | size_t base64_len; 18 | bud_context_pem_t* pem; 19 | 20 | pem = &context->pem[type]; 21 | 22 | if (pem->ocsp_id == NULL) 23 | return NULL; 24 | 25 | base64 = NULL; 26 | encoded = NULL; 27 | /* Return cached id */ 28 | if (pem->ocsp_der_id != NULL) 29 | goto done; 30 | 31 | encoded_len = i2d_OCSP_CERTID(pem->ocsp_id, NULL); 32 | base64_len = bud_base64_encoded_size(encoded_len); 33 | encoded = malloc(encoded_len); 34 | base64 = malloc(base64_len); 35 | if (encoded == NULL || base64 == NULL) 36 | goto done; 37 | 38 | pencoded = (unsigned char*) encoded; 39 | i2d_OCSP_CERTID(pem->ocsp_id, &pencoded); 40 | 41 | bud_base64_encode(encoded, encoded_len, base64, base64_len); 42 | pem->ocsp_der_id = base64; 43 | pem->ocsp_der_id_len = base64_len; 44 | base64 = NULL; 45 | 46 | done: 47 | free(encoded); 48 | free(base64); 49 | *size = pem->ocsp_der_id_len; 50 | return pem->ocsp_der_id; 51 | } 52 | 53 | 54 | const char* bud_context_get_ocsp_req(bud_context_t* context, 55 | bud_context_pkey_type_t type, 56 | size_t* size, 57 | char** ocsp_request, 58 | size_t* ocsp_request_len) { 59 | STACK_OF(OPENSSL_STRING)* urls; 60 | OCSP_REQUEST* req; 61 | OCSP_CERTID* id; 62 | char* encoded; 63 | unsigned char* pencoded; 64 | size_t encoded_len; 65 | bud_context_pem_t* pem; 66 | 67 | urls = NULL; 68 | id = NULL; 69 | encoded = NULL; 70 | 71 | pem = &context->pem[type]; 72 | 73 | /* Cached url */ 74 | if (pem->ocsp_url != NULL) 75 | goto has_url; 76 | 77 | urls = X509_get1_ocsp(pem->cert); 78 | if (urls == NULL) 79 | goto done; 80 | 81 | pem->ocsp_url = sk_OPENSSL_STRING_pop(urls); 82 | pem->ocsp_url_len = strlen(pem->ocsp_url); 83 | 84 | has_url: 85 | if (pem->ocsp_url == NULL) 86 | goto done; 87 | 88 | id = OCSP_CERTID_dup(pem->ocsp_id); 89 | if (id == NULL) 90 | goto done; 91 | 92 | /* Create request */ 93 | req = OCSP_REQUEST_new(); 94 | if (req == NULL) 95 | goto done; 96 | if (!OCSP_request_add0_id(req, id)) 97 | goto done; 98 | id = NULL; 99 | 100 | encoded_len = i2d_OCSP_REQUEST(req, NULL); 101 | encoded = malloc(encoded_len); 102 | if (encoded == NULL) 103 | goto done; 104 | 105 | pencoded = (unsigned char*) encoded; 106 | i2d_OCSP_REQUEST(req, &pencoded); 107 | OCSP_REQUEST_free(req); 108 | 109 | *ocsp_request = encoded; 110 | *ocsp_request_len = encoded_len; 111 | encoded = NULL; 112 | 113 | done: 114 | if (id != NULL) 115 | OCSP_CERTID_free(id); 116 | if (urls != NULL) 117 | X509_email_free(urls); 118 | if (encoded != NULL) 119 | free(encoded); 120 | 121 | *size = pem->ocsp_url_len; 122 | return pem->ocsp_url; 123 | } 124 | -------------------------------------------------------------------------------- /src/bud_provider.d: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | int dummy; 3 | } bud_dtrace_handshake_t; 4 | 5 | typedef struct { 6 | int dummy; 7 | } bud_handshake_t; 8 | 9 | typedef struct { 10 | int dummy; 11 | } bud_dtrace_connection_t; 12 | 13 | typedef struct { 14 | int dummy; 15 | } bud_connection_t; 16 | 17 | provider bud { 18 | probe frontend_accept(bud_dtrace_connection_t* c, 19 | int fd, 20 | int port, 21 | const char* host) 22 | : (bud_connection_t* c, int fd, int port, const char* host); 23 | 24 | probe backend_connect(bud_dtrace_connection_t* c, 25 | bud_dtrace_connection_t* backend, 26 | int fd, 27 | int port, 28 | const char* host, 29 | int backend_fd, 30 | int backend_port, 31 | const char* backend_host) 32 | : (bud_connection_t* c, 33 | bud_connection_t* backend, 34 | int fd, 35 | int port, 36 | const char* host, 37 | int backend_fd, 38 | int backend_port, 39 | const char* backend_host); 40 | 41 | probe kill_backend(bud_dtrace_connection_t* c, 42 | bud_dtrace_connection_t* backend, 43 | int fd, 44 | int port, 45 | const char* host, 46 | int backend_port, 47 | const char* backend_host) 48 | : (bud_connection_t* c, 49 | bud_connection_t* backend, 50 | int fd, 51 | int port, 52 | const char* host, 53 | int backend_port, 54 | const char* backend_host); 55 | 56 | probe revive_backend(bud_dtrace_connection_t* c, 57 | bud_dtrace_connection_t* backend, 58 | int fd, 59 | int port, 60 | const char* host, 61 | int backend_port, 62 | const char* backend_host) 63 | : (bud_connection_t* c, 64 | bud_connection_t* backend, 65 | int fd, 66 | int port, 67 | const char* host, 68 | int backend_port, 69 | const char* backend_host); 70 | 71 | probe end(bud_dtrace_connection_t* c, 72 | int fd, 73 | int port, 74 | const char* host) 75 | : (bud_connection_t* c, int fd, int port, const char* host); 76 | 77 | probe retry(bud_dtrace_connection_t* c, 78 | int fd, 79 | int port, 80 | const char* host) 81 | : (bud_connection_t* c, int fd, int port, const char* host); 82 | 83 | probe handshake(bud_dtrace_handshake_t* c, 84 | int fd, 85 | int port, 86 | const char* host) 87 | : (bud_handshake_t* c, int fd, int port, const char* host); 88 | 89 | probe close(bud_dtrace_connection_t* c, 90 | int fd, 91 | int port, 92 | const char* host, 93 | int code) 94 | : (bud_connection_t* c, int fd, int port, const char* host, int code); 95 | }; 96 | 97 | #pragma D attributes Evolving/Evolving/ISA provider bud provider 98 | #pragma D attributes Private/Private/Unknown provider bud module 99 | #pragma D attributes Private/Private/Unknown provider bud function 100 | #pragma D attributes Private/Private/ISA provider bud name 101 | #pragma D attributes Evolving/Evolving/ISA provider bud args 102 | -------------------------------------------------------------------------------- /src/sni.c: -------------------------------------------------------------------------------- 1 | #include /* NULL */ 2 | #include /* memset */ 3 | 4 | #include "openssl/err.h" 5 | #include "openssl/evp.h" 6 | #include "openssl/ssl.h" 7 | #include "parson.h" 8 | 9 | #include "src/error.h" 10 | #include "src/config.h" 11 | 12 | 13 | bud_error_t bud_sni_from_json(bud_config_t* config, 14 | struct json_value_t* json, 15 | bud_context_t* ctx) { 16 | JSON_Object* obj; 17 | JSON_Value* val; 18 | const char* cert_str; 19 | const char* key_str; 20 | const char* pass_str; 21 | JSON_Array* cert_strs; 22 | JSON_Array* key_strs; 23 | JSON_Array* pass_strs; 24 | bud_error_t err; 25 | 26 | cert_str = NULL; 27 | key_str = NULL; 28 | pass_str = NULL; 29 | cert_strs = NULL; 30 | key_strs = NULL; 31 | pass_strs = NULL; 32 | 33 | memset(ctx, 0, sizeof(*ctx)); 34 | 35 | obj = json_value_get_object(json); 36 | val = json_object_get_value(obj, "cert"); 37 | if (json_value_get_type(val) == JSONString) 38 | cert_str = json_value_get_string(val); 39 | else 40 | cert_strs = json_value_get_array(val); 41 | val = json_object_get_value(obj, "key"); 42 | if (json_value_get_type(val) == JSONString) 43 | key_str = json_value_get_string(val); 44 | else 45 | key_strs = json_value_get_array(val); 46 | val = json_object_get_value(obj, "passphrase"); 47 | if (json_value_get_type(val) == JSONString) 48 | pass_str = json_value_get_string(val); 49 | else 50 | pass_strs = json_value_get_array(val); 51 | 52 | if (obj == NULL || 53 | !((cert_str != NULL && key_str != NULL) || 54 | (cert_strs != NULL && key_strs != NULL))) { 55 | err = bud_error_str(kBudErrJSONParse, ""); 56 | goto fatal; 57 | } 58 | 59 | /* Load NPN from response */ 60 | ctx->cert_file = cert_str; 61 | ctx->key_file = key_str; 62 | ctx->key_pass = pass_str; 63 | ctx->cert_files = cert_strs; 64 | ctx->key_files = key_strs; 65 | ctx->key_passes = pass_strs; 66 | ctx->ciphers = json_object_get_string(obj, "ciphers"); 67 | ctx->ecdh = json_object_get_string(obj, "ecdh"); 68 | ctx->ticket_key = json_object_get_string(obj, "ticket_key"); 69 | val = json_object_get_value(obj, "ticket_timeout"); 70 | if (val != NULL) 71 | ctx->ticket_timeout = json_value_get_number(val); 72 | else 73 | ctx->ticket_timeout = -1; 74 | ctx->npn = json_object_get_array(obj, "npn"); 75 | ctx->ca_array = json_object_get_array(obj, "ca"); 76 | val = json_object_get_value(obj, "request_cert"); 77 | if (val != NULL) 78 | ctx->request_cert = json_value_get_boolean(val); 79 | val = json_object_get_value(obj, "server_preference"); 80 | if (val != NULL) 81 | ctx->server_preference = json_value_get_boolean(val); 82 | else 83 | ctx->server_preference = config->contexts[0].server_preference; 84 | err = bud_config_load_backend_list(config, obj, &ctx->backend); 85 | if (!bud_is_ok(err)) 86 | goto fatal; 87 | 88 | err = bud_context_init(config, ctx); 89 | 90 | /* Make sure that deallocated values won't be used */ 91 | ctx->cert_file = NULL; 92 | ctx->key_file = NULL; 93 | ctx->key_pass = NULL; 94 | ctx->cert_files = NULL; 95 | ctx->key_files = NULL; 96 | ctx->key_passes = NULL; 97 | ctx->ticket_key = NULL; 98 | if (!bud_is_ok(err)) 99 | goto fatal; 100 | 101 | return bud_ok(); 102 | 103 | fatal: 104 | if (!bud_is_ok(err)) { 105 | SSL_CTX_free(ctx->ctx); 106 | ctx->ctx = NULL; 107 | } 108 | free(ctx->backend.list); 109 | ctx->backend.list = NULL; 110 | return err; 111 | } 112 | -------------------------------------------------------------------------------- /test/proxyline-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const fixtures = require('./fixtures'); 5 | const request = fixtures.request; 6 | const caRequest = fixtures.caRequest; 7 | const sniRequest = fixtures.sniRequest; 8 | const spdyRequest = fixtures.spdyRequest; 9 | const renegRequest = fixtures.renegRequest; 10 | 11 | describe('Bud TLS Terminator/Proxyline', () => { 12 | describe('proxyline', () => { 13 | const sh = fixtures.getServers({ 14 | backends: [{ 15 | proxyline: true 16 | }] 17 | }); 18 | 19 | it('should work', (cb) => { 20 | let gotProxyline = false; 21 | 22 | request(sh, '/hello', (res, body) => { 23 | assert.equal(sh.backends[0].requests, 1); 24 | assert(gotProxyline); 25 | cb(); 26 | }); 27 | 28 | sh.backends[0].server.on('proxyline', (obj) => { 29 | assert.equal(obj.inbound.port, sh.frontend.port); 30 | gotProxyline = true; 31 | }); 32 | }); 33 | 34 | it('should not be sent twice on renegotiation', (cb) => { 35 | let gotProxyline = 0; 36 | 37 | renegRequest(sh, '/hello', () => { 38 | assert.equal(sh.backends[0].requests, 1); 39 | assert.equal(gotProxyline, 1); 40 | cb(); 41 | }); 42 | 43 | sh.backends[0].server.on('proxyline', (obj) => { 44 | assert.equal(obj.inbound.port, sh.frontend.port); 45 | gotProxyline++; 46 | }); 47 | }); 48 | }); 49 | 50 | describe('request cert and JSON proxyline', () => { 51 | const sh = fixtures.getServers({ 52 | frontend: { 53 | request_cert: true, 54 | ca: [ fixtures.ca ] 55 | }, 56 | backends: [{ 57 | proxyline: 'json' 58 | }] 59 | }); 60 | 61 | it('should request and validate cert', (cb) => { 62 | caRequest(sh, '/hello', false, (res, body) => { 63 | assert.equal(sh.backends[0].requests, 1); 64 | assert(gotProxyline); 65 | cb(); 66 | }); 67 | let gotProxyline = false; 68 | 69 | sh.backends[0].server.on('proxyline', (obj) => { 70 | assert.equal(obj.inbound.port, sh.frontend.port); 71 | assert(/agent1/.test(obj.outbound.cn)); 72 | assert(/distinguished-agent1/.test(obj.outbound.dn)); 73 | gotProxyline = true; 74 | }); 75 | }); 76 | }); 77 | 78 | describe('JSON proxyline', () => { 79 | const sh = fixtures.getServers({ 80 | frontend: { 81 | }, 82 | backends: [{ 83 | proxyline: 'json' 84 | }] 85 | }); 86 | 87 | it('should return empty cn cert', (cb) => { 88 | request(sh, '/hello', (res, body) => { 89 | assert.equal(sh.backends[0].requests, 1); 90 | assert(gotProxyline); 91 | cb(); 92 | }); 93 | let gotProxyline = false; 94 | 95 | sh.backends[0].server.on('proxyline', (obj) => { 96 | assert.equal(obj.inbound.port, sh.frontend.port); 97 | assert.equal(false, obj.outbound.cn); 98 | gotProxyline = true; 99 | }); 100 | }); 101 | }); 102 | 103 | describe('JSON proxyline', () => { 104 | const sh = fixtures.getServers({ 105 | frontend: { 106 | }, 107 | backends: [{ 108 | proxyline: 'json' 109 | }] 110 | }); 111 | 112 | it('should return empty cn cert', (cb) => { 113 | request(sh, '/hello', (res, body) => { 114 | assert.equal(sh.backends[0].requests, 1); 115 | assert(gotProxyline); 116 | cb(); 117 | }); 118 | let gotProxyline = false; 119 | 120 | sh.backends[0].server.on('proxyline', (obj) => { 121 | assert.equal(obj.inbound.port, sh.frontend.port); 122 | assert.equal(false, obj.outbound.cn); 123 | gotProxyline = true; 124 | }); 125 | }); 126 | }); 127 | }); 128 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_COMMON_H_ 2 | #define SRC_COMMON_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "uv.h" 8 | 9 | #include "src/error.h" 10 | 11 | #define ASSERT__COMMON(expr, desc, ...) \ 12 | do { \ 13 | if (!(expr)) { \ 14 | fprintf(stderr, desc "\n", __VA_ARGS__); \ 15 | abort(); \ 16 | } \ 17 | } while (0) 18 | 19 | #define ASSERT_VA(expr, desc, ...) \ 20 | ASSERT__COMMON(expr, \ 21 | "Assertion failed %s:%d\n" desc, \ 22 | __FILE__, \ 23 | __LINE__, \ 24 | __VA_ARGS__) 25 | 26 | #define ASSERT(expr, desc) \ 27 | ASSERT__COMMON(expr, \ 28 | "Assertion failed %s:%d\n" desc, \ 29 | __FILE__, \ 30 | __LINE__) 31 | 32 | #define UNEXPECTED ASSERT(0, "Unexpected") 33 | 34 | #define container_of(ptr, type, member) \ 35 | ((type *) ((char *) (ptr) - offsetof(type, member))) 36 | 37 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 38 | 39 | size_t bud_base64_decode(char* buf, 40 | size_t len, 41 | const char* src, 42 | const size_t srcLen); 43 | size_t bud_base64_decoded_size_fast(size_t size); 44 | 45 | #define bud_base64_encoded_size(size) ((size + 2 - ((size + 2) % 3)) / 3 * 4) 46 | size_t bud_base64_encode(const char* src, 47 | size_t slen, 48 | char* dst, 49 | size_t dlen); 50 | const char* bud_sslerror_str(int err); 51 | 52 | /* Hashmap */ 53 | 54 | typedef struct bud_hashmap_s bud_hashmap_t; 55 | typedef struct bud_hashmap_item_s bud_hashmap_item_t; 56 | typedef void (*bud_hashmap_free_cb)(void*); 57 | typedef bud_error_t (*bud_hashmap_iterate_cb)(bud_hashmap_item_t* item, 58 | void* arg); 59 | 60 | struct bud_hashmap_s { 61 | bud_hashmap_item_t* space; 62 | unsigned int size; 63 | }; 64 | 65 | struct bud_hashmap_item_s { 66 | const char* key; 67 | unsigned int key_len; 68 | void* value; 69 | }; 70 | 71 | bud_error_t bud_hashmap_init(bud_hashmap_t* hashmap, unsigned int size); 72 | void bud_hashmap_destroy(bud_hashmap_t* hashmap); 73 | 74 | bud_error_t bud_hashmap_insert(bud_hashmap_t* hashmap, 75 | const char* key, 76 | unsigned int key_len, 77 | void* value); 78 | void* bud_hashmap_get(bud_hashmap_t* hashmap, 79 | const char* key, 80 | unsigned int key_len); 81 | bud_error_t bud_hashmap_iterate(bud_hashmap_t* hashmap, 82 | bud_hashmap_iterate_cb cb, 83 | void* arg); 84 | 85 | bud_error_t bud_read_file_by_path(uv_loop_t* loop, 86 | const char* path, 87 | char** buffer); 88 | bud_error_t bud_read_file_by_fd(uv_loop_t* loop, uv_file fd, char** buffer); 89 | 90 | void bud_write_uint32(void* mem, uint32_t value, off_t offset); 91 | uint32_t bud_read_uint32(void* mem, off_t offset); 92 | 93 | /* Encoding primitives */ 94 | typedef enum bud_encoding_e bud_encoding_t; 95 | 96 | enum bud_encoding_e { 97 | kBudEncodingRaw, 98 | kBudEncodingBase64 99 | }; 100 | 101 | #endif /* SRC_COMMON_H_ */ 102 | -------------------------------------------------------------------------------- /include/bud/tracing.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_BUD_TRACING_H_ 2 | #define INCLUDE_BUD_TRACING_H_ 3 | 4 | #include "bud/error.h" /* bud_error_t */ 5 | #include "bud/common.h" 6 | #include /* uint16_t */ 7 | 8 | /* Forward declarations */ 9 | struct ssl_st; 10 | struct bud_logger_s; 11 | 12 | typedef struct bud_trace_module_s bud_trace_module_t; 13 | typedef struct bud_trace_client_s bud_trace_client_t; 14 | typedef struct bud_trace_backend_s bud_trace_backend_t; 15 | typedef enum bud_trace_balance_e bud_trace_balance_t; 16 | 17 | typedef void (*bud_trace_cb_t)(bud_trace_client_t* client); 18 | typedef void (*bud_trace_backend_cb_t)(bud_trace_client_t* client, 19 | bud_trace_backend_t* backend); 20 | typedef void (*bud_trace_close_cb_t)(bud_trace_client_t* client, 21 | bud_error_t err); 22 | 23 | #define CONNECTION_FIELDS \ 24 | int fd; \ 25 | uint16_t port; \ 26 | const char* host; \ 27 | 28 | enum bud_trace_balance_e { 29 | kBudTraceBalanceRoundRobin, 30 | kBudTraceBalanceSNI, 31 | kBudTraceBalanceOnFail, 32 | kBudTraceBalanceExternal 33 | }; 34 | 35 | struct bud_trace_client_s { 36 | CONNECTION_FIELDS 37 | 38 | /* OpenSSL's SSL* object */ 39 | struct ssl_st* ssl; 40 | 41 | /* Circularly-monotonic semi-unique connection id */ 42 | uint64_t id; 43 | 44 | /* Logger instance */ 45 | struct bud_logger_s* logger; 46 | }; 47 | 48 | struct bud_trace_backend_s { 49 | CONNECTION_FIELDS 50 | 51 | bud_trace_balance_t balance; 52 | const char* balance_str; 53 | int sni_match; 54 | }; 55 | 56 | #undef CONNECTION_FIELDS 57 | 58 | /* All tracing functions */ 59 | #define BUD_TRACING_ENUM(X) \ 60 | BUD_TRACING_CLIENT_ENUM(X) \ 61 | BUD_TRACING_BACKEND_ENUM(X) \ 62 | BUD_TRACING_CLOSE_ENUM(X) \ 63 | 64 | /* Tracing functions that do accept only one argument: client */ 65 | #define BUD_TRACING_CLIENT_ENUM(X) \ 66 | X(frontend_accept) \ 67 | X(end) \ 68 | X(handshake) \ 69 | X(retry) \ 70 | 71 | /* Tracing functions that do accept two arguments: client, backend */ 72 | #define BUD_TRACING_BACKEND_ENUM(X) \ 73 | X(backend_connect) \ 74 | X(kill_backend) \ 75 | X(revive_backend) \ 76 | 77 | /* Tracing functions that do accept two arguments: client, err */ 78 | #define BUD_TRACING_CLOSE_ENUM(X) \ 79 | X(close) \ 80 | 81 | #define BUD_TRACE_MODULE_CLIENT_DECL(V) bud_trace_cb_t V; 82 | #define BUD_TRACE_MODULE_BACKEND_DECL(V) bud_trace_backend_cb_t V; 83 | #define BUD_TRACE_MODULE_CLOSE_DECL(V) bud_trace_close_cb_t V; 84 | 85 | struct bud_trace_module_s { 86 | int version; 87 | BUD_TRACING_CLIENT_ENUM(BUD_TRACE_MODULE_CLIENT_DECL) 88 | BUD_TRACING_BACKEND_ENUM(BUD_TRACE_MODULE_BACKEND_DECL) 89 | BUD_TRACING_CLOSE_ENUM(BUD_TRACE_MODULE_CLOSE_DECL) 90 | }; 91 | 92 | #undef BUD_TRACE_MODULE_CLIENT_DECL 93 | #undef BUD_TRACE_MODULE_BACKEND_DECL 94 | #undef BUD_TRACE_MODULE_CLOSE_DECL 95 | 96 | /* Convenient define for a module declaration */ 97 | #define BUD_TRACE_MODULE bud_trace_module_t bud_trace_module 98 | #define BUD_TRACE_VERSION 3 99 | 100 | #endif /* INCLUDE_BUD_TRACING_H_ */ 101 | -------------------------------------------------------------------------------- /src/config/files.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "src/config/files.h" 5 | #include "src/common.h" 6 | #include "src/config.h" 7 | 8 | 9 | bud_error_t bud_config_files_reduce_size(bud_hashmap_item_t* item, void* arg) { 10 | size_t* size; 11 | 12 | size = arg; 13 | (*size) += item->key_len + 1 + strlen((char*) item->value) + 1; 14 | 15 | return bud_ok(); 16 | } 17 | 18 | 19 | bud_error_t bud_config_files_reduce_copy(bud_hashmap_item_t* item, void* arg) { 20 | char** res; 21 | int len; 22 | 23 | res = arg; 24 | 25 | /* Copy key */ 26 | memcpy(*res, item->key, item->key_len); 27 | (*res) += item->key_len; 28 | (*res)[0] = '\0'; 29 | (*res)++; 30 | 31 | len = strlen((char*) item->value); 32 | memcpy(*res, item->value, len + 1); 33 | (*res) += len + 1; 34 | 35 | return bud_ok(); 36 | } 37 | 38 | 39 | bud_error_t bud_config_get_files(bud_config_t* config, 40 | const char** files, 41 | size_t* size) { 42 | char* res; 43 | char* pres; 44 | 45 | if (config->files.str != NULL) 46 | goto done; 47 | 48 | /* Calculate size */ 49 | *size = 0; 50 | bud_hashmap_iterate(&config->files.hashmap, 51 | bud_config_files_reduce_size, 52 | size); 53 | 54 | res = malloc(*size); 55 | if (res == NULL) 56 | return bud_error_str(kBudErrNoMem, "config files list"); 57 | 58 | /* Copy data in */ 59 | pres = res; 60 | bud_hashmap_iterate(&config->files.hashmap, 61 | bud_config_files_reduce_copy, 62 | &pres); 63 | 64 | config->files.str = res; 65 | config->files.len = *size; 66 | 67 | done: 68 | *files = config->files.str; 69 | *size = config->files.len; 70 | return bud_ok(); 71 | } 72 | 73 | 74 | bud_error_t bud_config_set_files(bud_config_t* config, 75 | const char* files, 76 | size_t size) { 77 | bud_error_t err; 78 | const char* end; 79 | 80 | end = files + size; 81 | while (files < end) { 82 | char* key; 83 | int key_len; 84 | const char* value; 85 | 86 | key = strdup(files); 87 | if (key == NULL) { 88 | return bud_error_str(kBudErrNoMem, "Failed to dup key"); 89 | } 90 | key_len = strlen(key); 91 | files += key_len + 1; 92 | ASSERT(files < end, "Config file cache key OOB"); 93 | value = strdup(files); 94 | if (value == NULL) { 95 | free(key); 96 | return bud_error_str(kBudErrNoMem, "Failed to dup value"); 97 | } 98 | files += strlen(value) + 1; 99 | ASSERT(files <= end, "Config file cache value OOB"); 100 | 101 | err = bud_hashmap_insert(&config->files.hashmap, 102 | key, 103 | key_len, 104 | (void*) value); 105 | if (!bud_is_ok(err)) 106 | return err; 107 | } 108 | 109 | return bud_ok(); 110 | } 111 | 112 | 113 | bud_error_t bud_config_files_reduce_reload(bud_hashmap_item_t* item, 114 | void* arg) { 115 | bud_error_t err; 116 | bud_config_t* config; 117 | char* content; 118 | 119 | config = arg; 120 | 121 | ASSERT(config->loop != NULL, "Loop should be present"); 122 | err = bud_read_file_by_path(config->loop, item->key, &content); 123 | /* 124 | * Ignore file read errors, it might be a permission problem 125 | * after dropping the privileges 126 | */ 127 | if (!bud_is_ok(err)) 128 | return bud_ok(); 129 | 130 | free(item->value); 131 | item->value = content; 132 | 133 | return bud_ok(); 134 | } 135 | 136 | 137 | bud_error_t bud_config_reload_files(bud_config_t* config) { 138 | bud_error_t err; 139 | 140 | err = bud_hashmap_iterate(&config->files.hashmap, 141 | bud_config_files_reduce_reload, 142 | config); 143 | if (!bud_is_ok(err)) 144 | return err; 145 | 146 | /* Reset files string */ 147 | free(config->files.str); 148 | config->files.str = NULL; 149 | config->files.len = 0; 150 | return bud_ok(); 151 | } 152 | -------------------------------------------------------------------------------- /test/keys/Makefile: -------------------------------------------------------------------------------- 1 | all: agent1-cert.pem agent2-cert.pem agent3-cert.pem agent4-cert.pem ca2-crl.pem ec-cert.pem 2 | 3 | 4 | # 5 | # Create Certificate Authority: ca1 6 | # ('password' is used for the CA password.) 7 | # 8 | ca1-cert.pem: ca1.cnf 9 | openssl req -new -x509 -days 9999 -config ca1.cnf -keyout ca1-key.pem -out ca1-cert.pem 10 | 11 | # 12 | # Create Certificate Authority: ca2 13 | # ('password' is used for the CA password.) 14 | # 15 | ca2-cert.pem: ca2.cnf 16 | openssl req -new -x509 -days 9999 -config ca2.cnf -keyout ca2-key.pem -out ca2-cert.pem 17 | echo '01' > ca2-serial 18 | touch ca2-database.txt 19 | 20 | 21 | # 22 | # agent1 is signed by ca1. 23 | # 24 | 25 | agent1-key.pem: 26 | openssl genrsa -out agent1-key.pem 1024 27 | 28 | agent1-csr.pem: agent1.cnf agent1-key.pem 29 | openssl req -new -config agent1.cnf -key agent1-key.pem -out agent1-csr.pem 30 | 31 | agent1-cert.pem: agent1-csr.pem ca1-cert.pem ca1-key.pem 32 | openssl x509 -req \ 33 | -extfile agent1.cnf \ 34 | -extensions v3_ca \ 35 | -days 9999 \ 36 | -passin "pass:password" \ 37 | -in agent1-csr.pem \ 38 | -CA ca1-cert.pem \ 39 | -CAkey ca1-key.pem \ 40 | -CAcreateserial \ 41 | -out agent1-cert.pem 42 | 43 | agent1-verify: agent1-cert.pem ca1-cert.pem 44 | openssl verify -CAfile ca1-cert.pem agent1-cert.pem 45 | 46 | 47 | # 48 | # agent2 has a self signed cert 49 | # 50 | # Generate new private key 51 | agent2-key.pem: 52 | openssl genrsa -out agent2-key.pem 1024 53 | 54 | # Create a Certificate Signing Request for the key 55 | agent2-csr.pem: agent2-key.pem agent2.cnf 56 | openssl req -new -config agent2.cnf -key agent2-key.pem -out agent2-csr.pem 57 | 58 | # Create a Certificate for the agent. 59 | agent2-cert.pem: agent2-csr.pem agent2-key.pem 60 | openssl x509 -req \ 61 | -days 9999 \ 62 | -in agent2-csr.pem \ 63 | -signkey agent2-key.pem \ 64 | -out agent2-cert.pem 65 | 66 | agent2-verify: agent2-cert.pem 67 | openssl verify -CAfile agent2-cert.pem agent2-cert.pem 68 | 69 | # 70 | # agent3 is signed by ca2. 71 | # 72 | 73 | agent3-key.pem: 74 | openssl genrsa -out agent3-key.pem 1024 75 | 76 | agent3-csr.pem: agent3.cnf agent3-key.pem 77 | openssl req -new -config agent3.cnf -key agent3-key.pem -out agent3-csr.pem 78 | 79 | agent3-cert.pem: agent3-csr.pem ca2-cert.pem ca2-key.pem 80 | openssl x509 -req \ 81 | -days 9999 \ 82 | -passin "pass:password" \ 83 | -in agent3-csr.pem \ 84 | -CA ca2-cert.pem \ 85 | -CAkey ca2-key.pem \ 86 | -CAcreateserial \ 87 | -out agent3-cert.pem 88 | 89 | agent3-verify: agent3-cert.pem ca2-cert.pem 90 | openssl verify -CAfile ca2-cert.pem agent3-cert.pem 91 | 92 | 93 | # 94 | # agent4 is signed by ca2 (client cert) 95 | # 96 | 97 | agent4-key.pem: 98 | openssl genrsa -out agent4-key.pem 1024 99 | 100 | agent4-csr.pem: agent4.cnf agent4-key.pem 101 | openssl req -new -config agent4.cnf -key agent4-key.pem -out agent4-csr.pem 102 | 103 | agent4-cert.pem: agent4-csr.pem ca2-cert.pem ca2-key.pem 104 | openssl x509 -req \ 105 | -days 9999 \ 106 | -passin "pass:password" \ 107 | -in agent4-csr.pem \ 108 | -CA ca2-cert.pem \ 109 | -CAkey ca2-key.pem \ 110 | -CAcreateserial \ 111 | -extfile agent4.cnf \ 112 | -extensions ext_key_usage \ 113 | -out agent4-cert.pem 114 | 115 | agent4-verify: agent4-cert.pem ca2-cert.pem 116 | openssl verify -CAfile ca2-cert.pem agent4-cert.pem 117 | 118 | # 119 | # Make CRL with agent4 being rejected 120 | # 121 | ca2-crl.pem: ca2-key.pem ca2-cert.pem ca2.cnf 122 | openssl ca -revoke agent4-cert.pem \ 123 | -keyfile ca2-key.pem \ 124 | -cert ca2-cert.pem \ 125 | -config ca2.cnf \ 126 | -passin 'pass:password' 127 | openssl ca \ 128 | -keyfile ca2-key.pem \ 129 | -cert ca2-cert.pem \ 130 | -config ca2.cnf \ 131 | -gencrl \ 132 | -out ca2-crl.pem \ 133 | -passin 'pass:password' 134 | 135 | ec-key.pem: 136 | openssl ecparam -genkey -out ec-key.pem -name prime256v1 137 | 138 | ec-csr.pem: ec-key.pem 139 | openssl req -new -config ec.cnf -key ec-key.pem -out ec-csr.pem 140 | 141 | ec-cert.pem: ec-csr.pem ec-key.pem 142 | openssl x509 -req \ 143 | -days 9999 \ 144 | -in ec-csr.pem \ 145 | -signkey ec-key.pem \ 146 | -out ec-cert.pem 147 | 148 | clean: 149 | rm -f *.pem *.srl ca2-database.txt ca2-serial 150 | 151 | test: agent1-verify agent2-verify agent3-verify agent4-verify 152 | 153 | 154 | .PHONY: all clean test agent1-verify agent2-verify agent3-verify agent4-verify 155 | -------------------------------------------------------------------------------- /src/client-common.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CLIENT_PRIVATE_H_ 2 | #define SRC_CLIENT_PRIVATE_H_ 3 | 4 | #include "ringbuffer.h" 5 | 6 | #include "src/error.h" 7 | #include "src/logger.h" 8 | 9 | 10 | /* Forward declarations */ 11 | struct bud_client_s; 12 | 13 | typedef enum bud_client_side_type_e bud_client_side_type_t; 14 | typedef enum bud_client_progress_e bud_client_progress_t; 15 | typedef enum bud_client_proxyline_phase_e bud_client_proxyline_phase_t; 16 | typedef struct bud_client_side_s bud_client_side_t; 17 | typedef struct bud_client_error_s bud_client_error_t; 18 | 19 | enum bud_client_side_type_e { 20 | kBudFrontend, 21 | kBudBackend 22 | }; 23 | 24 | enum bud_client_progress_e { 25 | kBudProgressNone, 26 | kBudProgressRunning, 27 | kBudProgressDone 28 | }; 29 | 30 | struct bud_client_side_s { 31 | bud_client_side_type_t type; 32 | uv_tcp_t tcp; 33 | ringbuffer input; 34 | ringbuffer output; 35 | 36 | uv_write_t write_req; 37 | uv_shutdown_t shutdown_req; 38 | 39 | bud_client_progress_t reading; 40 | bud_client_progress_t shutdown; 41 | bud_client_progress_t close; 42 | bud_client_progress_t write; 43 | 44 | size_t write_size; 45 | }; 46 | 47 | struct bud_client_error_s { 48 | bud_error_t err; 49 | bud_client_side_t* side; 50 | }; 51 | 52 | enum bud_client_proxyline_phase_e { 53 | kBudProxylineHandshake = 0x1, 54 | kBudProxylineBackendConnect = 0x2, 55 | kBudProxylineOnce = 0x4 56 | }; 57 | 58 | const char* bud_side_str(bud_client_side_type_t side); 59 | bud_client_error_t bud_client_error(bud_error_t err, bud_client_side_t* side); 60 | bud_client_error_t bud_client_ok(); 61 | 62 | void bud_client_close(struct bud_client_s* client, bud_client_error_t err); 63 | void bud_client_close_cb(uv_handle_t* handle); 64 | void bud_client_alloc_cb(uv_handle_t* handle, 65 | size_t suggested_size, 66 | uv_buf_t* buf); 67 | void bud_client_read_cb(uv_stream_t* stream, 68 | ssize_t nread, 69 | const uv_buf_t* buf); 70 | bud_client_error_t bud_client_read_start(struct bud_client_s* client, 71 | bud_client_side_t* side); 72 | bud_client_error_t bud_client_cycle(struct bud_client_s* client); 73 | bud_client_error_t bud_client_prepend_proxyline( 74 | struct bud_client_s* client, 75 | bud_client_proxyline_phase_t phase); 76 | void bud_client_log(struct bud_client_s* client, 77 | bud_log_level_t level, 78 | const char* fmt, 79 | ...); 80 | int bud_client_ssl_cert_cb(SSL* ssl, void* arg); 81 | 82 | #define LOG(level, side, fmt, ...) \ 83 | bud_client_log(client, \ 84 | (level), \ 85 | "client %p on %s " fmt, \ 86 | client, \ 87 | bud_side_str((side)->type), \ 88 | __VA_ARGS__) 89 | 90 | #define LOG_LN(level, side, fmt) \ 91 | bud_client_log(client, \ 92 | (level), \ 93 | "client %p on %s " fmt, \ 94 | client, \ 95 | bud_side_str((side)->type)) 96 | 97 | #define INFO(side, fmt, ...) \ 98 | LOG(kBudLogInfo, side, fmt, __VA_ARGS__) 99 | 100 | #define NOTICE(side, fmt, ...) \ 101 | LOG(kBudLogNotice, side, fmt, __VA_ARGS__) 102 | 103 | #define WARNING(side, fmt, ...) \ 104 | LOG(kBudLogWarning, side, fmt, __VA_ARGS__) 105 | 106 | #define DBG(side, fmt, ...) \ 107 | LOG(kBudLogDebug, side, fmt, __VA_ARGS__) 108 | 109 | #define WARNING_LN(side, fmt) LOG_LN(kBudLogWarning, side, fmt) 110 | #define NOTICE_LN(side, fmt) LOG_LN(kBudLogNotice, side, fmt) 111 | #define DBG_LN(side, fmt) LOG_LN(kBudLogDebug, side, fmt) 112 | 113 | #endif /* SRC_CLIENT_PRIVATE_H_ */ 114 | -------------------------------------------------------------------------------- /src/worker.c: -------------------------------------------------------------------------------- 1 | #include /* NULL */ 2 | 3 | #include "uv.h" 4 | 5 | #include "src/worker.h" 6 | #include "src/ipc.h" 7 | #include "src/client.h" 8 | #include "src/common.h" 9 | #include "src/config.h" 10 | #include "src/error.h" 11 | #include "src/logger.h" 12 | 13 | static void bud_worker_close_cb(uv_handle_t* handle); 14 | static void bud_worker_signal_cb(uv_signal_t* signal, int status); 15 | static void bud_worker_ipc_client_cb(bud_ipc_t* ipc); 16 | static void bud_worker_ipc_msg_cb(bud_ipc_t* ipc, bud_ipc_msg_t* msg); 17 | 18 | bud_error_t bud_worker(bud_config_t* config) { 19 | int r; 20 | bud_error_t err; 21 | 22 | config->loop = uv_default_loop(); 23 | if (config->loop == NULL) { 24 | err = bud_error_str(kBudErrNoMem, "config->loop"); 25 | goto fatal; 26 | } 27 | 28 | err = bud_ipc_init(&config->ipc, config); 29 | if (!bud_is_ok(err)) 30 | goto fatal; 31 | 32 | config->ipc.client_cb = bud_worker_ipc_client_cb; 33 | config->ipc.msg_cb = bud_worker_ipc_msg_cb; 34 | 35 | err = bud_ipc_open(&config->ipc, 0); 36 | if (!bud_is_ok(err)) 37 | goto failed_ipc_open; 38 | 39 | err = bud_ipc_start(&config->ipc); 40 | if (!bud_is_ok(err)) 41 | goto failed_ipc_open; 42 | 43 | /* Wait for first file cache message */ 44 | bud_ipc_wait(&config->ipc); 45 | 46 | config->signal.sighup = malloc(sizeof(*config->signal.sighup)); 47 | if (config->signal.sighup == NULL) { 48 | err = bud_error_str(kBudErrNoMem, "config->sighup"); 49 | goto failed_ipc_open; 50 | } 51 | 52 | config->signal.sighup->data = config; 53 | 54 | r = uv_signal_init(config->loop, config->signal.sighup); 55 | if (r != 0) { 56 | err = bud_error_num(kBudErrSignalInit, r); 57 | goto failed_signal_init; 58 | } 59 | 60 | r = uv_signal_start(config->signal.sighup, bud_worker_signal_cb, SIGHUP); 61 | if (r != 0) { 62 | err = bud_error_num(kBudErrSignalInit, r); 63 | goto failed_signal_start; 64 | } 65 | uv_unref((uv_handle_t*) config->signal.sighup); 66 | 67 | err = bud_config_load(config); 68 | if (!bud_is_ok(err)) 69 | goto failed_signal_start; 70 | 71 | bud_ipc_start(&config->ipc); 72 | 73 | bud_clog(config, kBudLogDebug, "worker starting"); 74 | 75 | err = bud_ok(); 76 | return err; 77 | 78 | failed_signal_start: 79 | uv_close((uv_handle_t*) config->signal.sighup, bud_worker_close_cb); 80 | goto failed_ipc_open; 81 | 82 | failed_signal_init: 83 | free(config->signal.sighup); 84 | config->signal.sighup = NULL; 85 | 86 | failed_ipc_open: 87 | bud_ipc_close(&config->ipc); 88 | 89 | fatal: 90 | return err; 91 | } 92 | 93 | 94 | bud_error_t bud_worker_finalize(bud_config_t* config) { 95 | uv_close((uv_handle_t*) config->signal.sighup, bud_worker_close_cb); 96 | config->signal.sighup = NULL; 97 | bud_ipc_close(&config->ipc); 98 | 99 | return bud_ok(); 100 | } 101 | 102 | 103 | void bud_worker_close_cb(uv_handle_t* handle) { 104 | free(handle); 105 | } 106 | 107 | 108 | void bud_worker_ipc_client_cb(bud_ipc_t* ipc) { 109 | /* Accept client */ 110 | bud_client_create(ipc->config, bud_ipc_get_stream(ipc)); 111 | } 112 | 113 | 114 | void bud_worker_ipc_msg_cb(bud_ipc_t* ipc, bud_ipc_msg_t* msg) { 115 | bud_error_t err; 116 | 117 | /* No-op for now */ 118 | switch (msg->type) { 119 | case kBudIPCBalance: 120 | ASSERT(0, "Unexpected"); 121 | err = bud_ok(); 122 | break; 123 | case kBudIPCConfigFileCache: 124 | err = bud_config_set_files(ipc->config, 125 | (const char*) msg->data, 126 | msg->size); 127 | if (bud_is_ok(err)) 128 | bud_ipc_continue(ipc); 129 | break; 130 | case kBudIPCEOF: 131 | err = bud_ok(); 132 | break; 133 | case kBudIPCSetTicket: 134 | err = bud_config_set_ticket(ipc->config, msg); 135 | break; 136 | } 137 | 138 | if (bud_is_ok(err)) 139 | return; 140 | 141 | bud_error_log(ipc->config, kBudLogWarning, err); 142 | } 143 | 144 | 145 | void bud_worker_signal_cb(uv_signal_t* signal, int status) { 146 | bud_config_t* config; 147 | 148 | config = signal->data; 149 | if (status == UV_ECANCELED) 150 | return; 151 | 152 | bud_clog(config, kBudLogInfo, "Worker shutting down"); 153 | 154 | /* Close server and signal listener and let the worker die */ 155 | uv_close((uv_handle_t*) config->signal.sighup, bud_worker_close_cb); 156 | config->signal.sighup = NULL; 157 | bud_ipc_close(&config->ipc); 158 | } 159 | -------------------------------------------------------------------------------- /test/basic-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const https = require('https'); 5 | const net = require('net'); 6 | const fixtures = require('./fixtures'); 7 | const request = fixtures.request; 8 | const caRequest = fixtures.caRequest; 9 | const sniRequest = fixtures.sniRequest; 10 | const spdyRequest = fixtures.spdyRequest; 11 | const agentRequest = fixtures.agentRequest; 12 | 13 | describe('Bud TLS Terminator/Basic', () => { 14 | describe('single backend', () => { 15 | const sh = fixtures.getServers(); 16 | 17 | it('should support basic termination', (cb) => { 18 | request(sh, '/hello', (res, body) => { 19 | assert.equal(sh.backends[0].requests, 1); 20 | assert.equal(res.statusCode, 200); 21 | assert.equal(res.headers['x-backend-id'], 0); 22 | assert.equal(body, 'hello world'); 23 | cb(); 24 | }); 25 | }); 26 | }); 27 | 28 | describe('single backend with passphrase', () => { 29 | const sh = fixtures.getServers({ 30 | log: { 31 | level: 'debug' 32 | }, 33 | frontend: { 34 | key: fixtures.keys.caKey, 35 | cert: fixtures.keys.caCert, 36 | passphrase: 'password' 37 | } 38 | }); 39 | 40 | it('should support basic termination', (cb) => { 41 | request(sh, '/hello', (res, body) => { 42 | assert.equal(sh.backends[0].requests, 1); 43 | assert.equal(res.statusCode, 200); 44 | assert.equal(res.headers['x-backend-id'], 0); 45 | assert.equal(body, 'hello world'); 46 | cb(); 47 | }); 48 | }); 49 | }); 50 | 51 | describe('multi-backend', () => { 52 | const sh = fixtures.getServers({ backends: 2 }); 53 | 54 | it('should support round-robin balancing', (cb) => { 55 | request(sh, '/hello', (res, body) => { 56 | assert.equal(sh.backends[0].requests, 1); 57 | assert.equal(res.headers['x-backend-id'], 0); 58 | request(sh, '/hello', (res, body) => { 59 | assert.equal(sh.backends[1].requests, 1); 60 | assert.equal(res.headers['x-backend-id'], 1); 61 | request(sh, '/hello', (res, body) => { 62 | assert.equal(sh.backends[0].requests, 2); 63 | assert.equal(res.headers['x-backend-id'], 0); 64 | cb(); 65 | }); 66 | }); 67 | }); 68 | }); 69 | }); 70 | 71 | describe('cipher preference', () => { 72 | const sh = fixtures.getServers({ 73 | frontend: { 74 | server_preference: true, 75 | ciphers: 'AES256-SHA:AES256-SHA256:AES128-SHA' 76 | } 77 | }); 78 | 79 | it('should select server preferred cipher', (cb) => { 80 | const agent = new https.Agent({ 81 | ciphers: 'AES128-SHA:AES256-SHA', 82 | port: sh.frontend.port 83 | }); 84 | 85 | agentRequest(sh, agent, '/hello', (res, body, info) => { 86 | assert.equal(sh.backends[0].requests, 1); 87 | assert.equal(res.headers['x-backend-id'], 0); 88 | assert.equal(info.cipher.name, 'AES256-SHA'); 89 | cb(); 90 | }); 91 | }); 92 | 93 | it('should select server preferred cipher #2', (cb) => { 94 | const agent = new https.Agent({ 95 | ciphers: 'AES128-SHA:AES256-SHA256', 96 | port: sh.frontend.port 97 | }); 98 | 99 | agentRequest(sh, agent, '/hello', (res, body, info) => { 100 | assert.equal(sh.backends[0].requests, 1); 101 | assert.equal(res.headers['x-backend-id'], 0); 102 | assert.equal(info.cipher.name, 'AES256-SHA256'); 103 | cb(); 104 | }); 105 | }); 106 | }); 107 | 108 | describe('multi-frontend', () => { 109 | const sh = fixtures.getServers({ 110 | frontend: { 111 | interfaces: [ 112 | { port: fixtures.FRONT_PORT }, 113 | { port: fixtures.FRONT_PORT + 1 } 114 | ] 115 | } 116 | }); 117 | 118 | it('should be reachable on both interfaces', (cb) => { 119 | function fire(port, cb) { 120 | https.get('https://127.0.0.1:' + port, (res) => { 121 | res.resume(); 122 | res.once('end', cb); 123 | }); 124 | } 125 | 126 | fire(fixtures.FRONT_PORT, () => { 127 | fire(fixtures.FRONT_PORT + 1, cb); 128 | }); 129 | }); 130 | }); 131 | 132 | describe('EOF on frontend', () => { 133 | const sh = fixtures.getServers(); 134 | 135 | it('should support basic termination', (cb) => { 136 | const socket = net.connect(sh.frontend.port); 137 | socket.on('close', () => { 138 | cb(); 139 | }); 140 | socket.end(); 141 | }); 142 | }); 143 | }); 144 | -------------------------------------------------------------------------------- /include/bud/error.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_BUD_ERROR_H_ 2 | #define INCLUDE_BUD_ERROR_H_ 3 | 4 | #include /* FILE */ 5 | #include "bud/common.h" 6 | 7 | /* Forward declaration */ 8 | struct bud_config_s; 9 | 10 | typedef enum bud_error_code_e bud_error_code_t; 11 | typedef struct bud_error_s bud_error_t; 12 | 13 | enum bud_error_code_e { 14 | kBudOk = 0, 15 | 16 | /* General errors */ 17 | kBudErrNoMem = 0x001, 18 | kBudErrSkip = 0x002, 19 | 20 | /* Config errors */ 21 | kBudErrJSONParse = 0x100, 22 | kBudErrJSONNonObjectRoot = 0x101, 23 | kBudErrJSONNonObjectCtx = 0x102, 24 | kBudErrLoadCert = 0x103, 25 | kBudErrParseCert = 0x104, 26 | kBudErrLoadKey = 0x105, 27 | kBudErrParseKey = 0x106, 28 | kBudErrSNINotSupported = 0x107, 29 | kBudErrNonString = 0x108, 30 | kBudErrNPNNotSupported = 0x109, 31 | kBudErrExePath = 0x10a, 32 | kBudErrECDHNotFound = 0x10b, 33 | kBudErrNoBackend = 0x10c, 34 | kBudErrNoSSLIndex = 0x10d, 35 | kBudErrSmallTicketKey = 0x10e, 36 | kBudErrAddCert = 0x10f, 37 | kBudErrProxyline = 0x110, 38 | kBudErrInvalidUser = 0x111, 39 | kBudErrInvalidGroup = 0x112, 40 | kBudErrSetuid = 0x113, 41 | kBudErrSetgid = 0x114, 42 | kBudErrLoadDH = 0x115, 43 | kBudErrParseDH = 0x116, 44 | kBudErrInvalidBalance = 0x117, 45 | kBudErrDLOpen = 0x118, 46 | kBudErrDLSym = 0x119, 47 | kBudErrDLVersion = 0x11a, 48 | kBudErrMultipleConfigs = 0x11b, 49 | kBudErrLoadFile = 0x11c, 50 | kBudErrNoConfig = 0x11d, 51 | kBudErrFSRead = 0x11e, 52 | kBudErrRotateTimer = 0x11f, 53 | 54 | /* Master/Worker errors */ 55 | kBudErrForkFailed = 0x200, 56 | kBudErrSetsidFailed = 0x201, 57 | kBudErrChdirFailed = 0x202, 58 | /* x203 - x205, previously used by IPC */ 59 | kBudErrRestartTimer = 0x206, 60 | kBudErrSpawn = 0x207, 61 | kBudErrSignalInit = 0x208, 62 | kBudErrSignalStart = 0x209, 63 | 64 | /* Server errors */ 65 | kBudErrTcpServerInit = 0x300, 66 | kBudErrPton = 0x301, 67 | kBudErrNtop = 0x302, 68 | kBudErrTcpServerBind = 0x303, 69 | kBudErrServerListen = 0x304, 70 | kBudErrServerIPCAccept = 0x305, 71 | kBudErrServerSimAccept = 0x306, 72 | 73 | /* Client hello parser errors */ 74 | kBudErrParserNeedMore = 0x400, 75 | kBudErrParserErr = 0x401, 76 | 77 | /* HTTP pool */ 78 | kBudErrHttpTcpInit = 0x500, 79 | kBudErrHttpTcpConnect = 0x501, 80 | kBudErrHttpWrite = 0x502, 81 | kBudErrHttpWriteCb = 0x503, 82 | kBudErrHttpConnectCb = 0x504, 83 | kBudErrHttpReadStart = 0x505, 84 | kBudErrHttpReadCb = 0x506, 85 | kBudErrHttpParse = 0x507, 86 | kBudErrHttpEof = 0x508, 87 | 88 | /* Stapling */ 89 | kBudErrStaplingSetData = 0x600, 90 | 91 | /* Availability */ 92 | kBudErrMaxRetries = 0x700, 93 | kBudErrRetryTimerStart = 0x701, 94 | kBudErrRetryAfterClose = 0x702, 95 | 96 | /* Client */ 97 | kBudErrClientReadStart = 0x800, 98 | kBudErrClientReadStop = 0x801, 99 | kBudErrClientWrite = 0x802, 100 | kBudErrClientWriteCb = 0x803, 101 | kBudErrClientTryWrite = 0x804, 102 | kBudErrClientConnect = 0x805, 103 | kBudErrClientReadCb = 0x806, 104 | kBudErrClientWriteAppend = 0x807, 105 | kBudErrClientSetExData = 0x808, 106 | kBudErrClientSSLWrite = 0x809, 107 | kBudErrClientSSLRead = 0x80a, 108 | kBudErrClientThrottle = 0x80b, 109 | kBudErrClientShutdown = 0x80c, 110 | kBudErrClientShutdownCb = 0x80d, 111 | kBudErrClientRenegotiationAttack = 0x80e, 112 | kBudErrClientRetry = 0x80f, 113 | kBudErrClientProxyline = 0x810, 114 | kBudErrClientNoBackendInSNI = 0x811, 115 | kBudErrClientXForwardInsert = 0x812, 116 | kBudErrClientShutdownNoConn = 0x813, 117 | kBudErrClientSetSNICert = 0x814, 118 | 119 | /* IPC */ 120 | kBudErrIPCPipeInit = 0x900, 121 | kBudErrIPCPipeOpen = 0x901, 122 | kBudErrIPCReadStart = 0x902, 123 | kBudErrIPCBalanceInit = 0x903, 124 | kBudErrIPCBalanceAccept = 0x904, 125 | kBudErrIPCBalanceWrite = 0x905, 126 | kBudErrIPCSend = 0x906 127 | }; 128 | 129 | struct bud_error_s { 130 | bud_error_code_t code; 131 | union { 132 | const char* str; 133 | int ret; 134 | } data; 135 | }; 136 | 137 | #define bud_is_ok(err) ((err).code == kBudOk) 138 | #define bud_ok() ((bud_error_t) { .code = kBudOk, .data = { .ret = 0 } }) 139 | 140 | BUD_EXPORT bud_error_t bud_error(bud_error_code_t code); 141 | BUD_EXPORT bud_error_t bud_error_str(bud_error_code_t code, const char* str); 142 | BUD_EXPORT bud_error_t bud_error_dstr(bud_error_code_t code, const char* str); 143 | BUD_EXPORT bud_error_t bud_error_num(bud_error_code_t code, int ret); 144 | BUD_EXPORT void bud_error_log(struct bud_config_s* config, 145 | int level, 146 | bud_error_t err); 147 | BUD_EXPORT void bud_error_print(FILE* fp, bud_error_t err); 148 | BUD_EXPORT const char* bud_error_to_str(bud_error_t err); 149 | 150 | #endif /* SRC_ERROR_H_ */ 151 | -------------------------------------------------------------------------------- /src/config/tracing.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "parson.h" 4 | 5 | #include "src/config/tracing.h" 6 | #include "src/config.h" 7 | #include "src/common.h" 8 | 9 | #define BUD_CONFIG_INIT_TRACING(V) trace->V = NULL; 10 | 11 | #define BUD_CONFIG_ALLOC_TRACING(V) \ 12 | trace->V = calloc(trace->dso_count + 1, sizeof(*trace->V)); \ 13 | if (trace->V == NULL) \ 14 | goto fatal; \ 15 | 16 | #define BUD_CONFIG_FREE_TRACING(V) \ 17 | free(trace->V); \ 18 | trace->V = NULL; \ 19 | 20 | 21 | bud_error_t bud_config_load_tracing(bud_config_trace_t* trace, 22 | JSON_Object* obj) { 23 | BUD_TRACING_ENUM(BUD_CONFIG_INIT_TRACING) 24 | 25 | trace->dso_count = 0; 26 | trace->dso = NULL; 27 | trace->dso_array = NULL; 28 | 29 | if (obj == NULL) 30 | return bud_ok(); 31 | 32 | trace->dso_array = json_object_get_array(obj, "dso"); 33 | trace->dso_count = json_array_get_count(trace->dso_array); 34 | if (trace->dso_count == 0) 35 | goto done; 36 | 37 | trace->dso = calloc(trace->dso_count, sizeof(*trace->dso)); 38 | if (trace->dso == NULL) 39 | return bud_error_str(kBudErrNoMem, "trace dso"); 40 | 41 | BUD_TRACING_ENUM(BUD_CONFIG_ALLOC_TRACING) 42 | 43 | done: 44 | return bud_ok(); 45 | 46 | fatal: 47 | BUD_TRACING_ENUM(BUD_CONFIG_FREE_TRACING) 48 | free(trace->dso); 49 | trace->dso = NULL; 50 | 51 | return bud_error_str(kBudErrNoMem, "trace callbacks"); 52 | } 53 | 54 | 55 | #undef BUD_CONFIG_FREE_TRACING 56 | #undef BUD_CONFIG_ALLOC_TRACING 57 | #undef BUD_CONFIG_INIT_TRACING 58 | 59 | 60 | #define BUD_CONFIG_DECL_CLIENT_TRACING(V) bud_trace_cb_t* last_##V; 61 | #define BUD_CONFIG_DECL_BACKEND_TRACING(V) bud_trace_backend_cb_t* last_##V; 62 | #define BUD_CONFIG_DECL_CLOSE_TRACING(V) bud_trace_close_cb_t* last_##V; 63 | 64 | #define BUD_CONFIG_INIT_TRACING(V) last_##V = trace->V; 65 | 66 | #define BUD_CONFIG_COPY_TRACING(V) \ 67 | if (module->V != NULL) { \ 68 | *last_##V = module->V; \ 69 | last_##V++; \ 70 | } \ 71 | 72 | #define BUD_CONFIG_ZERO_TRACING(V) \ 73 | if (last_##V == trace->V) { \ 74 | free(trace->V); \ 75 | trace->V = NULL; \ 76 | } \ 77 | 78 | bud_error_t bud_config_init_tracing(bud_config_trace_t* trace) { 79 | int i; 80 | int r; 81 | bud_error_t err; 82 | BUD_TRACING_CLIENT_ENUM(BUD_CONFIG_DECL_CLIENT_TRACING) 83 | BUD_TRACING_BACKEND_ENUM(BUD_CONFIG_DECL_BACKEND_TRACING) 84 | BUD_TRACING_CLOSE_ENUM(BUD_CONFIG_DECL_CLOSE_TRACING) 85 | 86 | BUD_TRACING_ENUM(BUD_CONFIG_INIT_TRACING) 87 | 88 | for (i = 0; i < trace->dso_count; i++) { 89 | bud_trace_module_t* module; 90 | 91 | r = uv_dlopen(json_array_get_string(trace->dso_array, i), &trace->dso[i]); 92 | if (r != 0) { 93 | i--; 94 | err = bud_error_num(kBudErrDLOpen, r); 95 | goto fatal; 96 | } 97 | 98 | r = uv_dlsym(&trace->dso[i], "bud_trace_module", (void**) &module); 99 | if (r != 0) { 100 | err = bud_error_num(kBudErrDLSym, r); 101 | goto fatal; 102 | } 103 | 104 | /* Verify that version is correct */ 105 | if (module->version != BUD_TRACE_VERSION) { 106 | err = bud_error_num(kBudErrDLVersion, module->version); 107 | goto fatal; 108 | } 109 | 110 | BUD_TRACING_ENUM(BUD_CONFIG_COPY_TRACING) 111 | } 112 | 113 | BUD_TRACING_ENUM(BUD_CONFIG_ZERO_TRACING) 114 | 115 | return bud_ok(); 116 | 117 | fatal: 118 | /* Unload libraries */ 119 | for (; i >= 0; i--) 120 | uv_dlclose(&trace->dso[i]); 121 | 122 | /* Prevent us from unloading it again in trace_free */ 123 | trace->dso_count = 0; 124 | 125 | return err; 126 | } 127 | 128 | 129 | #undef BUD_CONFIG_ZERO_TRACING 130 | #undef BUD_CONFIG_COPY_TRACING 131 | #undef BUD_CONFIG_DECL_CLIENT_TRACING 132 | #undef BUD_CONFIG_DECL_BACKEND_TRACING 133 | #undef BUD_CONFIG_DECL_CLOSE_TRACING 134 | #undef BUD_CONFIG_INIT_TRACING 135 | 136 | 137 | void bud_config_trace_free(bud_config_trace_t* trace) { 138 | int i; 139 | 140 | for (i = 0; i < trace->dso_count; i++) 141 | uv_dlclose(&trace->dso[i]); 142 | 143 | free(trace->dso); 144 | trace->dso = NULL; 145 | } 146 | -------------------------------------------------------------------------------- /bud.gyp.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "openssl_fips%": "", 4 | "LIBOPENSSL": "<(PRODUCT_DIR)/libopenssl.a", 5 | "gypkg_deps": [ 6 | "git://github.com/libuv/libuv.git@^1.9.0 => uv.gyp:libuv", 7 | "git://github.com/nodejs/http-parser@^2.7.1 [gpg] => http_parser.gyp:http_parser", 8 | "git://github.com/gypkg/ringbuffer@^1.0.0 [gpg] => ringbuffer.gyp:ringbuffer", 9 | "git://github.com/gypkg/openssl@~1.2.10 [gpg] => openssl.gyp:openssl", 10 | ], 11 | }, 12 | 13 | "targets": [{ 14 | "target_name": "bud", 15 | "type": "executable", 16 | "dependencies": [ 17 | " 2 | * 3 | * Permission to use, copy, modify, and/or distribute this software for any 4 | * purpose with or without fee is hereby granted, provided that the above 5 | * copyright notice and this permission notice appear in all copies. 6 | * 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | */ 15 | 16 | #ifndef QUEUE_H_ 17 | #define QUEUE_H_ 18 | 19 | typedef void *QUEUE[2]; 20 | 21 | /* Private macros. */ 22 | #define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0])) 23 | #define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1])) 24 | #define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q))) 25 | #define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q))) 26 | 27 | /* Public macros. */ 28 | #define QUEUE_DATA(ptr, type, field) \ 29 | ((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field))) 30 | 31 | #define QUEUE_FOREACH(q, h) \ 32 | for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q)) 33 | 34 | #define QUEUE_EMPTY(q) \ 35 | ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q)) 36 | 37 | #define QUEUE_HEAD(q) \ 38 | (QUEUE_NEXT(q)) 39 | 40 | #define QUEUE_INIT(q) \ 41 | do { \ 42 | QUEUE_NEXT(q) = (q); \ 43 | QUEUE_PREV(q) = (q); \ 44 | } \ 45 | while (0) 46 | 47 | #define QUEUE_ADD(h, n) \ 48 | do { \ 49 | QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \ 50 | QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \ 51 | QUEUE_PREV(h) = QUEUE_PREV(n); \ 52 | QUEUE_PREV_NEXT(h) = (h); \ 53 | } \ 54 | while (0) 55 | 56 | #define QUEUE_SPLIT(h, q, n) \ 57 | do { \ 58 | QUEUE_PREV(n) = QUEUE_PREV(h); \ 59 | QUEUE_PREV_NEXT(n) = (n); \ 60 | QUEUE_NEXT(n) = (q); \ 61 | QUEUE_PREV(h) = QUEUE_PREV(q); \ 62 | QUEUE_PREV_NEXT(h) = (h); \ 63 | QUEUE_PREV(q) = (n); \ 64 | } \ 65 | while (0) 66 | 67 | #define QUEUE_INSERT_HEAD(h, q) \ 68 | do { \ 69 | QUEUE_NEXT(q) = QUEUE_NEXT(h); \ 70 | QUEUE_PREV(q) = (h); \ 71 | QUEUE_NEXT_PREV(q) = (q); \ 72 | QUEUE_NEXT(h) = (q); \ 73 | } \ 74 | while (0) 75 | 76 | #define QUEUE_INSERT_TAIL(h, q) \ 77 | do { \ 78 | QUEUE_NEXT(q) = (h); \ 79 | QUEUE_PREV(q) = QUEUE_PREV(h); \ 80 | QUEUE_PREV_NEXT(q) = (q); \ 81 | QUEUE_PREV(h) = (q); \ 82 | } \ 83 | while (0) 84 | 85 | #define QUEUE_REMOVE(q) \ 86 | do { \ 87 | QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \ 88 | QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \ 89 | } \ 90 | while (0) 91 | 92 | #endif /* QUEUE_H_ */ 93 | -------------------------------------------------------------------------------- /src/config/ticket.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "openssl/rand.h" 4 | 5 | #include "src/config/tracing.h" 6 | #include "src/config.h" 7 | #include "src/common.h" 8 | #include "src/logger.h" 9 | #include "src/master.h" /* bud_worker_t */ 10 | 11 | static bud_error_t bud_config_set_ticket_raw(bud_config_t* config, 12 | uint32_t index, 13 | uint32_t size, 14 | const char* data); 15 | 16 | 17 | bud_error_t bud_context_set_ticket(bud_context_t* context, 18 | const char* ticket, 19 | size_t size, 20 | bud_encoding_t enc) { 21 | bud_config_t* config; 22 | bud_context_t* root; 23 | size_t max_len; 24 | int i; 25 | 26 | config = context->config; 27 | root = &config->contexts[0]; 28 | 29 | if (enc == kBudEncodingRaw) { 30 | if (size != sizeof(context->ticket_key_storage)) 31 | return bud_error(kBudErrSmallTicketKey); 32 | 33 | memcpy(context->ticket_key_storage, ticket, size); 34 | } else { 35 | ASSERT(enc == kBudEncodingBase64, "Unexpected encoding of ticket key"); 36 | 37 | max_len = sizeof(context->ticket_key_storage); 38 | if (bud_base64_decode(context->ticket_key_storage, 39 | max_len, 40 | ticket, 41 | size) < max_len) { 42 | return bud_error(kBudErrSmallTicketKey); 43 | } 44 | } 45 | 46 | context->ticket_key_on = 1; 47 | if (context->ctx != NULL) { 48 | SSL_CTX_set_tlsext_ticket_keys(context->ctx, 49 | context->ticket_key_storage, 50 | sizeof(context->ticket_key_storage)); 51 | } 52 | 53 | if (context != root) 54 | return bud_ok(); 55 | 56 | /* Update ticket key in dependent contexts */ 57 | for (i = 0; i < config->context_count + 1; i++) { 58 | bud_context_t* cur; 59 | 60 | cur = &config->contexts[i]; 61 | if (cur->ticket_key_on || cur->ctx == NULL) 62 | continue; 63 | 64 | SSL_CTX_set_tlsext_ticket_keys(cur->ctx, 65 | cur->ticket_key_storage, 66 | sizeof(cur->ticket_key_storage)); 67 | } 68 | 69 | return bud_ok(); 70 | } 71 | 72 | 73 | bud_error_t bud_config_set_ticket(bud_config_t* config, bud_ipc_msg_t* msg) { 74 | uint32_t index; 75 | uint32_t size; 76 | const char* data; 77 | 78 | bud_ipc_parse_set_ticket(msg, &index, &data, &size); 79 | return bud_config_set_ticket_raw(config, index, size, data); 80 | } 81 | 82 | 83 | bud_error_t bud_config_set_ticket_raw(bud_config_t* config, 84 | uint32_t index, 85 | uint32_t size, 86 | const char* data) { 87 | bud_error_t err; 88 | int i; 89 | 90 | ASSERT(index < (uint32_t) config->context_count + 1, 91 | "ticket index overflow"); 92 | 93 | err = bud_context_set_ticket(&config->contexts[index], 94 | data, 95 | size, 96 | kBudEncodingRaw); 97 | if (!bud_is_ok(err)) 98 | return err; 99 | 100 | 101 | if (config->is_worker) { 102 | bud_clog(config, 103 | kBudLogInfo, 104 | "Worker updated ticket key for context: %d", 105 | index); 106 | return bud_ok(); 107 | } 108 | 109 | 110 | /* Retransmit */ 111 | for (i = 0; i < config->worker_count; i++) { 112 | bud_error_t worker_err; 113 | if (config->workers[i].state & kBudWorkerStateActive) { 114 | worker_err = bud_ipc_set_ticket(&config->workers[i].ipc, 115 | index, 116 | data, 117 | size); 118 | 119 | /* Send to everyone anyway */ 120 | if (!bud_is_ok(worker_err)) 121 | err = worker_err; 122 | } 123 | } 124 | 125 | if (bud_is_ok(err)) { 126 | bud_clog(config, 127 | kBudLogInfo, 128 | "Master retransmitted ticket key for context: %d", 129 | index); 130 | } 131 | 132 | return bud_ok(); 133 | } 134 | 135 | 136 | void bud_context_rotate_cb(uv_timer_t* timer) { 137 | bud_error_t err; 138 | bud_context_t* context; 139 | bud_config_t* config; 140 | int r; 141 | uint32_t index; 142 | 143 | context = (bud_context_t*) timer->data; 144 | config = context->config; 145 | 146 | /* No rotation in workers */ 147 | if (config->is_worker) 148 | return; 149 | 150 | r = RAND_bytes((unsigned char*) context->ticket_key_storage, 151 | sizeof(context->ticket_key_storage)); 152 | ASSERT(r == 1, "Failed to randomize new TLS Ticket Key"); 153 | 154 | index = context - config->contexts; 155 | 156 | err = bud_config_set_ticket_raw(config, 157 | index, 158 | sizeof(context->ticket_key_storage), 159 | context->ticket_key_storage); 160 | if (!bud_is_ok(err)) 161 | bud_error_log(config, kBudLogWarning, err); 162 | } 163 | -------------------------------------------------------------------------------- /src/xforward.c: -------------------------------------------------------------------------------- 1 | #include /* htonl */ 2 | #include 3 | 4 | #include "openssl/ssl.h" 5 | #include "uv.h" 6 | 7 | #include "src/xforward.h" 8 | #include "src/client.h" 9 | #include "src/client-common.h" 10 | #include "src/config.h" 11 | #include "src/logger.h" 12 | 13 | static size_t kSpdyXForwardMaxSkip = RING_BUFFER_LEN; 14 | static const int kSpdyXForwardFrameType = 0xf000; 15 | 16 | 17 | static bud_client_error_t bud_client_http_xforward(bud_client_t* client); 18 | static bud_client_error_t bud_client_spdy_xforward(bud_client_t* client, 19 | const char* protocol, 20 | unsigned int protocol_len); 21 | 22 | 23 | int bud_client_xforward_done(bud_client_t* client) { 24 | return client->xforward.crlf == 2; 25 | } 26 | 27 | 28 | void bud_client_xforward_skip(bud_client_t* client, size_t size) { 29 | if (client->xforward.skip >= size) 30 | client->xforward.skip -= size; 31 | else 32 | client->xforward.skip = 0; 33 | } 34 | 35 | 36 | bud_client_error_t bud_client_prepend_xforward(bud_client_t* client) { 37 | #ifdef OPENSSL_NPN_NEGOTIATED 38 | unsigned int proto_len; 39 | const char* protocol; 40 | 41 | proto_len = sizeof(protocol); 42 | SSL_get0_next_proto_negotiated(client->ssl, 43 | (const unsigned char**) &protocol, 44 | &proto_len); 45 | if (proto_len == 0) { 46 | SSL_get0_alpn_selected(client->ssl, 47 | (const unsigned char**) &protocol, 48 | &proto_len); 49 | } 50 | 51 | if (proto_len >= 5 && memcmp(protocol, "spdy/", 5) == 0) { 52 | return bud_client_spdy_xforward(client, protocol + 5, proto_len - 5); 53 | } 54 | #endif /* OPENSSL_NPN_NEGOTIATED */ 55 | 56 | /* No NPN or not SPDY */ 57 | return bud_client_http_xforward(client); 58 | } 59 | 60 | 61 | bud_client_error_t bud_client_http_xforward(bud_client_t* client) { 62 | char* out; 63 | size_t avail; 64 | size_t off; 65 | char xforward[256]; 66 | int r; 67 | 68 | out = ringbuffer_read_next(&client->backend.output, &avail); 69 | 70 | /* Not enough data yet */ 71 | if (avail <= client->xforward.skip) 72 | goto done; 73 | 74 | /* Find first CRLF */ 75 | for (off = client->xforward.skip; 76 | off < avail && client->xforward.crlf != 2; 77 | off++) { 78 | char cur; 79 | 80 | cur = out[off]; 81 | 82 | switch (cur) { 83 | case '\r': 84 | /* More of convenience than a functionality */ 85 | client->xforward.crlf = 1; 86 | continue; 87 | case '\n': 88 | /* Single line feed is enough */ 89 | client->xforward.crlf = 2; 90 | break; 91 | default: 92 | /* Reset on mismatch */ 93 | client->xforward.crlf = 0; 94 | continue; 95 | } 96 | } 97 | client->xforward.skip = off; 98 | if (client->xforward.skip >= kSpdyXForwardMaxSkip) { 99 | return bud_client_error(bud_error(kBudErrClientXForwardInsert), 100 | &client->backend); 101 | } 102 | 103 | if (!bud_client_xforward_done(client)) 104 | goto done; 105 | 106 | /* Format header */ 107 | r = snprintf(xforward, 108 | sizeof(xforward), 109 | "X-Forwarded-For: %.*s\r\nX-Forwarded-Proto: https\r\n", 110 | client->remote.host_len, 111 | client->remote.host); 112 | 113 | /* Shift data and insert xforward header */ 114 | r = ringbuffer_insert(&client->backend.output, 115 | client->xforward.skip, 116 | xforward, 117 | (size_t) r); 118 | if (r != 0) { 119 | return bud_client_error(bud_error(kBudErrClientXForwardInsert), 120 | &client->backend); 121 | } 122 | 123 | done: 124 | return bud_client_ok(&client->backend); 125 | } 126 | 127 | 128 | bud_client_error_t bud_client_spdy_xforward(bud_client_t* client, 129 | const char* protocol, 130 | unsigned int protocol_len) { 131 | int major; 132 | int minor; 133 | int r; 134 | unsigned char frame[256]; 135 | 136 | /* Detect protocol version */ 137 | major = -1; 138 | minor = 0; 139 | switch (protocol_len) { 140 | case 1: 141 | if (protocol[0] == '3') 142 | major = 3; 143 | else if (protocol[0] == '2') 144 | major = 2; 145 | break; 146 | case 3: 147 | if (strncmp(protocol, "3.1", protocol_len) == 0) { 148 | major = 3; 149 | minor = 1; 150 | } 151 | break; 152 | default: 153 | break; 154 | } 155 | 156 | /* We are done by now */ 157 | client->xforward.crlf = 2; 158 | 159 | if (major == -1) 160 | goto skip; 161 | 162 | assert(12 + client->remote.host_len <= sizeof(frame)); 163 | 164 | frame[0] = 0x80; 165 | frame[1] = major; 166 | *(uint16_t*) (frame + 2) = ntohs(kSpdyXForwardFrameType); 167 | 168 | /* Frame and Host lengths */ 169 | *(uint32_t*) (frame + 4) = htonl(4 + client->remote.host_len); 170 | *(uint32_t*) (frame + 8) = htonl(client->remote.host_len); 171 | 172 | /* Copy hostname */ 173 | memcpy(frame + 12, client->remote.host, client->remote.host_len); 174 | 175 | /* Prepend it to output data */ 176 | r = ringbuffer_insert(&client->backend.output, 177 | 0, 178 | (const char*) frame, 179 | (size_t) 12 + client->remote.host_len); 180 | if (r != 0) { 181 | return bud_client_error(bud_error(kBudErrClientXForwardInsert), 182 | &client->backend); 183 | } 184 | 185 | skip: 186 | return bud_client_ok(&client->backend); 187 | } 188 | -------------------------------------------------------------------------------- /src/logger.c: -------------------------------------------------------------------------------- 1 | #include /* calloc, free */ 2 | #include /* strcmp */ 3 | #ifndef _WIN32 4 | #include 5 | #endif /* !_WIN32 */ 6 | #include /* getpid */ 7 | 8 | #include "include/bud/logger.h" 9 | #include "src/error.h" 10 | #include "src/common.h" 11 | #include "src/config.h" 12 | #include "src/logger.h" 13 | 14 | static const char* bud_log_level_str(bud_log_level_t level); 15 | 16 | 17 | bud_logger_t* bud_logger_new(bud_config_t* config, bud_error_t* err) { 18 | bud_logger_t* logger; 19 | #ifndef _WIN32 20 | int facility; 21 | #endif /* !_WIN32 */ 22 | 23 | logger = calloc(1, sizeof(*logger)); 24 | if (logger == NULL) { 25 | *err = bud_error_str(kBudErrNoMem, "logger"); 26 | goto done; 27 | } 28 | 29 | if (strcmp(config->log.level, "debug") == 0) 30 | logger->level = kBudLogDebug; 31 | else if (strcmp(config->log.level, "notice") == 0) 32 | logger->level = kBudLogNotice; 33 | else if (strcmp(config->log.level, "fatal") == 0) 34 | logger->level = kBudLogFatal; 35 | else if (strcmp(config->log.level, "warning") == 0) 36 | logger->level = kBudLogWarning; 37 | else 38 | logger->level = kBudLogInfo; 39 | logger->stdio_enabled = config->log.stdio; 40 | logger->syslog_enabled = config->log.syslog; 41 | 42 | #ifndef _WIN32 43 | if (logger->syslog_enabled) { 44 | if (strcmp(config->log.facility, "auth") == 0) 45 | facility = LOG_AUTH; 46 | else if (strcmp(config->log.facility, "cron") == 0) 47 | facility = LOG_CRON; 48 | else if (strcmp(config->log.facility, "kern") == 0) 49 | facility = LOG_KERN; 50 | else if (strcmp(config->log.facility, "lpr") == 0) 51 | facility = LOG_LPR; 52 | else if (strcmp(config->log.facility, "mail") == 0) 53 | facility = LOG_MAIL; 54 | else if (strcmp(config->log.facility, "news") == 0) 55 | facility = LOG_NEWS; 56 | else if (strcmp(config->log.facility, "syslog") == 0) 57 | facility = LOG_SYSLOG; 58 | else if (strcmp(config->log.facility, "daemon") == 0) 59 | facility = LOG_DAEMON; 60 | else if (strcmp(config->log.facility, "uucp") == 0) 61 | facility = LOG_UUCP; 62 | else if (strcmp(config->log.facility, "local0") == 0) 63 | facility = LOG_LOCAL0; 64 | else if (strcmp(config->log.facility, "local1") == 0) 65 | facility = LOG_LOCAL1; 66 | else if (strcmp(config->log.facility, "local2") == 0) 67 | facility = LOG_LOCAL2; 68 | else if (strcmp(config->log.facility, "local3") == 0) 69 | facility = LOG_LOCAL3; 70 | else if (strcmp(config->log.facility, "local4") == 0) 71 | facility = LOG_LOCAL4; 72 | else if (strcmp(config->log.facility, "local5") == 0) 73 | facility = LOG_LOCAL5; 74 | else if (strcmp(config->log.facility, "local6") == 0) 75 | facility = LOG_LOCAL6; 76 | else if (strcmp(config->log.facility, "local7") == 0) 77 | facility = LOG_LOCAL7; 78 | else 79 | facility = LOG_USER; 80 | openlog("bud", LOG_PID | LOG_NDELAY, facility); 81 | } 82 | #endif /* !_WIN32 */ 83 | 84 | *err = bud_ok(); 85 | 86 | done: 87 | return logger; 88 | } 89 | 90 | 91 | void bud_logger_free(bud_logger_t* logger) { 92 | if (logger == NULL) 93 | return; 94 | 95 | #ifndef _WIN32 96 | if (logger->syslog_enabled) 97 | closelog(); 98 | #endif /* !_WIN32 */ 99 | free(logger); 100 | } 101 | 102 | 103 | void bud_logva(bud_logger_t* logger, 104 | bud_log_level_t level, 105 | const char* fmt, 106 | va_list ap) { 107 | va_list stdio_ap; 108 | va_list syslog_ap; 109 | 110 | ASSERT(logger != NULL, "Logger not initalized"); 111 | 112 | /* Ignore low-level logging */ 113 | if (logger->level > level) 114 | return; 115 | 116 | if (logger->stdio_enabled) { 117 | int r; 118 | static char buf[1024]; 119 | 120 | va_copy(stdio_ap, ap); 121 | r = vsnprintf(buf, sizeof(buf), fmt, stdio_ap); 122 | ASSERT(r < (int) sizeof(buf), "Log line overflow"); 123 | 124 | fprintf(stderr, 125 | "(%s) [%d] %s\n", 126 | bud_log_level_str(level), 127 | #ifndef _WIN32 128 | getpid(), 129 | #else 130 | 0, 131 | #endif /* !_WIN32 */ 132 | buf); 133 | va_end(stdio_ap); 134 | } 135 | 136 | #ifndef _WIN32 137 | if (logger->syslog_enabled) { 138 | int priority; 139 | 140 | va_copy(syslog_ap, ap); 141 | switch (level) { 142 | case kBudLogDebug: 143 | priority = LOG_DEBUG; 144 | break; 145 | case kBudLogNotice: 146 | priority = LOG_NOTICE; 147 | break; 148 | case kBudLogInfo: 149 | priority = LOG_INFO; 150 | break; 151 | case kBudLogFatal: 152 | priority = LOG_ERR; 153 | break; 154 | case kBudLogWarning: 155 | default: 156 | priority = LOG_WARNING; 157 | break; 158 | } 159 | vsyslog(priority, fmt, syslog_ap); 160 | va_end(syslog_ap); 161 | } 162 | #endif /* !_WIN32 */ 163 | } 164 | 165 | 166 | void bud_log(bud_logger_t* logger, 167 | bud_log_level_t level, 168 | const char* fmt, 169 | ...) { 170 | va_list ap; 171 | 172 | va_start(ap, fmt); 173 | bud_logva(logger, level, fmt, ap); 174 | va_end(ap); 175 | } 176 | 177 | 178 | const char* bud_log_level_str(bud_log_level_t level) { 179 | switch (level) { 180 | case kBudLogDebug: 181 | return "dbg"; 182 | case kBudLogNotice: 183 | return "ntc"; 184 | case kBudLogInfo: 185 | return "inf"; 186 | case kBudLogWarning: 187 | return "wrn"; 188 | case kBudLogFatal: 189 | return "ftl"; 190 | default: 191 | return "unk"; 192 | } 193 | } 194 | 195 | 196 | void bud_clog(bud_config_t* config, 197 | bud_log_level_t level, 198 | const char* fmt, 199 | ...) { 200 | va_list ap; 201 | 202 | va_start(ap, fmt); 203 | bud_clogva(config, level, fmt, ap); 204 | va_end(ap); 205 | } 206 | 207 | 208 | void bud_clogva(bud_config_t* config, 209 | bud_log_level_t level, 210 | const char* fmt, 211 | va_list ap) { 212 | bud_logva(config->logger, level, fmt, ap); 213 | } 214 | -------------------------------------------------------------------------------- /test/sni-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const fixtures = require('./fixtures'); 5 | const ocsp = require('ocsp'); 6 | const request = fixtures.request; 7 | const caRequest = fixtures.caRequest; 8 | const sniRequest = fixtures.sniRequest; 9 | const spdyRequest = fixtures.spdyRequest; 10 | const agentRequest = fixtures.agentRequest; 11 | 12 | describe('Bud TLS Terminator/SNI', () => { 13 | describe('multi-backend', () => { 14 | const sh = fixtures.getServers({ 15 | balance: 'sni', 16 | contexts: [{ 17 | servername: 'local.host', 18 | backends: 2 19 | }] 20 | }); 21 | 22 | it('should support round-robin balancing', (cb) => { 23 | const ctx = sh.contexts[0]; 24 | let count = 20; 25 | const stats = []; 26 | function fire(cb) { 27 | if (--count === 0) 28 | return cb(); 29 | 30 | sniRequest(sh, 'local.host', '/hello', (res) => { 31 | stats.push({ 32 | backends: ctx.backends.map((back) => { 33 | return back.requests; 34 | }), 35 | id: res.headers['x-backend-id'] | 0 36 | }); 37 | fire(cb); 38 | }); 39 | } 40 | 41 | fire(() => { 42 | const check = ctx.backends.map(() => { 43 | return 0; 44 | }); 45 | 46 | for (let i = 0; i < stats.length; i++) { 47 | check[stats[i].id]++; 48 | assert.deepEqual(check, stats[i].backends); 49 | } 50 | 51 | cb(); 52 | }); 53 | }); 54 | }); 55 | 56 | [ true, false ].forEach((keepalive) => { 57 | describe('async sni with keepalive=' + keepalive, () => { 58 | const sh = fixtures.getServers({ 59 | sni: { 60 | enabled: true, 61 | port: 9000 62 | } 63 | }); 64 | 65 | let sniBackend; 66 | beforeEach((cb) => { 67 | sniBackend = fixtures.sniBackend({ 68 | keepalive: keepalive 69 | }).listen(9000, cb); 70 | }); 71 | 72 | afterEach((cb) => { 73 | sniBackend.close(cb); 74 | }); 75 | 76 | it('should asynchronously fetch cert', (cb) => { 77 | sniRequest(sh, 'local.host', '/hello', (res, chunks, info) => { 78 | assert.equal(sniBackend.misses, 1); 79 | assert.equal(sniBackend.hits, 0); 80 | assert.equal(info.cert.serialNumber, '82F2A828A42C1728'); 81 | assert.notEqual(info.cipher.name, 'AES128-SHA'); 82 | 83 | sniRequest(sh, 'sni.host', '/hello', (res, chunks, info) => { 84 | assert.equal(sniBackend.misses, 1); 85 | assert.equal(sniBackend.hits, 1); 86 | assert.equal(info.cert.serialNumber, '2B'); 87 | assert.equal(info.cipher.name, 'AES128-SHA'); 88 | cb(); 89 | }); 90 | }); 91 | }); 92 | 93 | it('should survive stress test', (cb) => { 94 | function stress(count, cb) { 95 | function fire(cb) { 96 | if (--count === 0) 97 | return cb(); 98 | 99 | sniRequest(sh, 'local.host', '/hello', () => { 100 | fire(cb); 101 | }); 102 | } 103 | 104 | fire(cb); 105 | } 106 | 107 | let waiting = 10; 108 | for (let i = 0; i < waiting; i++) 109 | stress(10, done); 110 | 111 | function done() { 112 | if (--waiting === 0) 113 | cb(); 114 | } 115 | }); 116 | }); 117 | }); 118 | 119 | describe('async sni with invalid JSON', () => { 120 | const sh = fixtures.getServers({ 121 | log: { level: 'debug' }, 122 | sni: { 123 | enabled: true, 124 | port: 9000 125 | } 126 | }); 127 | 128 | let sniBackend; 129 | beforeEach((cb) => { 130 | sniBackend = fixtures.sniBackend().listen(9000, cb); 131 | }); 132 | 133 | afterEach((cb) => { 134 | sniBackend.close(cb); 135 | }); 136 | 137 | it('should not crash', (cb) => { 138 | sniRequest(sh, 'empty.json', '/hello', (err) => { 139 | assert(err instanceof Error); 140 | assert.equal(sniBackend.hits, 1); 141 | cb(); 142 | }); 143 | }); 144 | }); 145 | 146 | describe('async sni+ocsp', () => { 147 | const sh = fixtures.getServers({ 148 | log: { level: 'debug' }, 149 | sni: { 150 | enabled: true, 151 | port: 9000 152 | }, 153 | stapling: { 154 | enabled: true, 155 | port: 9001 156 | } 157 | }); 158 | 159 | let sniBackend; 160 | let ocspBackend; 161 | beforeEach((cb) => { 162 | sniBackend = fixtures.sniBackend().listen(9000, () => { 163 | ocspBackend = fixtures.ocspBackend().listen(9001, cb); 164 | }); 165 | }); 166 | 167 | afterEach((cb) => { 168 | sniBackend.close(() => { 169 | ocspBackend.close(cb); 170 | }); 171 | }); 172 | 173 | it('should asynchronously fetch cert', (cb) => { 174 | const agent = new ocsp.Agent({ 175 | port: sh.frontend.port, 176 | servername: 'sni.host' 177 | }); 178 | 179 | // Nasty hack for node.js v0.12 180 | const createConn = agent.createConnection; 181 | agent.createConnection = function createConnection(options) { 182 | options.servername = 'sni.host'; 183 | return createConn.call(this, options); 184 | }; 185 | 186 | agentRequest(sh, agent, '/hello', (res, chunks, info) => { 187 | assert.equal(sniBackend.misses, 0); 188 | assert.equal(sniBackend.hits, 1); 189 | assert.equal(ocspBackend.cacheHits, 0); 190 | assert.equal(ocspBackend.cacheMisses, 1); 191 | 192 | assert.equal(info.cert.serialNumber, '2B'); 193 | assert.equal(info.cipher.name, 'AES128-SHA'); 194 | cb(); 195 | }); 196 | }); 197 | }); 198 | 199 | describe('sync sni+ocsp', () => { 200 | const sh = fixtures.getServers({ 201 | contexts: [{ 202 | servername: 'local.host', 203 | cert: fixtures.goodCert + '\n' + fixtures.issuerCert, 204 | key: fixtures.goodKey 205 | }], 206 | stapling: { 207 | enabled: true, 208 | port: 9001 209 | } 210 | }); 211 | 212 | let ocspBackend; 213 | beforeEach((cb) => { 214 | ocspBackend = fixtures.ocspBackend().listen(9001, cb); 215 | }); 216 | 217 | afterEach((cb) => { 218 | ocspBackend.close(cb); 219 | }); 220 | 221 | it('should still provide stapling response', (cb) => { 222 | const agent = new ocsp.Agent({ 223 | port: sh.frontend.port, 224 | servername: 'local.host' 225 | }); 226 | 227 | // Nasty hack for node.js v0.12 228 | const createConn = agent.createConnection; 229 | agent.createConnection = function createConnection(options) { 230 | options.servername = 'local.host'; 231 | return createConn.call(this, options); 232 | }; 233 | 234 | agentRequest(sh, agent, '/hello', (res, chunks, info) => { 235 | assert.equal(ocspBackend.cacheHits, 0); 236 | assert.equal(ocspBackend.cacheMisses, 1); 237 | 238 | assert.equal(info.cert.serialNumber, '2B'); 239 | cb(); 240 | }); 241 | }); 242 | }); 243 | }); 244 | -------------------------------------------------------------------------------- /src/ocsp.c: -------------------------------------------------------------------------------- 1 | #include /* NULL */ 2 | #include /* strlen */ 3 | 4 | #include "openssl/ocsp.h" 5 | #include "openssl/ssl.h" 6 | #include "parson.h" 7 | 8 | #include "src/ocsp.h" 9 | #include "src/client.h" 10 | #include "src/client-common.h" 11 | #include "src/common.h" 12 | #include "src/config.h" 13 | #include "src/error.h" 14 | #include "src/http-pool.h" 15 | 16 | static void bud_client_stapling_cache_req_cb(bud_http_request_t* req, 17 | bud_error_t err); 18 | static void bud_client_stapling_req_cb(bud_http_request_t* req, 19 | bud_error_t err); 20 | static int bud_client_staple_json(bud_client_t* client, JSON_Value* json); 21 | 22 | bud_error_t bud_client_ocsp_stapling(bud_client_t* client) { 23 | bud_config_t* config; 24 | bud_context_t* context; 25 | bud_error_t err; 26 | const char* id; 27 | size_t id_size; 28 | bud_context_pkey_type_t type; 29 | 30 | config = client->config; 31 | 32 | if (client->sni_ctx.ctx != NULL) { 33 | /* Async SNI success */ 34 | context = &client->sni_ctx; 35 | } else if (client->hello.servername_len != 0) { 36 | /* Matching context */ 37 | context = bud_config_select_context(config, 38 | client->hello.servername, 39 | client->hello.servername_len); 40 | } else { 41 | /* Default context */ 42 | context = &config->contexts[0]; 43 | } 44 | 45 | type = bud_context_select_pkey(context, client->ssl); 46 | client->stapling_type = type; 47 | 48 | /* Cache context to prevent second search in OpenSSL's callback */ 49 | if (!SSL_set_ex_data(client->ssl, kBudSSLSNIIndex, context)) { 50 | err = bud_error(kBudErrStaplingSetData); 51 | goto fatal; 52 | } 53 | 54 | id = bud_context_get_ocsp_id(context, type, &id_size); 55 | 56 | /* Certificate has no OCSP id */ 57 | if (id == NULL) { 58 | DBG_LN(&client->backend, "stapling id missing"); 59 | return bud_ok(); 60 | } 61 | 62 | /* Request backend for cached respose first */ 63 | client->stapling_cache_req = bud_http_get(config->stapling.pool, 64 | config->stapling.url, 65 | id, 66 | id_size, 67 | bud_client_stapling_cache_req_cb, 68 | &err); 69 | 70 | if (!bud_is_ok(err)) 71 | goto fatal; 72 | 73 | client->stapling_cache_req->data = client; 74 | client->async_hello = kBudProgressRunning; 75 | return bud_ok(); 76 | 77 | fatal: 78 | return err; 79 | } 80 | 81 | 82 | void bud_client_stapling_cache_req_cb(bud_http_request_t* req, 83 | bud_error_t err) { 84 | bud_client_t* client; 85 | bud_client_error_t cerr; 86 | bud_config_t* config; 87 | bud_context_t* context; 88 | const char* id; 89 | size_t id_size; 90 | const char* url; 91 | size_t url_size; 92 | char* ocsp; 93 | size_t ocsp_size; 94 | char* json; 95 | size_t json_size; 96 | size_t offset; 97 | bud_context_pkey_type_t type; 98 | 99 | client = req->data; 100 | config = client->config; 101 | type = client->stapling_type; 102 | context = SSL_get_ex_data(client->ssl, kBudSSLSNIIndex); 103 | 104 | client->async_hello = kBudProgressDone; 105 | client->stapling_cache_req = NULL; 106 | json = NULL; 107 | ocsp = NULL; 108 | 109 | ASSERT(context != NULL, "Context disappeared"); 110 | 111 | if (!bud_is_ok(err)) { 112 | WARNING(&client->frontend, 113 | "OCSP cache cb failed: \"%s\"", 114 | bud_error_to_str(err)); 115 | goto done; 116 | } 117 | 118 | /* Cache hit, success */ 119 | if ((req->code >= 200 && req->code < 400) && 120 | bud_client_staple_json(client, req->response) == 0) { 121 | DBG_LN(&client->frontend, "stapling cache hit"); 122 | goto done; 123 | } 124 | 125 | DBG_LN(&client->frontend, "stapling cache miss"); 126 | id = bud_context_get_ocsp_id(context, type, &id_size); 127 | url = bud_context_get_ocsp_req(context, type, &url_size, &ocsp, &ocsp_size); 128 | 129 | /* Certificate has no OCSP url */ 130 | if (url == NULL) 131 | goto done; 132 | 133 | /* Format JSON request */ 134 | json_size = 2 + bud_base64_encoded_size(ocsp_size) + 2 + url_size; 135 | json_size += /* "ocsp": */ 7 + /* "url": */ 6 + /* {,}\0 */ 4; 136 | json = malloc(json_size); 137 | if (json == NULL) 138 | goto done; 139 | 140 | offset = snprintf(json, 141 | json_size, 142 | "{\"url\":\"%.*s\",\"ocsp\":\"", 143 | (int) url_size, 144 | url); 145 | bud_base64_encode(ocsp, ocsp_size, json + offset, json_size - offset); 146 | offset += bud_base64_encoded_size(ocsp_size); 147 | snprintf(json + offset, json_size - offset, "\"}"); 148 | 149 | /* Request OCSP response */ 150 | client->stapling_req = bud_http_post(config->stapling.pool, 151 | config->stapling.url, 152 | id, 153 | id_size, 154 | json, 155 | json_size - 1, 156 | bud_client_stapling_req_cb, 157 | &err); 158 | 159 | if (!bud_is_ok(err)) 160 | goto done; 161 | 162 | client->stapling_req->data = client; 163 | client->async_hello = kBudProgressRunning; 164 | 165 | done: 166 | free(ocsp); 167 | free(json); 168 | json_value_free(req->response); 169 | cerr = bud_client_cycle(client); 170 | if (!bud_is_ok(cerr.err)) 171 | return bud_client_close(client, cerr); 172 | } 173 | 174 | 175 | void bud_client_stapling_req_cb(bud_http_request_t* req, bud_error_t err) { 176 | bud_client_t* client; 177 | bud_client_error_t cerr; 178 | 179 | client = req->data; 180 | client->stapling_req = NULL; 181 | client->async_hello = kBudProgressDone; 182 | 183 | if (!bud_is_ok(err)) { 184 | WARNING(&client->frontend, 185 | "OCSP cb failed: \"%s\"", 186 | bud_error_to_str(err)); 187 | goto done; 188 | } 189 | 190 | /* Stapling backend failure - ignore */ 191 | if (req->code < 200 || req->code >= 400) { 192 | DBG_LN(&client->frontend, "stapling request failure"); 193 | goto done; 194 | } 195 | DBG_LN(&client->frontend, "stapling request success"); 196 | 197 | /* Note, ignoring return value here */ 198 | (void) bud_client_staple_json(client, req->response); 199 | 200 | /* NOTE: Stapling failure should not prevent us from responding */ 201 | done: 202 | json_value_free(req->response); 203 | cerr = bud_client_cycle(client); 204 | if (!bud_is_ok(cerr.err)) 205 | return bud_client_close(client, cerr); 206 | } 207 | 208 | 209 | int bud_client_staple_json(bud_client_t* client, JSON_Value* json) { 210 | JSON_Object* obj; 211 | const char* b64_body; 212 | size_t b64_body_len; 213 | char* body; 214 | const unsigned char* pbody; 215 | size_t body_len; 216 | OCSP_RESPONSE* resp; 217 | int status; 218 | int r; 219 | 220 | r = -1; 221 | body = NULL; 222 | 223 | obj = json_value_get_object(json); 224 | b64_body = json_object_get_string(obj, "response"); 225 | if (b64_body == NULL) 226 | goto done; 227 | 228 | b64_body_len = strlen(b64_body); 229 | body_len = bud_base64_decoded_size_fast(b64_body_len); 230 | body = malloc(body_len); 231 | if (body == NULL) 232 | goto done; 233 | 234 | body_len = bud_base64_decode(body, body_len, b64_body, b64_body_len); 235 | pbody = (const unsigned char*) body; 236 | resp = d2i_OCSP_RESPONSE(NULL, &pbody, body_len); 237 | if (resp == NULL) 238 | goto done; 239 | 240 | /* Not successful response, do not waste bandwidth on it */ 241 | status = OCSP_response_status(resp); 242 | OCSP_RESPONSE_free(resp); 243 | if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) 244 | goto done; 245 | 246 | /* Set stapling! */ 247 | client->stapling_ocsp_resp = body; 248 | client->stapling_ocsp_resp_len = body_len; 249 | body = NULL; 250 | r = 0; 251 | 252 | done: 253 | free(body); 254 | return r; 255 | } 256 | 257 | 258 | int bud_client_stapling_cb(SSL* ssl, void* arg) { 259 | bud_client_t* client; 260 | 261 | client = SSL_get_ex_data(ssl, kBudSSLClientIndex); 262 | if (client == NULL || client->stapling_ocsp_resp == NULL) 263 | return SSL_TLSEXT_ERR_NOACK; 264 | 265 | SSL_set_tlsext_status_ocsp_resp(ssl, 266 | client->stapling_ocsp_resp, 267 | client->stapling_ocsp_resp_len); 268 | client->stapling_ocsp_resp = NULL; 269 | return SSL_TLSEXT_ERR_OK; 270 | } 271 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_CONFIG_H_ 2 | #define SRC_CONFIG_H_ 3 | 4 | #include 5 | 6 | #include "uv.h" 7 | #include "openssl/bio.h" 8 | #include "openssl/ocsp.h" 9 | #include "openssl/ssl.h" 10 | #include "openssl/x509.h" 11 | #include "parson.h" 12 | 13 | #include "include/bud/tracing.h" 14 | #include "src/common.h" 15 | #include "src/error.h" 16 | #include "src/ipc.h" 17 | 18 | /* Forward declarations */ 19 | struct bud_server_s; 20 | struct bud_worker_s; 21 | struct bud_logger_s; 22 | struct bud_http_pool_s; 23 | 24 | typedef struct bud_context_s bud_context_t; 25 | typedef struct bud_config_http_pool_s bud_config_http_pool_t; 26 | typedef enum bud_config_balance_e bud_config_balance_t; 27 | typedef enum bud_context_pkey_type_e bud_context_pkey_type_t; 28 | typedef struct bud_context_pem_s bud_context_pem_t; 29 | typedef struct bud_config_trace_s bud_config_trace_t; 30 | typedef struct bud_config_s bud_config_t; 31 | typedef struct bud_config_addr_s bud_config_addr_t; 32 | typedef enum bud_config_proxyline_s bud_config_proxyline_t; 33 | typedef struct bud_config_backend_s bud_config_backend_t; 34 | typedef struct bud_config_backend_list_s bud_config_backend_list_t; 35 | typedef struct bud_config_frontend_s bud_config_frontend_t; 36 | typedef struct bud_config_frontend_interface_s bud_config_frontend_interface_t; 37 | 38 | int kBudSSLConfigIndex; 39 | int kBudSSLClientIndex; 40 | int kBudSSLSNIIndex; 41 | int kBudSSLTicketKeyIndex; 42 | const char* kPipedConfigPath; 43 | 44 | struct bud_config_http_pool_s { 45 | int enabled; 46 | 47 | uint16_t port; 48 | const char* host; 49 | const char* url; 50 | 51 | /* internal */ 52 | struct bud_http_pool_s* pool; 53 | }; 54 | 55 | 56 | #define BUD_CONFIG_ADDR_FIELDS \ 57 | uint16_t port; \ 58 | const char* host; \ 59 | int keepalive; \ 60 | /* internal */ \ 61 | struct sockaddr_storage addr; 62 | 63 | 64 | 65 | struct bud_config_addr_s { 66 | BUD_CONFIG_ADDR_FIELDS 67 | }; 68 | 69 | struct bud_config_frontend_interface_s { 70 | bud_config_addr_t* list; 71 | int count; 72 | }; 73 | 74 | struct bud_config_frontend_s { 75 | /* Inheritance */ 76 | BUD_CONFIG_ADDR_FIELDS 77 | 78 | /* Public */ 79 | bud_config_frontend_interface_t interface; 80 | 81 | const char* security; 82 | int reneg_window; 83 | int reneg_limit; 84 | int ssl3; 85 | int max_send_fragment; 86 | int allow_half_open; 87 | 88 | /* Internal */ 89 | const SSL_METHOD* method; 90 | }; 91 | 92 | enum bud_config_proxyline_s { 93 | kBudProxylineNone, 94 | kBudProxylineHAProxy, 95 | kBudProxylineJSON 96 | }; 97 | 98 | struct bud_config_backend_s { 99 | /* Inheritance */ 100 | BUD_CONFIG_ADDR_FIELDS 101 | 102 | /* Public */ 103 | bud_config_proxyline_t proxyline; 104 | int xforward; 105 | 106 | /* Internal */ 107 | bud_config_t* config; 108 | int dead; 109 | uint64_t last_checked; 110 | uint64_t dead_since; 111 | uv_timer_t* revive_timer; 112 | }; 113 | 114 | struct bud_config_backend_list_s { 115 | bud_config_backend_t* list; 116 | int count; 117 | int last; 118 | 119 | /* Map by ip */ 120 | bud_hashmap_t external_map; 121 | unsigned int external_count; 122 | }; 123 | 124 | enum bud_config_balance_e { 125 | kBudBalanceRoundRobin, 126 | kBudBalanceSNI, 127 | kBudBalanceOnFail, 128 | kBudBalanceExternal 129 | }; 130 | 131 | enum bud_context_pkey_type_e { 132 | kBudContextPKeyRSA = 0x0, 133 | kBudContextPKeyECC = 0x1, 134 | kBudContextPKeyEnd = 0x2 135 | }; 136 | 137 | struct bud_context_pem_s { 138 | X509* cert; 139 | X509* issuer; 140 | OCSP_CERTID* ocsp_id; 141 | char* ocsp_der_id; 142 | size_t ocsp_der_id_len; 143 | const char* ocsp_url; 144 | size_t ocsp_url_len; 145 | }; 146 | 147 | #undef BUD_CONFIG_ADDR_FIELDS 148 | 149 | struct bud_context_s { 150 | bud_config_t* config; 151 | 152 | /* From config file */ 153 | const char* servername; 154 | size_t servername_len; 155 | bud_config_backend_list_t backend; 156 | 157 | const char* cert_file; 158 | const JSON_Array* cert_files; 159 | const char* key_file; 160 | const char* key_pass; 161 | const JSON_Array* key_files; 162 | const JSON_Array* key_passes; 163 | const JSON_Array* npn; 164 | const char* ciphers; 165 | const char* ecdh; 166 | const char* dh_file; 167 | const char* ticket_key; 168 | 169 | int ticket_timeout; 170 | int ticket_rotate; 171 | 172 | unsigned int ticket_key_on:1; 173 | unsigned int request_cert:1; 174 | unsigned int optional_cert:1; 175 | int server_preference; 176 | 177 | const char* ca_file; 178 | const JSON_Array* ca_array; 179 | 180 | /* Could be either `on-fail` or false */ 181 | const char* balance; 182 | 183 | /* Various */ 184 | SSL_CTX* ctx; 185 | bud_context_pem_t pem[kBudContextPKeyEnd]; 186 | X509_STORE* ca_store; 187 | DH* dh; 188 | char ticket_key_storage[48]; 189 | char* npn_line; 190 | size_t npn_line_len; 191 | bud_config_balance_t balance_e; 192 | uv_timer_t* rotate_timer; 193 | }; 194 | 195 | #define BUD_CONFIG_TRACE_CLIENT_DECL(V) bud_trace_cb_t* V; 196 | #define BUD_CONFIG_TRACE_BACKEND_DECL(V) bud_trace_backend_cb_t* V; 197 | #define BUD_CONFIG_TRACE_CLOSE_DECL(V) bud_trace_close_cb_t* V; 198 | 199 | struct bud_config_trace_s { 200 | /* DSO hooks for tracing */ 201 | BUD_TRACING_CLIENT_ENUM(BUD_CONFIG_TRACE_CLIENT_DECL) 202 | BUD_TRACING_BACKEND_ENUM(BUD_CONFIG_TRACE_BACKEND_DECL) 203 | BUD_TRACING_CLOSE_ENUM(BUD_CONFIG_TRACE_CLOSE_DECL) 204 | 205 | JSON_Array* dso_array; 206 | uv_lib_t* dso; 207 | int dso_count; 208 | }; 209 | 210 | #undef BUD_CONFIG_TRACE_CLIENT_DECL 211 | #undef BUD_CONFIG_TRACE_BACKEND_DECL 212 | #undef BUD_CONFIG_TRACE_CLOSE_DECL 213 | 214 | struct bud_config_s { 215 | /* Internal, just to keep stuff allocated */ 216 | JSON_Value* json; 217 | 218 | /* Just internal things */ 219 | uv_loop_t* loop; 220 | int argc; 221 | char** argv; 222 | char exepath[1024]; 223 | struct bud_server_s* server; 224 | struct bud_logger_s* logger; 225 | uint64_t client_id; 226 | 227 | /* 228 | * Map with a contents of every loaded file. 229 | * Primary used for syncing the files between master and workers 230 | */ 231 | struct { 232 | bud_hashmap_t hashmap; 233 | char* str; 234 | size_t len; 235 | } files; 236 | 237 | /* Master state */ 238 | struct { 239 | uv_signal_t* sigterm; 240 | uv_signal_t* sigint; 241 | 242 | /* NOTE: shared with worker */ 243 | uv_signal_t* sighup; 244 | } signal; 245 | struct bud_worker_s* workers; 246 | int last_worker; 247 | 248 | /* Worker state, and master control IPC */ 249 | bud_ipc_t ipc; 250 | 251 | /* Used by client.c */ 252 | struct { 253 | char haproxy[256]; 254 | char json[256]; 255 | } proxyline_fmt; 256 | bud_config_balance_t balance_e; 257 | 258 | /* Options from config file */ 259 | int piped_index; 260 | unsigned int piped:1; 261 | unsigned int inlined:1; 262 | const char* path; 263 | 264 | int worker_count; 265 | int restart_timeout; 266 | int is_daemon; 267 | int is_worker; 268 | int master_ipc; 269 | struct { 270 | const char* level; 271 | const char* facility; 272 | int stdio; 273 | int syslog; 274 | } log; 275 | 276 | struct { 277 | int death_timeout; 278 | int revive_interval; 279 | int retry_interval; 280 | int max_retries; 281 | } availability; 282 | 283 | bud_config_frontend_t frontend; 284 | const char* balance; 285 | 286 | const char* user; 287 | const char* group; 288 | 289 | bud_config_http_pool_t sni; 290 | bud_config_http_pool_t stapling; 291 | 292 | int context_count; 293 | bud_context_t* contexts; 294 | 295 | bud_config_trace_t trace; 296 | }; 297 | 298 | bud_error_t bud_config_new(int argc, char** argv, bud_config_t** out); 299 | bud_error_t bud_config_load(bud_config_t* config); 300 | void bud_config_free(bud_config_t* config); 301 | 302 | /* Getting/Setting file cache */ 303 | bud_error_t bud_config_get_files(bud_config_t* config, 304 | const char** files, 305 | size_t* size); 306 | bud_error_t bud_config_set_files(bud_config_t* config, 307 | const char* files, 308 | size_t size); 309 | bud_error_t bud_config_reload_files(bud_config_t* config); 310 | 311 | /* Helper for loading SNI */ 312 | bud_error_t bud_context_load(JSON_Object* obj, 313 | bud_context_t* ctx); 314 | bud_error_t bud_context_init(bud_config_t* config, 315 | bud_context_t* context); 316 | void bud_context_free(bud_context_t* context); 317 | 318 | bud_error_t bud_config_load_backend_list(bud_config_t* config, 319 | JSON_Object* obj, 320 | bud_config_backend_list_t* backend); 321 | 322 | /* Helper for stapling */ 323 | bud_context_t* bud_config_select_context(bud_config_t* config, 324 | const char* servername, 325 | size_t servername_len); 326 | const char* bud_context_get_ocsp_id(bud_context_t* context, 327 | bud_context_pkey_type_t type, 328 | size_t* size); 329 | const char* bud_context_get_ocsp_req(bud_context_t* context, 330 | bud_context_pkey_type_t type, 331 | size_t* size, 332 | char** ocsp_request, 333 | size_t* ocsp_request_len); 334 | 335 | /* Helper for http-pool.c */ 336 | int bud_config_str_to_addr(const char* host, 337 | uint16_t port, 338 | struct sockaddr_storage* addr); 339 | 340 | bud_error_t bud_config_drop_privileges(bud_config_t* config); 341 | 342 | /* Helper for tracing */ 343 | const char* bud_config_balance_to_str(bud_config_balance_t balance); 344 | 345 | /* Helper for client */ 346 | uint64_t bud_config_get_client_id(bud_config_t* config); 347 | bud_context_pkey_type_t bud_config_pkey_type(EVP_PKEY* pkey); 348 | bud_context_pkey_type_t bud_context_select_pkey(bud_context_t* context, SSL* s); 349 | 350 | /* IPC helpers */ 351 | bud_error_t bud_config_set_ticket(bud_config_t* config, bud_ipc_msg_t* msg); 352 | 353 | #endif /* SRC_CONFIG_H_ */ 354 | -------------------------------------------------------------------------------- /src/avail.c: -------------------------------------------------------------------------------- 1 | #include "uv.h" 2 | 3 | #include "src/avail.h" 4 | #include "src/client.h" 5 | #include "src/client-common.h" 6 | #include "src/common.h" 7 | #include "src/config.h" 8 | #include "src/logger.h" 9 | #include "src/tracing.h" 10 | 11 | static void bud_kill_backend(bud_client_t* client, 12 | bud_config_backend_t* backend); 13 | static void bud_revive_backend(uv_timer_t* timer); 14 | 15 | bud_config_backend_t* bud_select_backend(bud_client_t* client) { 16 | bud_config_t* config; 17 | bud_config_balance_t balance; 18 | bud_config_backend_list_t* backend; 19 | bud_config_backend_t* res; 20 | int first; 21 | uint64_t now; 22 | uint64_t death_timeout; 23 | 24 | config = client->config; 25 | balance = client->balance; 26 | backend = client->backend_list; 27 | 28 | /* External balancing if any of backends has `external` field */ 29 | if (backend->external_count != 0) { 30 | char key[1024]; 31 | unsigned int key_len; 32 | 33 | /* Lookup in external map */ 34 | key_len = snprintf(key, 35 | sizeof(key), 36 | "[%.*s]:%d", 37 | client->local.host_len, 38 | client->local.host, 39 | config->frontend.port); 40 | res = bud_hashmap_get(&backend->external_map, key, key_len); 41 | if (res != NULL) { 42 | backend->last = res - backend->list; 43 | 44 | /* Info for tracing */ 45 | client->balance = kBudBalanceExternal; 46 | } 47 | 48 | /* Continue with default balancing */ 49 | } 50 | 51 | now = uv_now(config->loop); 52 | death_timeout = (uint64_t) config->availability.death_timeout; 53 | 54 | /* Always try the top-most backend when balancing `on-fail` */ 55 | if (balance == kBudBalanceOnFail) 56 | backend->last = 0; 57 | 58 | first = backend->last; 59 | do { 60 | res = &backend->list[backend->last]; 61 | 62 | /* 63 | * Mark backend as dead if it isn't responding for a significant 64 | * amount of time 65 | */ 66 | if (!res->dead && res->dead_since != 0) { 67 | if (now - res->last_checked <= death_timeout && 68 | now - res->dead_since >= death_timeout) { 69 | bud_kill_backend(client, res); 70 | } 71 | } 72 | 73 | /* Do not iterate over backends when balancing 'on-fail' */ 74 | if (balance == kBudBalanceOnFail && !res->dead) 75 | break; 76 | 77 | backend->last++; 78 | backend->last %= backend->count; 79 | } while (res->dead && backend->last != first); 80 | 81 | /* All dead. 82 | * Make sure we make progress when selecting backends. 83 | */ 84 | if (res->dead) { 85 | res = &backend->list[backend->last]; 86 | backend->last++; 87 | backend->last %= backend->count; 88 | } 89 | 90 | return res; 91 | } 92 | 93 | 94 | void bud_kill_backend(bud_client_t* client, 95 | bud_config_backend_t* backend) { 96 | bud_config_t* config; 97 | int r; 98 | 99 | config = client->config; 100 | 101 | /* If there're no reviving - there are no death */ 102 | if (config->availability.revive_interval == 0) 103 | return; 104 | 105 | /* Already waiting for revival */ 106 | if (backend->revive_timer != NULL) 107 | return; 108 | 109 | backend->revive_timer = malloc(sizeof(*backend->revive_timer)); 110 | if (backend->revive_timer == NULL) 111 | return; 112 | 113 | r = uv_timer_init(config->loop, backend->revive_timer); 114 | if (r != 0) 115 | goto failed_init; 116 | backend->revive_timer->data = backend; 117 | r = uv_timer_start(backend->revive_timer, 118 | bud_revive_backend, 119 | config->availability.revive_interval, 120 | 0); 121 | if (r != 0) 122 | goto failed_start; 123 | 124 | 125 | bud_clog(config, 126 | kBudLogWarning, 127 | "Killed backend %s:%d", 128 | backend->host, 129 | backend->port); 130 | bud_trace_kill_backend(client, backend); 131 | backend->dead = 1; 132 | return; 133 | 134 | failed_start: 135 | uv_close((uv_handle_t*) backend->revive_timer, (uv_close_cb) free); 136 | backend->revive_timer = NULL; 137 | return; 138 | 139 | failed_init: 140 | free(backend->revive_timer); 141 | backend->revive_timer = NULL; 142 | } 143 | 144 | 145 | void bud_revive_backend(uv_timer_t* timer) { 146 | bud_config_backend_t* backend; 147 | 148 | /* Ignore errors */ 149 | backend = timer->data; 150 | uv_close((uv_handle_t*) backend->revive_timer, (uv_close_cb) free); 151 | 152 | /* Backend is gone :( */ 153 | if (backend == NULL) 154 | return; 155 | 156 | backend->dead = 0; 157 | backend->dead_since = 0; 158 | backend->revive_timer = NULL; 159 | 160 | bud_trace_revive_backend(NULL, backend); 161 | bud_clog(backend->config, 162 | kBudLogWarning, 163 | "Reviving backend %s:%d", 164 | backend->host, 165 | backend->port); 166 | } 167 | 168 | 169 | bud_client_error_t bud_client_connect(bud_client_t* client) { 170 | int r; 171 | bud_config_t* config; 172 | bud_config_backend_t* backend; 173 | 174 | config = client->config; 175 | backend = client->selected_backend; 176 | 177 | /* 178 | * Connect to backend 179 | * NOTE: We won't start reading until some SSL data will be sent. 180 | */ 181 | r = uv_tcp_init(config->loop, &client->backend.tcp); 182 | if (r != 0) 183 | goto fatal; 184 | client->backend.close = client->close; 185 | client->destroy_waiting++; 186 | 187 | if (r == 0) 188 | r = uv_tcp_nodelay(&client->backend.tcp, 1); 189 | if (r == 0 && backend->keepalive > 0) 190 | r = uv_tcp_keepalive(&client->backend.tcp, 1, backend->keepalive); 191 | if (r != 0) 192 | goto failed_connect; 193 | 194 | DBG(&client->backend, "connecting to %s:%d", backend->host, backend->port); 195 | 196 | r = uv_tcp_connect(&client->connect_req, 197 | &client->backend.tcp, 198 | (struct sockaddr*) &backend->addr, 199 | bud_client_connect_cb); 200 | if (r != 0) 201 | goto failed_connect; 202 | 203 | client->connect = kBudProgressRunning; 204 | 205 | return bud_client_ok(&client->backend); 206 | 207 | failed_connect: 208 | uv_close((uv_handle_t*) &client->backend.tcp, bud_client_close_cb); 209 | client->backend.close = kBudProgressDone; 210 | 211 | return bud_client_error(bud_error_num(kBudErrClientConnect, r), 212 | &client->backend); 213 | 214 | fatal: 215 | return bud_client_error(bud_error_num(kBudErrClientConnect, r), 216 | &client->backend); 217 | } 218 | 219 | 220 | void bud_client_connect_cb(uv_connect_t* req, int status) { 221 | bud_client_t* client; 222 | bud_client_error_t cerr; 223 | 224 | if (status == UV_ECANCELED) 225 | return; 226 | 227 | client = container_of(req, bud_client_t, connect_req); 228 | DBG(&client->backend, "connect %d", status); 229 | 230 | client->selected_backend->last_checked = uv_now(client->config->loop); 231 | 232 | if (status != 0) { 233 | /* Error, try reconnecting */ 234 | client->connect = kBudProgressNone; 235 | WARNING(&client->backend, 236 | "uv_connect() failed: %d - \"%s\"", 237 | status, 238 | uv_strerror(status)); 239 | if (client->selected_backend->dead_since == 0) 240 | client->selected_backend->dead_since = uv_now(client->config->loop); 241 | 242 | /* But reopen the socket first */ 243 | uv_close((uv_handle_t*) &client->backend.tcp, bud_client_connect_close_cb); 244 | client->backend.close = kBudProgressDone; 245 | return; 246 | } 247 | 248 | /* Success */ 249 | client->connect = kBudProgressDone; 250 | client->selected_backend->dead_since = 0; 251 | bud_trace_backend_connect(client, client->selected_backend); 252 | 253 | /* Start reading if queued */ 254 | if (client->backend.reading == kBudProgressRunning) { 255 | cerr = bud_client_read_start(client, &client->backend); 256 | if (!bud_is_ok(cerr.err)) 257 | goto fatal; 258 | } 259 | 260 | /* Prepend proxyline if configured any */ 261 | cerr = bud_client_prepend_proxyline(client, kBudProxylineBackendConnect); 262 | if (!bud_is_ok(cerr.err)) 263 | goto fatal; 264 | 265 | /* Cycle data anyway */ 266 | cerr = bud_client_cycle(client); 267 | if (bud_is_ok(cerr.err)) 268 | return; 269 | 270 | fatal: 271 | bud_client_close(client, cerr); 272 | } 273 | 274 | 275 | void bud_client_connect_close_cb(uv_handle_t* handle) { 276 | bud_client_error_t cerr; 277 | bud_client_t* client; 278 | 279 | client = handle->data; 280 | if (client->close != kBudProgressNone) 281 | return bud_client_close_cb(handle); 282 | 283 | client->destroy_waiting++; 284 | cerr = bud_client_retry(client); 285 | if (bud_is_ok(cerr.err)) 286 | return; 287 | 288 | bud_client_close(client, cerr); 289 | } 290 | 291 | 292 | bud_client_error_t bud_client_retry(bud_client_t* client) { 293 | int r; 294 | bud_client_side_t* side; 295 | 296 | side = &client->backend; 297 | 298 | /* Client closing can't retry */ 299 | if (client->close != kBudProgressNone) 300 | return bud_client_error(bud_error(kBudErrRetryAfterClose), side); 301 | 302 | if (++client->retry_count > client->config->availability.max_retries) 303 | return bud_client_error(bud_error(kBudErrMaxRetries), side); 304 | 305 | /* Select backend again */ 306 | client->backend.close = kBudProgressDone; 307 | if (client->backend_list != NULL) 308 | client->selected_backend = bud_select_backend(client); 309 | 310 | client->retry = kBudProgressNone; 311 | r = uv_timer_start(&client->retry_timer, 312 | bud_client_retry_cb, 313 | client->config->availability.retry_interval, 314 | 0); 315 | if (r != 0) 316 | return bud_client_error(bud_error_num(kBudErrRetryTimerStart, r), side); 317 | client->retry = kBudProgressRunning; 318 | 319 | return bud_client_ok(); 320 | } 321 | 322 | 323 | void bud_client_retry_cb(uv_timer_t* timer) { 324 | bud_client_error_t cerr; 325 | bud_client_t* client; 326 | 327 | client = timer->data; 328 | client->retry = kBudProgressDone; 329 | 330 | /* Backend still dead, try again */ 331 | if (client->selected_backend->dead) { 332 | WARNING_LN(&client->backend, "backend still dead, retrying"); 333 | bud_trace_retry(client); 334 | cerr = bud_client_retry(client); 335 | if (!bud_is_ok(cerr.err)) 336 | bud_client_close(client, cerr); 337 | return; 338 | } 339 | 340 | cerr = bud_client_connect(client); 341 | if (!bud_is_ok(cerr.err)) 342 | bud_client_close(client, cerr); 343 | } 344 | -------------------------------------------------------------------------------- /src/ipc.c: -------------------------------------------------------------------------------- 1 | #include /* malloc, free, NULL */ 2 | #include /* memcpy */ 3 | 4 | #include "uv.h" 5 | 6 | #include "src/ipc.h" 7 | #include "src/common.h" 8 | #include "src/config.h" 9 | #include "src/error.h" 10 | #include "src/logger.h" 11 | 12 | typedef struct bud_ipc_msg_handle_s bud_ipc_msg_handle_t; 13 | 14 | struct bud_ipc_msg_handle_s { 15 | bud_ipc_t* ipc; 16 | uv_tcp_t tcp; 17 | uv_write_t req; 18 | }; 19 | 20 | static void bud_ipc_alloc_cb(uv_handle_t* handle, 21 | size_t suggested_size, 22 | uv_buf_t* buf); 23 | static void bud_ipc_read_cb(uv_stream_t* stream, 24 | ssize_t nread, 25 | const uv_buf_t* buf); 26 | static void bud_ipc_parse(bud_ipc_t* ipc); 27 | static void bud_ipc_msg_handle_on_close(uv_handle_t* handle); 28 | static void bud_ipc_msg_send_cb(uv_write_t* req, int status); 29 | static void bud_ipc_accept_pending(bud_ipc_t* ipc); 30 | 31 | 32 | bud_error_t bud_ipc_init(bud_ipc_t* ipc, bud_config_t* config) { 33 | int r; 34 | bud_error_t err; 35 | 36 | ringbuffer_init(&ipc->buffer); 37 | 38 | ipc->handle = malloc(sizeof(*ipc->handle)); 39 | if (ipc->handle == NULL) { 40 | err = bud_error_str(kBudErrNoMem, "ipc->handle"); 41 | goto failed_alloc_handle; 42 | } 43 | 44 | r = uv_pipe_init(config->loop, ipc->handle, 1); 45 | if (r != 0) { 46 | err = bud_error_num(kBudErrIPCPipeInit, r); 47 | goto failed_pipe_init; 48 | } 49 | 50 | ipc->handle->data = ipc; 51 | ipc->config = config; 52 | ipc->state = kBudIPCType; 53 | ipc->waiting = 1; 54 | /* NOTE: May be overriden by bud_ipc_wait() */ 55 | ipc->ready = kBudIPCReadyDone; 56 | ipc->client_cb = NULL; 57 | ipc->msg_cb = NULL; 58 | 59 | return bud_ok(); 60 | 61 | failed_pipe_init: 62 | free(ipc); 63 | 64 | failed_alloc_handle: 65 | return err; 66 | } 67 | 68 | 69 | bud_error_t bud_ipc_open(bud_ipc_t* ipc, uv_file file) { 70 | int r; 71 | 72 | r = uv_pipe_open(ipc->handle, file); 73 | if (r != 0) 74 | return bud_error_num(kBudErrIPCPipeOpen, r); 75 | 76 | return bud_ok(); 77 | } 78 | 79 | 80 | bud_error_t bud_ipc_start(bud_ipc_t* ipc) { 81 | int r; 82 | 83 | bud_ipc_parse(ipc); 84 | bud_ipc_accept_pending(ipc); 85 | 86 | r = uv_read_start((uv_stream_t*) ipc->handle, 87 | bud_ipc_alloc_cb, 88 | bud_ipc_read_cb); 89 | if (r != 0) 90 | return bud_error_num(kBudErrIPCReadStart, r); 91 | 92 | return bud_ok(); 93 | } 94 | 95 | 96 | void bud_ipc_close(bud_ipc_t* ipc) { 97 | if (ipc->handle != NULL) { 98 | ringbuffer_destroy(&ipc->buffer); 99 | uv_close((uv_handle_t*) ipc->handle, (uv_close_cb) free); 100 | } 101 | ipc->handle = NULL; 102 | } 103 | 104 | 105 | void bud_ipc_alloc_cb(uv_handle_t* handle, 106 | size_t suggested_size, 107 | uv_buf_t* buf) { 108 | bud_ipc_t* ipc; 109 | size_t avail; 110 | char* ptr; 111 | 112 | ipc = handle->data; 113 | 114 | avail = 0; 115 | ptr = ringbuffer_write_ptr(&ipc->buffer, &avail); 116 | *buf = uv_buf_init(ptr, avail); 117 | } 118 | 119 | 120 | void bud_ipc_read_cb(uv_stream_t* stream, 121 | ssize_t nread, 122 | const uv_buf_t* buf) { 123 | bud_ipc_t* ipc; 124 | int r; 125 | 126 | ipc = stream->data; 127 | 128 | /* This should not really happen */ 129 | if (nread == UV_EOF) { 130 | bud_ipc_msg_t msg; 131 | 132 | msg.type = kBudIPCEOF; 133 | msg.size = 0; 134 | 135 | ASSERT(ipc->msg_cb != NULL, "ipc msg_cb not initialized"); 136 | ipc->msg_cb(ipc, &msg); 137 | return; 138 | } 139 | 140 | /* Error, must close the stream */ 141 | if (nread < 0) { 142 | uv_close((uv_handle_t*) ipc->handle, (uv_close_cb) free); 143 | ipc->handle = NULL; 144 | /* XXX Report error */ 145 | return; 146 | } 147 | 148 | r = ringbuffer_write_append(&ipc->buffer, nread); 149 | 150 | /* It is just easier to fail here, and not much point in handling it */ 151 | ASSERT(r >= 0, "Unexpected allocation failure in IPC ring buffer"); 152 | 153 | bud_ipc_parse(ipc); 154 | 155 | /* Accept incoming handles only after loading configuration */ 156 | if (ipc->ready != kBudIPCReadyDone) 157 | return; 158 | 159 | bud_ipc_accept_pending(ipc); 160 | } 161 | 162 | 163 | void bud_ipc_accept_pending(bud_ipc_t* ipc) { 164 | while (uv_pipe_pending_count(ipc->handle) > 0) { 165 | uv_handle_type pending; 166 | 167 | pending = uv_pipe_pending_type(ipc->handle); 168 | if (pending == UV_UNKNOWN_HANDLE) 169 | continue; 170 | 171 | ASSERT(pending == UV_TCP, "received non-tcp handle on ipc"); 172 | bud_clog(ipc->config, kBudLogDebug, "received handle on ipc"); 173 | 174 | ASSERT(ipc->client_cb != NULL, "ipc client_cb not initialized"); 175 | ipc->client_cb(ipc); 176 | } 177 | } 178 | 179 | 180 | void bud_ipc_parse(bud_ipc_t* ipc) { 181 | /* Loop while there is some data to parse */ 182 | while (ringbuffer_size(&ipc->buffer) >= ipc->waiting) { 183 | /* Accept IPC messages after initialization will finish */ 184 | if (ipc->ready == kBudIPCReadyNextTick) 185 | break; 186 | 187 | switch (ipc->state) { 188 | case kBudIPCType: 189 | { 190 | uint8_t type; 191 | size_t len; 192 | 193 | len = 1; 194 | type = *(uint8_t*) ringbuffer_read_next(&ipc->buffer, &len); 195 | ASSERT(len >= 1, "Expected at least one byte"); 196 | 197 | /* Consume Balance byte */ 198 | if (type == kBudIPCBalance) { 199 | ringbuffer_read_skip(&ipc->buffer, 1); 200 | continue; 201 | } 202 | 203 | /* Wait for full header */ 204 | ipc->waiting = BUD_IPC_HEADER_SIZE; 205 | ipc->state = kBudIPCHeader; 206 | } 207 | break; 208 | case kBudIPCHeader: 209 | { 210 | size_t r; 211 | char buf[BUD_IPC_HEADER_SIZE]; 212 | 213 | r = ringbuffer_read_into(&ipc->buffer, buf, sizeof(buf)); 214 | ASSERT(r == ipc->waiting, "Read less than expected"); 215 | 216 | ipc->pending.type = *(uint8_t*) buf; 217 | ipc->pending.size = bud_read_uint32(buf, 1); 218 | 219 | ipc->waiting = ipc->pending.size; 220 | ipc->state = kBudIPCBody; 221 | } 222 | break; 223 | case kBudIPCBody: 224 | { 225 | bud_ipc_msg_t* msg; 226 | size_t r; 227 | 228 | msg = malloc(sizeof(*msg) + ipc->waiting - 1); 229 | 230 | /* Can't read, just skip */ 231 | if (msg == NULL) { 232 | ringbuffer_read_skip(&ipc->buffer, ipc->waiting); 233 | continue; 234 | } 235 | 236 | memcpy(msg, &ipc->pending, sizeof(*msg)); 237 | r = ringbuffer_read_into(&ipc->buffer, 238 | (char*) msg->data, 239 | ipc->waiting); 240 | ASSERT(r == ipc->waiting, "Read less than expected"); 241 | 242 | ASSERT(ipc->msg_cb != NULL, "ipc msg_cb not initialized"); 243 | ipc->msg_cb(ipc, msg); 244 | 245 | ipc->waiting = 1; 246 | ipc->state = kBudIPCType; 247 | } 248 | break; 249 | } 250 | } 251 | } 252 | 253 | 254 | void bud_ipc_wait(bud_ipc_t* ipc) { 255 | ipc->ready = kBudIPCReadyNone; 256 | do 257 | uv_run(ipc->config->loop, UV_RUN_ONCE); 258 | while (ipc->ready == kBudIPCReadyNone); 259 | ASSERT(ipc->ready == kBudIPCReadyNextTick, "Unexpected IPC state"); 260 | ipc->ready = kBudIPCReadyDone; 261 | } 262 | 263 | 264 | void bud_ipc_continue(bud_ipc_t* ipc) { 265 | ipc->ready = kBudIPCReadyNextTick; 266 | } 267 | 268 | 269 | bud_error_t bud_ipc_balance(bud_ipc_t* ipc, uv_stream_t* server) { 270 | bud_error_t err; 271 | int r; 272 | uint8_t type; 273 | uv_buf_t buf; 274 | bud_ipc_msg_handle_t* handle; 275 | 276 | /* Allocate space for a IPC write request */ 277 | handle = malloc(sizeof(*handle)); 278 | if (handle == NULL) { 279 | err = bud_error_str(kBudErrNoMem, "bud_ipc_msg_handle_t"); 280 | goto failed_malloc; 281 | } 282 | 283 | handle->ipc = ipc; 284 | 285 | r = uv_tcp_init(ipc->config->loop, &handle->tcp); 286 | if (r != 0) { 287 | err = bud_error(kBudErrIPCBalanceInit); 288 | goto failed_tcp_init; 289 | } 290 | 291 | /* Accept handle */ 292 | r = uv_accept(server, (uv_stream_t*) &handle->tcp); 293 | if (r != 0) { 294 | err = bud_error(kBudErrIPCBalanceAccept); 295 | goto failed_accept; 296 | } 297 | 298 | /* Init IPC message */ 299 | type = kBudIPCBalance; 300 | buf = uv_buf_init((char*) &type, sizeof(type)); 301 | 302 | r = uv_write2(&handle->req, 303 | (uv_stream_t*) ipc->handle, 304 | &buf, 305 | 1, 306 | (uv_stream_t*) &handle->tcp, 307 | bud_ipc_msg_send_cb); 308 | if (r != 0) { 309 | err = bud_error_num(kBudErrIPCBalanceWrite, r); 310 | goto failed_accept; 311 | } 312 | 313 | return bud_ok(); 314 | 315 | failed_accept: 316 | uv_close((uv_handle_t*) &handle->tcp, bud_ipc_msg_handle_on_close); 317 | return err; 318 | 319 | failed_tcp_init: 320 | free(handle); 321 | 322 | failed_malloc: 323 | return err; 324 | } 325 | 326 | 327 | bud_error_t bud_ipc_send(bud_ipc_t* ipc, 328 | bud_ipc_msg_header_t* header, 329 | const char* body) { 330 | bud_error_t err; 331 | uv_write_t* req; 332 | uv_buf_t buf; 333 | int r; 334 | 335 | /* Allocate space for a IPC write request */ 336 | req = malloc(sizeof(*req) + BUD_IPC_HEADER_SIZE + header->size); 337 | if (req == NULL) { 338 | err = bud_error_str(kBudErrNoMem, "uv_write_t (ipc)"); 339 | goto failed_malloc; 340 | } 341 | 342 | buf = uv_buf_init((char*) req + sizeof(*req), 343 | BUD_IPC_HEADER_SIZE + header->size); 344 | 345 | buf.base[0] = header->type; 346 | bud_write_uint32(buf.base, header->size, 1); 347 | memcpy(buf.base + BUD_IPC_HEADER_SIZE, body, header->size); 348 | 349 | r = uv_write(req, 350 | (uv_stream_t*) ipc->handle, 351 | &buf, 352 | 1, 353 | (uv_write_cb) free); 354 | if (r != 0) { 355 | err = bud_error_num(kBudErrIPCSend, r); 356 | goto failed_write; 357 | } 358 | 359 | return bud_ok(); 360 | 361 | failed_write: 362 | free(req); 363 | 364 | failed_malloc: 365 | return err; 366 | } 367 | 368 | 369 | void bud_ipc_msg_handle_on_close(uv_handle_t* handle) { 370 | bud_ipc_msg_handle_t* msg; 371 | 372 | msg = container_of(handle, bud_ipc_msg_handle_t, tcp); 373 | free(msg); 374 | } 375 | 376 | 377 | void bud_ipc_msg_send_cb(uv_write_t* req, int status) { 378 | bud_ipc_msg_handle_t* msg; 379 | 380 | msg = container_of(req, bud_ipc_msg_handle_t, req); 381 | uv_close((uv_handle_t*) &msg->tcp, bud_ipc_msg_handle_on_close); 382 | 383 | /* Ignore ECANCELED */ 384 | if (status == UV_ECANCELED) 385 | return; 386 | 387 | /* Error */ 388 | if (status != 0) { 389 | /* XXX Probably report to caller? */ 390 | bud_clog(msg->ipc->config, 391 | kBudLogWarning, 392 | "ipc send_cb() failed with (%d) \"%s\"", 393 | status, 394 | uv_strerror(status)); 395 | } 396 | } 397 | 398 | 399 | uv_stream_t* bud_ipc_get_stream(bud_ipc_t* ipc) { 400 | ASSERT(ipc->handle != NULL, "IPC get stream before init"); 401 | return (uv_stream_t*) ipc->handle; 402 | } 403 | 404 | 405 | void bud_ipc_parse_set_ticket(bud_ipc_msg_t* msg, 406 | uint32_t* index, 407 | const char** data, 408 | uint32_t* size) { 409 | ASSERT(msg->size >= 4, "Too small message size for Set Ticket"); 410 | 411 | *index = bud_read_uint32(msg->data, 0); 412 | *data = (const char*) msg->data + 4; 413 | *size = msg->size - 4; 414 | } 415 | 416 | 417 | bud_error_t bud_ipc_set_ticket(bud_ipc_t* ipc, 418 | uint32_t index, 419 | const char* data, 420 | uint32_t size) { 421 | bud_ipc_msg_header_t header; 422 | char storage[64]; 423 | ASSERT(size + 4 <= sizeof(storage), "Too big message for Set Ticket"); 424 | 425 | memcpy(storage + 4, data, size); 426 | bud_write_uint32(storage, index, 0); 427 | 428 | header.type = kBudIPCSetTicket; 429 | header.size = size + 4; 430 | 431 | return bud_ipc_send(ipc, &header, storage); 432 | } 433 | --------------------------------------------------------------------------------