void clax_exit(int code) { if (options._log_file) { clax_log("Closing log file '%s'", options.log_file); fclose(options._log_file); clax_log("Exit=%d", code); } exit(code); }
void clax_dispatch(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { char *path_info = req->path_info; size_t path_info_len = strlen(path_info); size_t len = sizeof clax_dispatcher_actions / sizeof clax_dispatcher_actions[0]; clax_log("Matching request %s %s", http_method_str(req->method), path_info); for (int i = 0; i < len; i++) { clax_dispatcher_action_t *action = &clax_dispatcher_actions[i]; size_t match = clax_dispatcher_match(path_info, path_info_len, action->path, strlen(action->path)); if (match) { clax_log("Action matched"); if (action->method_mask & (1 << req->method)) { clax_log("Method matched"); if (req->continue_expected) { if (action->flags_mask & (1 << CLAX_DISPATCHER_FLAG_100_CONTINUE)) { action->fn(clax_ctx, req, res); } else { return; } } else { action->fn(clax_ctx, req, res); } return; } else { clax_dispatch_method_not_allowed(clax_ctx, req, res); return; } } } clax_log("No suitable action matched"); clax_dispatch_not_found(clax_ctx, req, res); return; }
int dev_random_entropy_poll(void *data, unsigned char *output, size_t len, size_t *olen) { FILE *file; size_t ret, left = len; unsigned char *p = output; ((void) data); clax_log("Polling entropy file"); *olen = 0; file = fopen(options.entropy_file, "rb"); if (file == NULL) { clax_log("Entropy failed"); return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; } while (left > 0) { /* /dev/random can return much less than requested. If so, try again */ ret = fread(p, 1, left, file); if (ret == 0 && ferror(file)) { fclose(file); clax_log("Entropy failed"); return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED; } p += ret; left -= ret; sleep(1); } fclose(file); *olen = len; clax_log("Entropy=%d", len); return 0; }
int clax_send_ssl(void *ctx, const unsigned char *buf, size_t len) { int ret; mbedtls_ssl_context *ssl = ctx; while ((ret = mbedtls_ssl_write(ssl, buf, len)) <= 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { clax_log("failed\n ! mbedtls_ssl_write returned %d", ret); return ret; } } /*clax_log("send (ssl)=%d from %d", ret, len);*/ return ret; }
int main(int argc, char **argv) { clax_ctx_t clax_ctx; #ifdef _WIN32 _setmode(_fileno(stdin), _O_BINARY); _setmode(_fileno(stdout), _O_BINARY); _setmode(_fileno(stderr), _O_BINARY); #endif int is_interactive = isatty(fileno(stdin)); signal(SIGINT, term); setbuf(stdout, NULL); clax_options_init(&options); clax_ctx_init(&clax_ctx); clax_ctx.options = &options; int ok = clax_parse_options(&options, argc, argv); if (ok < 0) { const char *error = clax_strerror(ok); size_t len = strlen(error); if (is_interactive) { fprintf(stdout, "Error: %s\n", error); if (ok != -1) { fprintf(stdout, "\n"); clax_usage(); } clax_exit(255); } else { char buf[1024]; char *b = buf; snprintf(buf, sizeof(buf), "HTTP/1.1 500 System Error\r\n" "Content-Type: text/plain\r\n" "Content-Length: %d\r\n" "\r\n" "%s\n", (int)(len + 1), error ); #ifdef MVS b = clax_etoa_alloc(buf, strlen(buf)); #endif fprintf(stdout, "%s", b); #ifdef MVS free(b); #endif goto cleanup; } } clax_log("Option: root=%s", options.root); clax_log("Option: entropy_file=%s", options.entropy_file); clax_log("Option: log_file=%s", options.log_file); clax_log("Option: ssl=%d", options.ssl); if (!options.ssl) { clax_http_dispatch(&clax_ctx, clax_send, clax_recv, NULL); } else { clax_loop_ssl(&clax_ctx); } cleanup: fflush(stdout); fclose(stdout); clax_ctx_free(&clax_ctx); clax_options_free(&options); clax_exit(0); }
void clax_loop_ssl(clax_ctx_t *clax_ctx) { int ret = 0; char pers[] = "clax_server"; #ifdef MVS clax_etoa(pers, strlen(pers)); #endif mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; mbedtls_ssl_context ssl; mbedtls_ssl_config conf; mbedtls_x509_crt srvcert; mbedtls_pk_context pkey; mbedtls_ssl_cache_context cache; mbedtls_ssl_init(&ssl); mbedtls_ssl_config_init(&conf); mbedtls_ssl_cache_init(&cache); mbedtls_x509_crt_init(&srvcert); mbedtls_pk_init(&pkey); mbedtls_entropy_init(&entropy); mbedtls_ctr_drbg_init(&ctr_drbg); #if defined(MBEDTLS_DEBUG_C) mbedtls_debug_set_threshold(DEBUG_LEVEL); #endif clax_log("Loading the server cert and key..."); unsigned char *file = NULL; size_t file_len = 0; clax_log("Loading '%s'...", options.cert_file); file = clax_slurp_alloc(options.cert_file, &file_len); if (file == NULL) { clax_log("Can't load cert_file '%s': %s", options.cert_file, strerror(errno)); goto exit; } #ifdef MVS clax_etoa((char *)file, file_len); #endif clax_log("Parsing '%s'...", options.cert_file); ret = mbedtls_x509_crt_parse(&srvcert, (const unsigned char *)file, file_len); free(file); if (ret != 0) { clax_log("failed\n ! mbedtls_x509_crt_parse returned %d", ret); goto exit; } clax_log("Loading '%s'...", options.key_file); file = clax_slurp_alloc(options.key_file, &file_len); if (file == NULL) { clax_log("Can't load key_file: %s", options.key_file); goto exit; } #ifdef MVS clax_etoa((char *)file, file_len); #endif clax_log("Parsing '%s'...", options.key_file); ret = mbedtls_pk_parse_key(&pkey, (const unsigned char *)file, file_len, NULL, 0); free(file); if (ret != 0) { clax_log("failed\n ! mbedtls_pk_parse_key returned %d", ret); goto exit; } clax_log("ok"); if (options.entropy_file[0]) { clax_log("Using '%s' as entropy file...", options.entropy_file); if ((ret = mbedtls_entropy_add_source(&entropy, dev_random_entropy_poll, NULL, DEV_RANDOM_THRESHOLD, MBEDTLS_ENTROPY_SOURCE_STRONG)) != 0) { clax_log("failed\n ! mbedtls_entropy_add_source returned -0x%04x", -ret); goto exit; } clax_log("ok"); } clax_log("Seeding the random number generator..."); if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)pers, strlen(pers))) != 0) { clax_log("failed\n ! mbedtls_ctr_drbg_seed returned %d", ret); goto exit; } clax_log("ok"); clax_log("Setting up the SSL data...."); if ((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { clax_log("failed\n ! mbedtls_ssl_config_defaults returned %d", ret); goto exit; } if (!options.no_ssl_verify) { mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED); } mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg); mbedtls_ssl_conf_session_cache(&conf, &cache, mbedtls_ssl_cache_get, mbedtls_ssl_cache_set); mbedtls_ssl_conf_ca_chain(&conf, srvcert.next, NULL); if ((ret = mbedtls_ssl_conf_own_cert(&conf, &srvcert, &pkey)) != 0) { clax_log(" failed\n ! mbedtls_ssl_conf_own_cert returned %d", ret); goto exit; } if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) { clax_log(" failed\n ! mbedtls_ssl_setup returned %d", ret); goto exit; } clax_log("ok"); mbedtls_ssl_session_reset(&ssl); mbedtls_ssl_set_bio(&ssl, NULL, clax_send, clax_recv, NULL); clax_log("ok"); clax_log("Performing the SSL/TLS handshake..."); while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { clax_log("failed\n ! mbedtls_ssl_handshake returned %d", ret); goto exit; } } clax_log("ok"); clax_http_dispatch(clax_ctx, clax_send_ssl, clax_recv_ssl, &ssl); clax_log("Closing the connection..."); while ((ret = mbedtls_ssl_close_notify(&ssl)) < 0) { if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { clax_log("failed\n ! mbedtls_ssl_close_notify returned %d", ret); goto exit; } } clax_log("ok"); ret = 0; goto exit; exit: fflush(stdout); #ifdef MBEDTLS_ERROR_C if (ret != 0) { char error_buf[100]; mbedtls_strerror(ret, error_buf, 100); #ifdef MVS clax_atoe(error_buf, strlen(error_buf)); #endif clax_log("Last error was: %d - %s", ret, error_buf); } #endif mbedtls_x509_crt_free(&srvcert); mbedtls_pk_free(&pkey); mbedtls_ssl_free(&ssl); mbedtls_ssl_config_free(&conf); mbedtls_ssl_cache_free(&cache); mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&entropy); }
void clax_dispatch_upload(clax_ctx_t *clax_ctx, clax_http_request_t *req, clax_http_response_t *res) { char *subdir = req->path_info + strlen("/tree/"); if (strlen(subdir)) { struct stat info; if (stat(subdir, &info) == 0 && info.st_mode & S_IFDIR) { } else { clax_log("Output directory '%s' does not exist", subdir); clax_dispatch_bad_request(clax_ctx, req, res); return; } } if (req->continue_expected) { return; } if (strlen(req->multipart_boundary)) { int i; for (i = 0; i < req->multiparts.size; i++) { clax_http_multipart_t *multipart = clax_http_multipart_list_at(&req->multiparts, i); const char *content_disposition = clax_kv_list_find(&multipart->headers, "Content-Disposition"); if (!content_disposition) continue; char prefix[] = "form-data; "; if (strncmp(content_disposition, prefix, strlen(prefix)) != 0) continue; const char *kv = content_disposition + strlen(prefix); size_t name_len; size_t filename_len; const char *name = clax_http_extract_kv(kv, "name", &name_len); const char *filename = clax_http_extract_kv(kv, "filename", &filename_len); if (!name || !filename || (strncmp(name, "file", name_len) != 0)) continue; char *new_name = clax_kv_list_find(&req->query_params, "name"); char *crc32 = clax_kv_list_find(&req->query_params, "crc"); char *time = clax_kv_list_find(&req->query_params, "time"); if (crc32 && strlen(crc32) != 8) { clax_dispatch_bad_request(clax_ctx, req, res); return; } char *fpath; if (new_name && strlen(new_name)) { fpath = clax_strjoin("/", subdir, new_name, NULL); } else { char *p = clax_strndup(filename, filename_len); fpath = clax_strjoin("/", subdir, p, NULL); free(p); } clax_san_path(fpath); int ret = clax_big_buf_write_file(&multipart->bbuf, fpath); if (ret < 0) { clax_log("Saving file failed: %s\n", fpath); clax_dispatch_system_error(clax_ctx, req, res); } else { if (crc32 && strlen(crc32)) { unsigned long got_crc32 = clax_htol(crc32); int fd = open(fpath, O_RDONLY); unsigned long exp_crc32 = clax_crc32_calc_fd(fd); close(fd); if (got_crc32 != exp_crc32) { clax_log("CRC mismatch %u != %u (%s)", exp_crc32, got_crc32, crc32); clax_dispatch_bad_request(clax_ctx, req, res); remove(fpath); free(fpath); return; } else { clax_log("CRC ok %u != %u (%s)", exp_crc32, got_crc32, crc32); } } if (time && strlen(time)) { int mtime = atol(time); struct utimbuf t; t.actime = mtime; t.modtime = mtime; int ok = utime(fpath, &t); if (ok < 0) clax_log("utime on file '%s' failed: %s", fpath, strerror(errno)); } res->status_code = 200; clax_kv_list_push(&res->headers, "Content-Type", "application/json"); clax_big_buf_append_str(&res->body, "{\"message\":\"ok\"}"); } free(fpath); break; } } if (!res->status_code) { clax_dispatch_bad_request(clax_ctx, req, res); } }