diff --git a/src/apps/relay/http_server.c b/src/apps/relay/http_server.c index ff8e3992..ca70d77e 100644 --- a/src/apps/relay/http_server.c +++ b/src/apps/relay/http_server.c @@ -99,6 +99,83 @@ const char* get_http_date_header() return buffer_header; } +static int is_acme_req(char *req, size_t len) { + static const char *A = " - 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ _ abcdefghijklmnopqrstuvwxyz "; + int c, i, k; + + // Check first request line. Should be like: GET path HTTP/1.x + if (strncmp(req, "GET /.well-known/acme-challenge/", 32)) + return -1; + // Usually (for LE) the "method path" is 32 + 43 = 55 chars. But other + // implementations may choose longer pathes. We define PATHMAX = 127 chars + // to be prepared for "DoS" attacks (STUN msg size max. is ~ 64K). + len =- 21; // min size of trailing headers + if (len > 131) + len = 131; + for (i=32; i < (int) len; i++) { + // find the end of the path + if (req[i] != ' ') + continue; + // consider path < 10 chars invalid. Also we wanna see a "trailer". + if (i < 42 || strncmp(req + i, " HTTP/1.", 8)) + return -2; + // finally check for allowed chars + for (k=32; k < i; k++) { + c = req[k]; + if ((c > 127) || (A[c] == ' ')) + return -3; + } + // all checks passed: sufficient for us to answer with a redirect + return i; + } + return -4; // end of path not found +} + +int try_acme_redirect(char *req, size_t len, const char *url, + ioa_socket_handle s) +{ + static const char *HTML = "301 Moved Permanently

301 Moved Permanently

"; + char http_response[1024]; + int plen, rlen; + + if (url == NULL || url[0] == '\0' || req == NULL || s == 0 ) + return 1; + if (len < 64 || len > 512 || (plen = is_acme_req(req, len)) < 33) + return 2; + + req[plen] = '\0'; + snprintf(http_response, sizeof(http_response) - 1, + "HTTP/1.1 301 Moved Permanently\r\n" + "Content-Type: text/html\r\n" + "Content-Length: %ld\r\n" + "Connection: close\r\n" + "Location: %s%s\r\n" + "\r\n%s", strlen(HTML), url, req + 32, HTML); + + rlen = strlen(http_response); + + // Variant A: direkt write, no eventbuf stuff + if (write(s->fd, http_response, rlen) == -1) { + perror("Sending redirect failed"); + } else if (((turn_turnserver *)s->session->server)->verbose) { + TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "ACME redirect to %s%s\n", + url, req + 32); + } + + req[plen] = ' '; + + // Variant B: via eventbuf does not send anything for whatever reason + /* + set_ioa_socket_app_type(s, HTTP_CLIENT_SOCKET); + ioa_network_buffer_handle nbh = ioa_network_buffer_allocate(s->e); + uint8_t *data = ioa_network_buffer_data(nbh); + bcopy(http_response, data, rlen); + ioa_network_buffer_set_size(nbh, rlen); + send_data_from_ioa_socket_nbh(s, NULL, nbh, TTL_IGNORE, TOS_IGNORE, NULL); + */ + + return 0; +} /////////////////////////////////////////////// static struct headers_list * post_parse(char *data, size_t data_len) diff --git a/src/apps/relay/mainrelay.c b/src/apps/relay/mainrelay.c index 2343a1cd..e6462d9d 100644 --- a/src/apps/relay/mainrelay.c +++ b/src/apps/relay/mainrelay.c @@ -1673,25 +1673,25 @@ static void read_config_file(int argc, char **argv, int pass) if(pass == 0) { - if (argv) { - int i = 0; - for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "-c")) { - if (i < argc - 1) { - STRCPY(config_file, argv[i + 1]); - } else { - TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Wrong usage of -c option\n"); + if (argv) { + int i = 0; + for (i = 0; i < argc; i++) { + if (!strcmp(argv[i], "-c")) { + if (i < argc - 1) { + STRCPY(config_file, argv[i + 1]); + } else { + TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Wrong usage of -c option\n"); + } + } else if (!strcmp(argv[i], "-n")) { + turn_params.do_not_use_config_file = 1; + config_file[0]=0; + return; + } else if (!strcmp(argv[i], "-h")) { + printf("\n%s\n",Usage); + exit(0); + } + } } - } else if (!strcmp(argv[i], "-n")) { - turn_params.do_not_use_config_file = 1; - config_file[0]=0; - return; - } else if (!strcmp(argv[i], "-h")) { - printf("\n%s\n",Usage); - exit(0); - } - } - } } if (!turn_params.do_not_use_config_file && config_file[0]) { @@ -1728,7 +1728,7 @@ static void read_config_file(int argc, char **argv, int pass) STRCPY(sarg, s); if (parse_arg_string(sarg, &c, &value) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Bad configuration format: %s\n", - sarg); + sarg); } else if((pass == 0) && (c == 'l')) { set_logfile(value); } else if((pass==0) && (c==NO_STDOUT_LOG_OPT)) { @@ -1742,9 +1742,9 @@ static void read_config_file(int argc, char **argv, int pass) } else if ((pass==0) && (c==NEW_LOG_TIMESTAMP_FORMAT_OPT)) { set_turn_log_timestamp_format(value); } else if((pass == 0) && (c != 'u')) { - set_option(c, value); + set_option(c, value); } else if((pass > 0) && (c == 'u')) { - set_option(c, value); + set_option(c, value); } if (s[slen - 1] == 59) { TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "Check config! The following line ends with semicolon: \"%s\" \n",s); @@ -1757,7 +1757,7 @@ static void read_config_file(int argc, char **argv, int pass) } else TURN_LOG_FUNC(TURN_LOG_LEVEL_WARNING, "WARNING: Cannot find config file: %s. Default and command-line settings will be used.\n", - config_file); + config_file); if (full_path_to_config_file) { free(full_path_to_config_file); @@ -1770,7 +1770,7 @@ static int disconnect_database(void) { const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->disconnect) { - dbd->disconnect(); + dbd->disconnect(); } return 0; } @@ -1801,183 +1801,183 @@ static int adminmain(int argc, char **argv) while (((c = getopt_long(argc, argv, ADMIN_OPTIONS, uo.u.o, NULL)) != -1)) { switch (c){ - case 'P': - if(pwd[0]) { - char result[257]; - generate_new_enc_password((char*)pwd, result); - printf("%s\n",result); - exit(0); - } - print_enc_password = 1; - break; - case 'E': - print_enc_aes_password = 1; - break; - case 'g': - ct = TA_SET_REALM_OPTION; - break; - case 'G': - ct = TA_LIST_REALM_OPTIONS; - break; - case ADMIN_USER_QUOTA_OPT: - po.user_quota = (vint)atoi(optarg); - break; - case ADMIN_TOTAL_QUOTA_OPT: - po.total_quota = (vint)atoi(optarg); - break; - case ADMIN_MAX_BPS_OPT: - po.max_bps = (vint)atoi(optarg); - break; - case 'O': - ct = TA_ADD_ORIGIN; - break; - case 'R': - ct = TA_DEL_ORIGIN; - break; - case 'I': - ct = TA_LIST_ORIGINS; - break; - case 'o': - STRCPY(origin,optarg); - break; - case 'k': - ct = TA_PRINT_KEY; - break; - case 'a': - ct = TA_UPDATE_USER; - break; - case 'd': - ct = TA_DELETE_USER; - break; - case 'A': - ct = TA_UPDATE_USER; - is_admin = 1; - break; - case 'D': - ct = TA_DELETE_USER; - is_admin = 1; - break; - case 'l': - ct = TA_LIST_USERS; - break; - case 'L': - ct = TA_LIST_USERS; - is_admin = 1; - break; - case 's': - ct = TA_SET_SECRET; - STRCPY(secret,optarg); - break; - case 'S': - ct = TA_SHOW_SECRET; - break; - case 'X': - ct = TA_DEL_SECRET; - if(optarg) - STRCPY(secret,optarg); - break; - case DEL_ALL_AUTH_SECRETS_OPT: - ct = TA_DEL_SECRET; - break; + case 'P': + if(pwd[0]) { + char result[257]; + generate_new_enc_password((char*)pwd, result); + printf("%s\n",result); + exit(0); + } + print_enc_password = 1; + break; + case 'E': + print_enc_aes_password = 1; + break; + case 'g': + ct = TA_SET_REALM_OPTION; + break; + case 'G': + ct = TA_LIST_REALM_OPTIONS; + break; + case ADMIN_USER_QUOTA_OPT: + po.user_quota = (vint)atoi(optarg); + break; + case ADMIN_TOTAL_QUOTA_OPT: + po.total_quota = (vint)atoi(optarg); + break; + case ADMIN_MAX_BPS_OPT: + po.max_bps = (vint)atoi(optarg); + break; + case 'O': + ct = TA_ADD_ORIGIN; + break; + case 'R': + ct = TA_DEL_ORIGIN; + break; + case 'I': + ct = TA_LIST_ORIGINS; + break; + case 'o': + STRCPY(origin,optarg); + break; + case 'k': + ct = TA_PRINT_KEY; + break; + case 'a': + ct = TA_UPDATE_USER; + break; + case 'd': + ct = TA_DELETE_USER; + break; + case 'A': + ct = TA_UPDATE_USER; + is_admin = 1; + break; + case 'D': + ct = TA_DELETE_USER; + is_admin = 1; + break; + case 'l': + ct = TA_LIST_USERS; + break; + case 'L': + ct = TA_LIST_USERS; + is_admin = 1; + break; + case 's': + ct = TA_SET_SECRET; + STRCPY(secret,optarg); + break; + case 'S': + ct = TA_SHOW_SECRET; + break; + case 'X': + ct = TA_DEL_SECRET; + if(optarg) + STRCPY(secret,optarg); + break; + case DEL_ALL_AUTH_SECRETS_OPT: + ct = TA_DEL_SECRET; + break; #if !defined(TURN_NO_SQLITE) - case 'b': - STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); - turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_SQLITE; - break; + case 'b': + STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); + turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_SQLITE; + break; #endif #if !defined(TURN_NO_PQ) - case 'e': - STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); - turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_PQ; - break; + case 'e': + STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); + turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_PQ; + break; #endif #if !defined(TURN_NO_MYSQL) - case 'M': - STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); - turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MYSQL; - break; + case 'M': + STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); + turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MYSQL; + break; #endif #if !defined(TURN_NO_MONGO) - case 'J': - STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); - turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MONGO; - break; + case 'J': + STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); + turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_MONGO; + break; #endif #if !defined(TURN_NO_HIREDIS) - case 'N': - STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); - turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_REDIS; - break; + case 'N': + STRCPY(turn_params.default_users_db.persistent_users_db.userdb,optarg); + turn_params.default_users_db.userdb_type = TURN_USERDB_TYPE_REDIS; + break; #endif - case 'u': - STRCPY(user,optarg); - if(!is_secure_string((uint8_t*)user,1)) { - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name structure or symbols, choose another name: %s\n",user); - exit(-1); - } - if(SASLprep((uint8_t*)user)<0) { - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name: %s\n",user); - exit(-1); - } - break; - case 'r': - set_default_realm_name(optarg); - STRCPY(realm,optarg); - if(SASLprep((uint8_t*)realm)<0) { - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong realm: %s\n",realm); - exit(-1); - } - break; - case 'p': - STRCPY(pwd,optarg); - if(SASLprep((uint8_t*)pwd)<0) { - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password: %s\n",pwd); - exit(-1); - } - if(print_enc_password) { - char result[257]; - generate_new_enc_password((char*)pwd, result); - printf("%s\n",result); - exit(0); - } - if(print_enc_aes_password){ - encrypt_aes_128(pwd, generated_key); - exit(0); - } - break; - case 'x': - generate_aes_128_key(optarg, generated_key); - exit(0); - break; - case 'f': - fptr = fopen((char*)optarg, "r"); - if(fptr == NULL){ - printf("No such file like %s\n", (char*)optarg); - } - else{ - fseek (fptr, 0, SEEK_SET); - rc = fread(generated_key, sizeof(char), 16, fptr); - if( rc == 0 ){ - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key file is empty\n",__FUNCTION__); + case 'u': + STRCPY(user,optarg); + if(!is_secure_string((uint8_t*)user,1)) { + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name structure or symbols, choose another name: %s\n",user); + exit(-1); + } + if(SASLprep((uint8_t*)user)<0) { + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name: %s\n",user); + exit(-1); + } + break; + case 'r': + set_default_realm_name(optarg); + STRCPY(realm,optarg); + if(SASLprep((uint8_t*)realm)<0) { + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong realm: %s\n",realm); + exit(-1); + } + break; + case 'p': + STRCPY(pwd,optarg); + if(SASLprep((uint8_t*)pwd)<0) { + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong password: %s\n",pwd); + exit(-1); + } + if(print_enc_password) { + char result[257]; + generate_new_enc_password((char*)pwd, result); + printf("%s\n",result); + exit(0); + } + if(print_enc_aes_password){ + encrypt_aes_128(pwd, generated_key); + exit(0); + } + break; + case 'x': + generate_aes_128_key(optarg, generated_key); + exit(0); + break; + case 'f': + fptr = fopen((char*)optarg, "r"); + if(fptr == NULL){ + printf("No such file like %s\n", (char*)optarg); } else{ - if( rc != 16 ){ - TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key length is not enough\n",__FUNCTION__); + fseek (fptr, 0, SEEK_SET); + rc = fread(generated_key, sizeof(char), 16, fptr); + if( rc == 0 ){ + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key file is empty\n",__FUNCTION__); } + else{ + if( rc != 16 ){ + TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s: ERROR: Secret-Key length is not enough\n",__FUNCTION__); + } + } + fclose (fptr); } - fclose (fptr); - } - break; - case 'v': - decrypt_aes_128((char*)optarg, generated_key); - exit(0); - case 'h': - printf("\n%s\n", AdminUsage); - exit(0); - break; - default: - fprintf(stderr,"\n%s\n", AdminUsage); - exit(-1); + break; + case 'v': + decrypt_aes_128((char*)optarg, generated_key); + exit(0); + case 'h': + printf("\n%s\n", AdminUsage); + exit(0); + break; + default: + fprintf(stderr,"\n%s\n", AdminUsage); + exit(-1); } } @@ -2021,16 +2021,16 @@ static void print_features(unsigned long mfn) TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "\n\n==== Show him the instruments, Practical Frost: ====\n\n"); -/* - Frost stepped forward and opened the polished case with a theatrical - flourish. It was a masterful piece of craftsmanship. As the lid was - pulled back, the many trays inside lifted and fanned out, displaying - Glokta’s tools in all their gruesome glory. There were blades of every - size and shape, needles curved and straight, bottles of oil and acid, - nails and screws, clamps and pliers, saws, hammers, chisels. Metal, wood - and glass glittered in the bright lamplight, all polished to mirror - brightness and honed to a murderous sharpness. -*/ + /* + Frost stepped forward and opened the polished case with a theatrical + flourish. It was a masterful piece of craftsmanship. As the lid was + pulled back, the many trays inside lifted and fanned out, displaying + Glokta’s tools in all their gruesome glory. There were blades of every + size and shape, needles curved and straight, bottles of oil and acid, + nails and screws, clamps and pliers, saws, hammers, chisels. Metal, wood + and glass glittered in the bright lamplight, all polished to mirror + brightness and honed to a murderous sharpness. + */ #if !TLS_SUPPORTED TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "TLS is not supported\n"); diff --git a/src/server/ns_turn_ioalib.h b/src/server/ns_turn_ioalib.h index da3e7c5a..4b9f38ad 100644 --- a/src/server/ns_turn_ioalib.h +++ b/src/server/ns_turn_ioalib.h @@ -285,6 +285,7 @@ int get_default_protocol_port(const char* scheme, size_t slen); ///////////// HTTP //////////////////// void handle_http_echo(ioa_socket_handle s); +int try_acme_redirect(char *req, size_t len, const char *url, ioa_socket_handle s); ///////////// ACME /////////////////////