int main (int argc, char** argv) try { init_signals(); // Initialize OpenSSL ERR_load_crypto_strings(); SSL_library_init(); SSL_load_error_strings(); // This cipher list is the "Intermediate compatibility" list from https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29 as of 2014-12-09 vhost_defaults.ciphers = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"; vhost_defaults.dhgroup = make_dh(dh_group14_prime, dh_group14_generator); // 2048 bit group vhost_defaults.ecdhcurve = get_ecdhcurve("prime256v1"); // a.k.a. secp256r1 // Set default SSL options, which can be overridden by config file vhost_defaults.ssl_options[SSL_OP_NO_COMPRESSION] = true; vhost_defaults.ssl_options[SSL_OP_NO_SSLv3] = true; vhost_defaults.ssl_options[SSL_OP_NO_TLSv1] = false; vhost_defaults.ssl_options[SSL_OP_NO_TLSv1_1] = false; vhost_defaults.ssl_options[SSL_OP_NO_TLSv1_2] = false; vhost_defaults.ssl_options[SSL_OP_CIPHER_SERVER_PREFERENCE] = true; // These can't be overriden by config file: vhost_defaults.ssl_options[SSL_OP_SINGLE_DH_USE] = true; vhost_defaults.ssl_options[SSL_OP_SINGLE_ECDH_USE] = true; vhost_defaults.ssl_options[SSL_OP_NO_SSLv2] = true; // Command line arguments come in pairs of the form "--name value" and correspond // directly to the name/value option pairs in the config file (a la OpenVPN). for (int i = 1; i < argc; ) { if (std::strncmp(argv[i], "--", 2) == 0 && i + 1 < argc) { process_config_param(argv[i] + 2, argv[i+1]); i += 2; } else { std::clog << argv[0] << ": Bad arguments" << std::endl; return 2; } } if (vhost_configs.empty()) { // No vhosts specified, so add one implicitly that matches all local addresses / SNI names. // It will use the options from vhost_defaults. vhost_configs.emplace_back(); } for (size_t i = 0; i < vhost_configs.size(); ++i) { vhosts.emplace_back(); Vhost& vhost(vhosts.back()); Vhost_config& config(vhost_configs[i]); vhost.id = i; vhost.servername_set = config.servername_set; vhost.servername = config.servername; init_ssl_ctx(vhost, config); resolve_addresses(vhost, config); } // Free up some memory that's no longer needed: vhost_configs.clear(); vhost_defaults = Basic_vhost_config(); // Listen listening_sock = socket(AF_INET6, SOCK_STREAM, 0); if (listening_sock == -1) { throw System_error("socket", "", errno); } set_reuseaddr(listening_sock); set_not_v6only(listening_sock); if (transparent == TRANSPARENT_ON) { set_transparent(listening_sock); } // TODO: support binding to specific IP addresses struct sockaddr_in6 listening_address; std::memset(&listening_address, '\0', sizeof(listening_address)); listening_address.sin6_family = AF_INET6; listening_address.sin6_addr = in6addr_any; listening_address.sin6_port = htons(listening_port); if (bind(listening_sock, reinterpret_cast<const struct sockaddr*>(&listening_address), sizeof(listening_address)) == -1) { throw System_error("bind", "", errno); } if (listen(listening_sock, SOMAXCONN) == -1) { throw System_error("listen", "", errno); } // Set up UNIX domain socket for communicating with the key server. // Put it in a temporary directory with restrictive permissions so // other users can't traverse its path. We have to use a named // socket as opposed to a socketpair because we need every child process // to communicate with the key server using its own socket. (Duping one // end of a socketpair wouldn't work because then every child would // be referring to the same underlying socket, which provides // insufficient isolation.) temp_directory = make_temp_directory(); filedesc keyserver_sock(make_unix_socket(temp_directory + "/server.sock", &keyserver_sockaddr, &keyserver_sockaddr_len)); if (listen(keyserver_sock, SOMAXCONN) == -1) { throw System_error("listen", "", errno); } // Write PID file, daemonize, etc. std::ofstream pid_file_out; if (!pid_file.empty()) { // Open PID file before forking so we can report errors pid_file_out.open(pid_file.c_str(), std::ofstream::out | std::ofstream::trunc); if (!pid_file_out) { throw Configuration_error("Unable to open PID file " + pid_file + " for writing."); } pid_file_created = true; } if (run_as_daemon) { daemonize(); } if (pid_file_out) { pid_file_out << getpid() << '\n'; pid_file_out.close(); } // Spawn the master key server process keyserver_pid = spawn(keyserver_main, std::move(keyserver_sock)); // Spawn spare children to accept() and service connections if (pipe(children_pipe) == -1) { throw System_error("pipe", "", errno); } set_nonblocking(children_pipe[0], true); spawn_children(); // Wait for signals and readability on children_pipe sigset_t empty_sigset; sigemptyset(&empty_sigset); fd_set readfds; FD_ZERO(&readfds); FD_SET(children_pipe[0], &readfds); is_running = 1; struct timespec timeout = { 2, 0 }; int select_res = 0; while (is_running && ((select_res = pselect(children_pipe[0] + 1, &readfds, NULL, NULL, failed_children ? &timeout : NULL, &empty_sigset)) >= 0 || errno == EINTR)) { if (failed_children && std::time(NULL) >= last_failed_child_time + 2) { failed_children = 0; } if (pending_sigchld) { on_sigchld(); pending_sigchld = 0; } if (select_res > 0) { read_children_pipe(); } FD_SET(children_pipe[0], &readfds); } if (is_running && select_res == -1) { throw System_error("pselect", "", errno); } cleanup(); return 0; } catch (const System_error& error) { std::clog << "titus: System error: " << error.syscall; if (!error.target.empty()) { std::clog << ": " << error.target; } std::clog << ": " << std::strerror(error.number) << std::endl; cleanup(); return 3; } catch (const Openssl_error& error) { std::clog << "titus: OpenSSL error: " << error.message() << std::endl; cleanup(); return 4; } catch (const Configuration_error& error) { std::clog << "titus: Configuration error: " << error.message << std::endl; cleanup(); return 5; } catch (const Too_many_failed_children& error) { // TODO: better error reporting when this happens std::clog << "titus: Too many child processes failed." << std::endl; cleanup(); return 7; } catch (const Keyserver_died& error) { // TODO: better error reporting when this happens std::clog << "titus: Key server died." << std::endl; cleanup(); return 8; }
int intercode_to_bcode(void) { int i; for (i = 0; i < BCODE_MAX; i ++) { cstate.target_bcode->op [i] = OP_nop; cstate.target_bcode->src_line [i] = 0; } // the end of the bcode is filled with stop instructions for (i = BCODE_POS_MAX; i < BCODE_MAX; i ++) { cstate.target_bcode->op [i] = OP_stop; } int intercode_length = cstate.ic_pos; cstate.bc_pos = 0; cstate.ic_pos = 0; cstate.resolve_pos = 0; // position in cstate.ic_address_resolve struct for (cstate.ic_pos = 0; cstate.ic_pos < intercode_length; cstate.ic_pos ++) { if (cstate.bc_pos >= BCODE_POS_MAX - 8) { return intercode_error_text("bcode too large"); } switch(cstate.intercode[cstate.ic_pos].type) { case IC_OP: #ifdef SANITY_CHECK if (cstate.intercode[cstate.ic_pos].value [0] < 0 || cstate.intercode[cstate.ic_pos].value [0] >= INSTRUCTIONS) { fpr("\nError: c_generate.c: intercode_to_bcode(): invalid IC_OP instruction %i at intercode %i (source line %i)", cstate.intercode[cstate.ic_pos].value [0], cstate.ic_pos, cstate.intercode[cstate.ic_pos].src_line); error_call(); } #endif write_bcode(cstate.intercode[cstate.ic_pos].value [0]); if (instruction_set[cstate.intercode[cstate.ic_pos].value [0]].operands > 0) { write_bcode(cstate.intercode[cstate.ic_pos].value [1]); } if (instruction_set[cstate.intercode[cstate.ic_pos].value [0]].operands > 1) { write_bcode(cstate.intercode[cstate.ic_pos].value [2]); } break; case IC_OP_WITH_VARIABLE_OPERAND: // This is like IC_OP but value [1] is an identifier index instead of a value #ifdef SANITY_CHECK if (cstate.intercode[cstate.ic_pos].value [0] < 0 || cstate.intercode[cstate.ic_pos].value [0] >= INSTRUCTIONS) { fpr("\nError: c_generate.c: intercode_to_bcode(): invalid IC_OP_WITH_VARIABLE_OPERAND instruction %i at intercode %i (source line %i)", cstate.intercode[cstate.ic_pos].value [0], cstate.ic_pos, cstate.intercode[cstate.ic_pos].src_line); error_call(); } if (identifier[cstate.intercode[cstate.ic_pos].value [1]].address < 0 || identifier[cstate.intercode[cstate.ic_pos].value [1]].address >= MEMORY_SIZE) { fpr("\nError: c_generate.c: intercode_to_bcode(): invalid IC_OP_WITH_VARIABLE_OPERAND operand (address %i) at intercode %i (source line %i)", identifier[cstate.intercode[cstate.ic_pos].value [1]].address, cstate.ic_pos, cstate.intercode[cstate.ic_pos].src_line); error_call(); } // unlikely to be possible as references to undeclared variables should have been caught during compilation stage. #endif write_bcode(cstate.intercode[cstate.ic_pos].value [0]); write_bcode(identifier[cstate.intercode[cstate.ic_pos].value [1]].address); break; case IC_EXIT_POINT_TRUE: cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_bcode = cstate.bc_pos; break; case IC_EXIT_POINT_FALSE: cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].false_point_bcode = cstate.bc_pos; break; case IC_LABEL_DEFINITION: identifier[cstate.intercode[cstate.ic_pos].value [0]].address = cstate.bc_pos; break; case IC_GOTO_LABEL: if (identifier[cstate.intercode[cstate.ic_pos].value [0]].type != CTOKEN_TYPE_IDENTIFIER_LABEL) return intercode_error_text("goto label not defined"); write_bcode(OP_jump_num); if (identifier[cstate.intercode[cstate.ic_pos].value [0]].address != -1) { write_bcode(identifier[cstate.intercode[cstate.ic_pos].value [0]].address); } else { if (!add_expoint_address_resolve(ADDRESS_RESOLVE_LABEL, cstate.intercode[cstate.ic_pos].value [0])) return 0; } break; case IC_IFFALSE_JUMP_TO_EXIT_POINT: write_bcode(OP_iffalse_jump); if (cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].false_point_bcode == -1) { // exit point address not yet known, so must resolve it at the end of code generation: if (!add_expoint_address_resolve(ADDRESS_RESOLVE_EX_POINT_FALSE, cstate.intercode[cstate.ic_pos].value [0])) return 0; } else write_bcode(cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].false_point_bcode); // address known cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].false_point_used = 1; break; case IC_IFTRUE_JUMP_TO_EXIT_POINT: write_bcode(OP_iftrue_jump); if (cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_bcode == -1) { // exit point address not yet known, so must resolve it at the end of code generation: if (!add_expoint_address_resolve(ADDRESS_RESOLVE_EX_POINT_TRUE, cstate.intercode[cstate.ic_pos].value [0])) return 0; } else write_bcode(cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_bcode); // address known cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_used = 1; break; case IC_JUMP_EXIT_POINT_TRUE: write_bcode(OP_jump_num); if (cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_bcode == -1) { // exit point address not yet known, so must resolve it at the end of code generation: if (!add_expoint_address_resolve(ADDRESS_RESOLVE_EX_POINT_TRUE, cstate.intercode[cstate.ic_pos].value [0])) return 0; } else write_bcode(cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_bcode); // address known cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_used = 1; break; case IC_JUMP_EXIT_POINT_FALSE: write_bcode(OP_jump_num); if (cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].false_point_bcode == -1) { // exit point address not yet known, so must resolve it at the end of code generation: if (!add_expoint_address_resolve(ADDRESS_RESOLVE_EX_POINT_FALSE, cstate.intercode[cstate.ic_pos].value [0])) return 0; } else write_bcode(cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].false_point_bcode); // address known cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].false_point_used = 1; break; case IC_NUMBER: write_bcode(cstate.intercode[cstate.ic_pos].value [0]); break; case IC_SWITCH: write_bcode(OP_switchA); if (!add_expoint_address_resolve(ADDRESS_RESOLVE_EX_POINT_TRUE, cstate.intercode[cstate.ic_pos].value [0])) return 0; write_bcode(cstate.intercode[cstate.ic_pos].value [1]); write_bcode(cstate.intercode[cstate.ic_pos].value [2]); cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_used = 1; break; case IC_JUMP_TABLE: // this just writes a number (to be used by switch code), no instruction. // cstate.target_bcode->op[cstate.bc_pos] = cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_bcode; // cstate.bc_pos ++; if (cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_bcode == -1) { // exit point address not yet known, so must resolve it at the end of code generation: if (!add_expoint_address_resolve(ADDRESS_RESOLVE_EX_POINT_TRUE, cstate.intercode[cstate.ic_pos].value [0])) return 0; } else write_bcode(cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_bcode); // address known cstate.expoint[cstate.intercode[cstate.ic_pos].value [0]].true_point_used = 1; break; default: fpr("\nError: c_generate.c: intercode_to_bcode(): invalid instruction %i at intercode %i (source line %i)", cstate.intercode[cstate.ic_pos].type, cstate.ic_pos, cstate.intercode[cstate.ic_pos].src_line); error_call(); break; // should never happen } } if (!resolve_addresses()) return 0; start_log_line(MLOG_COL_COMPILER); write_to_log("Bcode length "); write_number_to_log(cstate.bc_pos); write_to_log(" ("); write_number_to_log(BCODE_MAX); write_to_log("). Memory used "); write_number_to_log(cstate.mem_pos); write_to_log(" ("); write_number_to_log(MEMORY_SIZE); write_to_log(")."); finish_log_line(); // fpr("\n generation success! bc_pos %i ic_pos %i", cstate.bc_pos, cstate.ic_pos); return 1; // success! }