From 20c8d86a34c2e92f74fde7f0df04d1927e6900ee Mon Sep 17 00:00:00 2001 From: Dave Lambley Date: Mon, 6 Nov 2023 01:25:12 +0000 Subject: [PATCH] Return a 400 response to HTTP requests (#1231) For our deployment, it is useful if coturn returns a valid HTTP response to an HTTP request. To do this on the same port as STUN/TURN and without enabling the admin site, I have extended `read_client_connection()` to return a canned HTTP response, in response to an HTTP request, rather than immediately closing the connection. --- examples/etc/turnserver.conf | 6 ++++++ src/apps/relay/mainrelay.c | 12 ++++++++++- src/apps/relay/mainrelay.h | 1 + src/apps/relay/netengine.c | 28 ++++++++++++------------- src/server/ns_turn_server.c | 40 ++++++++++++++++++++++++++++++++---- src/server/ns_turn_server.h | 34 +++++++++++++++--------------- 6 files changed, 86 insertions(+), 35 deletions(-) diff --git a/examples/etc/turnserver.conf b/examples/etc/turnserver.conf index 5e008e49..3f7e4287 100644 --- a/examples/etc/turnserver.conf +++ b/examples/etc/turnserver.conf @@ -797,3 +797,9 @@ no-stun-backward-compatibility # binding responses. # response-origin-only-with-rfc5780 + +# Return an HTTP/S response when an HTTP/S connection is made to a TCP port +# otherwise only supporting STUN/TURN. This may be useful for debugging and +# diagnosing connection problems. A "400 Not supported" response is currently +# returned. +#respond-http-unsupported diff --git a/src/apps/relay/mainrelay.c b/src/apps/relay/mainrelay.c index f93cd33b..88ed1e07 100644 --- a/src/apps/relay/mainrelay.c +++ b/src/apps/relay/mainrelay.c @@ -231,7 +231,8 @@ turn_params_t turn_params = { 0, /* log_binding */ 0, /* no_stun_backward_compatibility */ - 0 /* response_origin_only_with_rfc5780 */ + 0, /* response_origin_only_with_rfc5780 */ + 0 /* respond_http_unsupported */ }; //////////////// OpenSSL Init ////////////////////// @@ -1271,6 +1272,10 @@ static char Usage[] = " in binding response (use only the XOR-MAPPED-ADDRESS).\n" " --response-origin-only-with-rfc5780 Only send RESPONSE-ORIGIN attribute in binding response if " "RFC5780 is enabled.\n" + " --respond-http-unsupported Return an HTTP reponse with a 400 status code to HTTP " + "connections made to ports not\n" + " supporting HTTP. The default behaviour is to immediately " + "close the connection.\n" " --version Print version (and exit).\n" " -h Help\n" "\n"; @@ -1426,6 +1431,7 @@ enum EXTRA_OPTS { NO_RFC5780, NO_STUN_BACKWARD_COMPATIBILITY_OPT, RESPONSE_ORIGIN_ONLY_WITH_RFC5780_OPT, + RESPOND_HTTP_UNSUPPORTED_OPT, VERSION_OPT }; @@ -1568,6 +1574,7 @@ static const struct myoption long_options[] = { {"no-rfc5780", optional_argument, NULL, NO_RFC5780}, {"no-stun-backward-compatibility", optional_argument, NULL, NO_STUN_BACKWARD_COMPATIBILITY_OPT}, {"response-origin-only-with-rfc5780", optional_argument, NULL, RESPONSE_ORIGIN_ONLY_WITH_RFC5780_OPT}, + {"respond-http-unsupported", optional_argument, NULL, RESPOND_HTTP_UNSUPPORTED_OPT}, {"version", optional_argument, NULL, VERSION_OPT}, {"syslog-facility", required_argument, NULL, SYSLOG_FACILITY_OPT}, {NULL, no_argument, NULL, 0}}; @@ -2263,6 +2270,9 @@ static void set_option(int c, char *value) { case RESPONSE_ORIGIN_ONLY_WITH_RFC5780_OPT: turn_params.response_origin_only_with_rfc5780 = get_bool_value(value); break; + case RESPOND_HTTP_UNSUPPORTED_OPT: + turn_params.respond_http_unsupported = get_bool_value(value); + break; /* these options have been already taken care of before: */ case 'l': diff --git a/src/apps/relay/mainrelay.h b/src/apps/relay/mainrelay.h index ccaf6c92..98b90135 100644 --- a/src/apps/relay/mainrelay.h +++ b/src/apps/relay/mainrelay.h @@ -330,6 +330,7 @@ typedef struct _turn_params_ { vint log_binding; vint no_stun_backward_compatibility; vint response_origin_only_with_rfc5780; + vint respond_http_unsupported; } turn_params_t; extern turn_params_t turn_params; diff --git a/src/apps/relay/netengine.c b/src/apps/relay/netengine.c index 8c2c7f8a..5df4cfcd 100644 --- a/src/apps/relay/netengine.c +++ b/src/apps/relay/netengine.c @@ -1621,20 +1621,20 @@ static void setup_relay_server(struct relay_server *rs, ioa_engine_handle e, int bufferevent_setcb(rs->auth_in_buf, relay_receive_auth_message, NULL, NULL, rs); bufferevent_enable(rs->auth_in_buf, EV_READ); - init_turn_server(&(rs->server), rs->id, turn_params.verbose, rs->ioa_eng, turn_params.ct, 0, turn_params.fingerprint, - DONT_FRAGMENT_SUPPORTED, start_user_check, check_new_allocation_quota, release_allocation_quota, - turn_params.external_ip, &turn_params.check_origin, &turn_params.no_tcp_relay, - &turn_params.no_udp_relay, &turn_params.stale_nonce, &turn_params.max_allocate_lifetime, - &turn_params.channel_lifetime, &turn_params.permission_lifetime, &turn_params.stun_only, - &turn_params.no_stun, &turn_params.no_software_attribute, &turn_params.web_admin_listen_on_workers, - &turn_params.alternate_servers_list, &turn_params.tls_alternate_servers_list, - &turn_params.aux_servers_list, turn_params.udp_self_balance, &turn_params.no_multicast_peers, - &turn_params.allow_loopback_peers, &turn_params.ip_whitelist, &turn_params.ip_blacklist, - send_socket_to_relay, &turn_params.secure_stun, &turn_params.mobility, turn_params.server_relay, - send_turn_session_info, send_https_socket, allocate_bps, turn_params.oauth, - turn_params.oauth_server_name, turn_params.acme_redirect, - turn_params.allocation_default_address_family, &turn_params.log_binding, - &turn_params.no_stun_backward_compatibility, &turn_params.response_origin_only_with_rfc5780); + init_turn_server( + &(rs->server), rs->id, turn_params.verbose, rs->ioa_eng, turn_params.ct, 0, turn_params.fingerprint, + DONT_FRAGMENT_SUPPORTED, start_user_check, check_new_allocation_quota, release_allocation_quota, + turn_params.external_ip, &turn_params.check_origin, &turn_params.no_tcp_relay, &turn_params.no_udp_relay, + &turn_params.stale_nonce, &turn_params.max_allocate_lifetime, &turn_params.channel_lifetime, + &turn_params.permission_lifetime, &turn_params.stun_only, &turn_params.no_stun, + &turn_params.no_software_attribute, &turn_params.web_admin_listen_on_workers, &turn_params.alternate_servers_list, + &turn_params.tls_alternate_servers_list, &turn_params.aux_servers_list, turn_params.udp_self_balance, + &turn_params.no_multicast_peers, &turn_params.allow_loopback_peers, &turn_params.ip_whitelist, + &turn_params.ip_blacklist, send_socket_to_relay, &turn_params.secure_stun, &turn_params.mobility, + turn_params.server_relay, send_turn_session_info, send_https_socket, allocate_bps, turn_params.oauth, + turn_params.oauth_server_name, turn_params.acme_redirect, turn_params.allocation_default_address_family, + &turn_params.log_binding, &turn_params.no_stun_backward_compatibility, + &turn_params.response_origin_only_with_rfc5780, &turn_params.respond_http_unsupported); if (to_set_rfc5780) { set_rfc5780(&(rs->server), get_alt_addr, send_message_from_listener_to_client); diff --git a/src/server/ns_turn_server.c b/src/server/ns_turn_server.c index 38f5e30a..3c0eb8d8 100644 --- a/src/server/ns_turn_server.c +++ b/src/server/ns_turn_server.c @@ -4497,7 +4497,8 @@ static int read_client_connection(turn_turnserver *server, ts_ur_super_session * if (is_stream_socket(st)) { if (is_http((char *)ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh))) { - const char *proto = "HTTP"; + const char *proto = st == TLS_SOCKET ? "HTTPS" : "HTTP"; + if ((st == TCP_SOCKET) && (try_acme_redirect((char *)ioa_network_buffer_data(in_buffer->nbh), ioa_network_buffer_get_size(in_buffer->nbh), server->acme_redirect, ss->client_socket) == 0)) { @@ -4505,7 +4506,6 @@ static int read_client_connection(turn_turnserver *server, ts_ur_super_session * return 0; } else if (*server->web_admin_listen_on_workers) { if (st == TLS_SOCKET) { - proto = "HTTPS"; set_ioa_socket_app_type(ss->client_socket, HTTPS_CLIENT_SOCKET); TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: %s (%s %s) request: %zu\n", __FUNCTION__, proto, get_ioa_socket_cipher(ss->client_socket), get_ioa_socket_ssl_method(ss->client_socket), @@ -4531,6 +4531,36 @@ static int read_client_connection(turn_turnserver *server, ts_ur_super_session * handle_http_echo(ss->client_socket); } return 0; + } else if (*server->respond_http_unsupported) { + /* Our incoming connection is HTTP, but we are not serving the + * admin site. Return a 400 response. */ + if (st == TLS_SOCKET) { + TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: %s (%s %s) returning 400 response: %zu\n", __FUNCTION__, proto, + get_ioa_socket_cipher(ss->client_socket), get_ioa_socket_ssl_method(ss->client_socket), + ioa_network_buffer_get_size(in_buffer->nbh)); + set_ioa_socket_app_type(ss->client_socket, HTTPS_CLIENT_SOCKET); + } else { + TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "%s: %s returning 400 response", __FUNCTION__, proto); + } + + ioa_network_buffer_handle nbh_http = ioa_network_buffer_allocate(ss->client_socket->e); + + /* HTTP content */ + char *content = "HTTP not supported.\n"; + + /* Measure length of content */ + int content_length = strlen(content); + + /* Construct full response */ + char buffer[1024]; + snprintf(buffer, sizeof(buffer), + "HTTP/1.1 400 %s Not supported\r\nConnection: close\r\nContent-Type: " + "text/plain\r\nContent-Length: %d\r\n\r\n%s", + proto, content_length, content); + + ioa_network_buffer_set_size(nbh_http, strlen(buffer)); + memcpy(ioa_network_buffer_data(nbh_http), buffer, strlen(buffer)); + send_data_from_ioa_socket_nbh(ss->client_socket, NULL, nbh_http, TTL_IGNORE, TOS_IGNORE, NULL); } else { ss->to_be_closed = 1; return 0; @@ -4774,8 +4804,8 @@ void init_turn_server(turn_turnserver *server, turnserver_id id, int verbose, io send_turn_session_info_cb send_turn_session_info, send_https_socket_cb send_https_socket, allocate_bps_cb allocate_bps_func, int oauth, const char *oauth_server_name, const char *acme_redirect, ALLOCATION_DEFAULT_ADDRESS_FAMILY allocation_default_address_family, - vintp log_binding, vintp no_stun_backward_compatibility, - vintp response_origin_only_with_rfc5780) { + vintp log_binding, vintp no_stun_backward_compatibility, vintp response_origin_only_with_rfc5780, + vintp respond_http_unsupported) { if (!server) return; @@ -4853,6 +4883,8 @@ void init_turn_server(turn_turnserver *server, turnserver_id id, int verbose, io server->no_stun_backward_compatibility = no_stun_backward_compatibility; server->response_origin_only_with_rfc5780 = response_origin_only_with_rfc5780; + + server->respond_http_unsupported = respond_http_unsupported; } ioa_engine_handle turn_server_get_engine(turn_turnserver *s) { diff --git a/src/server/ns_turn_server.h b/src/server/ns_turn_server.h index 332dfacb..526ab155 100644 --- a/src/server/ns_turn_server.h +++ b/src/server/ns_turn_server.h @@ -194,28 +194,30 @@ struct _turn_turnserver { /* Only send RESPONSE-ORIGIN attribute in response if RFC5780 is enabled */ vintp response_origin_only_with_rfc5780; + + /* Return an HTTP 400 response to HTTP connections made to ports not + otherwise handling HTTP. */ + vintp respond_http_unsupported; }; const char *get_version(turn_turnserver *server); /////////////////////////////////////////// -void init_turn_server(turn_turnserver *server, turnserver_id id, int verbose, ioa_engine_handle e, - turn_credential_type ct, int stun_port, int fingerprint, dont_fragment_option_t dont_fragment, - get_user_key_cb userkeycb, check_new_allocation_quota_cb chquotacb, - release_allocation_quota_cb raqcb, ioa_addr *external_addr, vintp check_origin, - vintp no_tcp_relay, vintp no_udp_relay, vintp stale_nonce, vintp max_allocate_lifetime, - vintp channel_lifetime, vintp permission_lifetime, vintp stun_only, vintp no_stun, - vintp no_software_attribute, vintp web_admin_listen_on_workers, - turn_server_addrs_list_t *alternate_servers_list, - turn_server_addrs_list_t *tls_alternate_servers_list, turn_server_addrs_list_t *aux_servers_list, - int self_udp_balance, vintp no_multicast_peers, vintp allow_loopback_peers, - ip_range_list_t *ip_whitelist, ip_range_list_t *ip_blacklist, - send_socket_to_relay_cb send_socket_to_relay, vintp secure_stun, vintp mobility, int server_relay, - send_turn_session_info_cb send_turn_session_info, send_https_socket_cb send_https_socket, - allocate_bps_cb allocate_bps_func, int oauth, const char *oauth_server_name, - const char *acme_redirect, ALLOCATION_DEFAULT_ADDRESS_FAMILY allocation_default_address_family, - vintp log_binding, vintp no_stun_backward_compatibility, vintp response_origin_only_with_rfc5780); +void init_turn_server( + turn_turnserver *server, turnserver_id id, int verbose, ioa_engine_handle e, turn_credential_type ct, int stun_port, + int fingerprint, dont_fragment_option_t dont_fragment, get_user_key_cb userkeycb, + check_new_allocation_quota_cb chquotacb, release_allocation_quota_cb raqcb, ioa_addr *external_addr, + vintp check_origin, vintp no_tcp_relay, vintp no_udp_relay, vintp stale_nonce, vintp max_allocate_lifetime, + vintp channel_lifetime, vintp permission_lifetime, vintp stun_only, vintp no_stun, vintp no_software_attribute, + vintp web_admin_listen_on_workers, turn_server_addrs_list_t *alternate_servers_list, + turn_server_addrs_list_t *tls_alternate_servers_list, turn_server_addrs_list_t *aux_servers_list, + int self_udp_balance, vintp no_multicast_peers, vintp allow_loopback_peers, ip_range_list_t *ip_whitelist, + ip_range_list_t *ip_blacklist, send_socket_to_relay_cb send_socket_to_relay, vintp secure_stun, vintp mobility, + int server_relay, send_turn_session_info_cb send_turn_session_info, send_https_socket_cb send_https_socket, + allocate_bps_cb allocate_bps_func, int oauth, const char *oauth_server_name, const char *acme_redirect, + ALLOCATION_DEFAULT_ADDRESS_FAMILY allocation_default_address_family, vintp log_binding, + vintp no_stun_backward_compatibility, vintp response_origin_only_with_rfc5780, vintp respond_http_unsupported); ioa_engine_handle turn_server_get_engine(turn_turnserver *s);