diff --git a/lib/httpd.c b/lib/httpd.c index a4adf21..854fed9 100644 --- a/lib/httpd.c +++ b/lib/httpd.c @@ -31,6 +31,7 @@ struct http_connection_s { int socket_fd; void *user_data; + connection_type_t type; http_request_t *request; }; typedef struct http_connection_s http_connection_t; @@ -42,6 +43,7 @@ struct httpd_s { int max_connections; int open_connections; http_connection_t *connections; + char nohold; /* These variables only edited mutex locked */ int running; @@ -54,14 +56,43 @@ struct httpd_s { int server_fd6; }; +int +httpd_set_connection_type (httpd_t *httpd, void *user_data, connection_type_t type) { + for (int i = 0; i < httpd->max_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + if (!connection->connected) { + continue; + } + if (connection->user_data == user_data) { + connection->type = type; + return i; + } + } + return -1; +} + +int +httpd_count_connection_type (httpd_t *httpd, connection_type_t type) { + int count = 0; + for (int i = 0; i < httpd->max_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + if (!connection->connected) { + continue; + } + if (connection->type == type) { + count++; + } + } + return count; +} + +#define MAX_CONNECTIONS 12 /* value used in AppleTV 3*/ httpd_t * -httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections) +httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int nohold) { httpd_t *httpd; - assert(logger); assert(callbacks); - assert(max_connections > 0); /* Allocate the httpd_t structure */ httpd = calloc(1, sizeof(httpd_t)); @@ -69,8 +100,10 @@ httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections) return NULL; } - httpd->max_connections = max_connections; - httpd->connections = calloc(max_connections, sizeof(http_connection_t)); + + httpd->nohold = (nohold ? 1 : 0); + httpd->max_connections = MAX_CONNECTIONS; + httpd->connections = calloc(httpd->max_connections, sizeof(http_connection_t)); if (!httpd->connections) { free(httpd); return NULL; @@ -111,6 +144,8 @@ httpd_remove_connection(httpd_t *httpd, http_connection_t *connection) shutdown(connection->socket_fd, SHUT_WR); closesocket(connection->socket_fd); connection->connected = 0; + connection->user_data = NULL; + connection->type = CONNECTION_TYPE_UNKNOWN; httpd->open_connections--; } @@ -131,7 +166,6 @@ httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len logger_log(httpd->logger, LOGGER_INFO, "Max connections reached"); return -1; } - user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len, zone_id); if (!user_data) { logger_log(httpd->logger, LOGGER_ERR, "Error initializing HTTP request handler"); @@ -142,6 +176,7 @@ httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len httpd->connections[i].socket_fd = fd; httpd->connections[i].connected = 1; httpd->connections[i].user_data = user_data; + httpd->connections[i].type = CONNECTION_TYPE_UNKNOWN; //should not be necessary ... return 0; } @@ -178,20 +213,20 @@ httpd_accept_connection(httpd_t *httpd, int server_fd, int is_ipv6) remote = netutils_get_address(&remote_saddr, &remote_len, &remote_zone_id); assert (local_zone_id == remote_zone_id); -#ifdef NOHOLD - /* remove existing connections to make way for new connections: - * this will only occur if max_connections > 2 */ - if (httpd->open_connections >= 2) { - logger_log(httpd->logger, LOGGER_INFO, "Destroying current connections to allow connection by new client"); - for (int i = 0; imax_connections; i++) { - http_connection_t *connection = &httpd->connections[i]; - if (!connection->connected) { - continue; + /* remove existing connections to make way for new connections, if http->nohold is set: + * this will only occur if open_connections >= 2 and a connection with CONNECTION_TYPE_RAOP already exists */ + if (httpd->nohold && httpd->open_connections >= 2) { + if (httpd_count_connection_type(httpd, CONNECTION_TYPE_RAOP)) { + logger_log(httpd->logger, LOGGER_INFO, "Destroying current connections to allow connection by new client"); + for (int i = 0; imax_connections; i++) { + http_connection_t *connection = &httpd->connections[i]; + if (!connection->connected) { + continue; + } + httpd_remove_connection(httpd, connection); } - httpd_remove_connection(httpd, connection); } } -#endif ret = httpd_add_connection(httpd, fd, local, local_len, remote, remote_len, local_zone_id); if (ret == -1) { @@ -476,4 +511,3 @@ httpd_stop(httpd_t *httpd) httpd->joined = 1; MUTEX_UNLOCK(httpd->run_mutex); } - diff --git a/lib/httpd.h b/lib/httpd.h index a606a6a..3ebde2f 100644 --- a/lib/httpd.h +++ b/lib/httpd.h @@ -21,17 +21,24 @@ typedef struct httpd_s httpd_t; +typedef enum connectype_type_e { + CONNECTION_TYPE_UNKNOWN, + CONNECTION_TYPE_RAOP +} connection_type_t; + struct httpd_callbacks_s { - void* opaque; - void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote, - int remotelen, unsigned int zone_id); - void (*conn_request)(void *ptr, http_request_t *request, http_response_t **response); - void (*conn_destroy)(void *ptr); + void* opaque; + void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote, + int remotelen, unsigned int zone_id); + void (*conn_request)(void *ptr, http_request_t *request, http_response_t **response); + void (*conn_destroy)(void *ptr); }; typedef struct httpd_callbacks_s httpd_callbacks_t; +int httpd_set_connection_type (httpd_t *http, void *user_data, connection_type_t type); +int httpd_count_connection_type (httpd_t *http, connection_type_t type); -httpd_t *httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections); +httpd_t *httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int nohold); int httpd_is_running(httpd_t *httpd); diff --git a/lib/raop.c b/lib/raop.c index fa16cba..cc3d3f9 100644 --- a/lib/raop.c +++ b/lib/raop.c @@ -90,6 +90,8 @@ struct raop_conn_s { unsigned int zone_id; + connection_type_t connection_type; + bool have_active_remote; }; typedef struct raop_conn_s raop_conn_t; @@ -140,10 +142,12 @@ conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remot memcpy(conn->remote, remote, remotelen); conn->zone_id = zone_id; - + conn->locallen = locallen; conn->remotelen = remotelen; + conn->connection_type = CONNECTION_TYPE_UNKNOWN; + conn->have_active_remote = false; if (raop->callbacks.conn_init) { @@ -160,13 +164,27 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { raop_conn_t *conn = ptr; logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request"); + bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG); const char *method = http_request_get_method(request); const char *url = http_request_get_url(request); const char *protocol = http_request_get_protocol(request); const char *cseq = http_request_get_header(request, "CSeq"); - bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG); + if (conn->connection_type == CONNECTION_TYPE_UNKNOWN) { + if (httpd_count_connection_type(conn->raop->httpd, CONNECTION_TYPE_RAOP)) { + char ipaddr[40]; + utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, ipaddr, (int) (sizeof(ipaddr))); + logger_log(conn->raop->logger, LOGGER_WARNING, "rejecting new connection request from %s", ipaddr); + *response = http_response_init("RTSP/1.0", 409, "Conflict: Server is connected to another client"); + http_response_add_header(*response, "CSeq", cseq); + http_response_add_header(*response, "Server", "AirTunes/"GLOBAL_VERSION); + goto finish; + } + httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_RAOP); + conn->connection_type = CONNECTION_TYPE_RAOP; + } + if (!conn->have_active_remote) { const char *active_remote = http_request_get_header(request, "Active-Remote"); if (active_remote) { @@ -264,6 +282,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) { if (handler != NULL) { handler(conn, request, *response, &response_data, &response_datalen); } + finish:; http_response_finish(*response, response_data, response_datalen); int len; @@ -395,14 +414,11 @@ raop_init(raop_callbacks_t *callbacks) { } int -raop_init2(raop_t *raop, int max_clients, const char *device_id, const char *keyfile) { +raop_init2(raop_t *raop, int nohold, const char *device_id, const char *keyfile) { pairing_t *pairing; httpd_t *httpd; httpd_callbacks_t httpd_cbs; - assert(max_clients > 0); - assert(max_clients < 100); - /* create a new public key for pairing */ int new_key; pairing = pairing_init_generate(device_id, keyfile, &new_key); @@ -433,7 +449,7 @@ raop_init2(raop_t *raop, int max_clients, const char *device_id, const char *key httpd_cbs.conn_destroy = &conn_destroy; /* Initialize the http daemon */ - httpd = httpd_init(raop->logger, &httpd_cbs, max_clients); + httpd = httpd_init(raop->logger, &httpd_cbs, nohold); if (!httpd) { logger_log(raop->logger, LOGGER_ERR, "failed to initialize http daemon"); pairing_destroy(pairing); diff --git a/lib/raop.h b/lib/raop.h index 43a7a4e..63a8ef0 100644 --- a/lib/raop.h +++ b/lib/raop.h @@ -69,7 +69,7 @@ raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const c int remote_addr_len, unsigned short timing_rport, timing_protocol_t *time_protocol); RAOP_API raop_t *raop_init(raop_callbacks_t *callbacks); -RAOP_API int raop_init2(raop_t *raop, int max_clients, const char *device_id, const char *keyfile); +RAOP_API int raop_init2(raop_t *raop, int nohold, const char *device_id, const char *keyfile); RAOP_API void raop_set_log_level(raop_t *raop, int level); RAOP_API void raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls); RAOP_API int raop_set_plist(raop_t *raop, const char *plist_item, const int value); diff --git a/uxplay.cpp b/uxplay.cpp index 43e8a5d..fde2bd2 100644 --- a/uxplay.cpp +++ b/uxplay.cpp @@ -121,7 +121,7 @@ static unsigned short display[5] = {0}, tcp[3] = {0}, udp[3] = {0}; static bool debug_log = DEFAULT_DEBUG_LOG; static int log_level = LOGGER_INFO; static bool bt709_fix = false; -static int max_connections = 2; +static int nohold = 0; static unsigned short raop_port; static unsigned short airplay_port; static uint64_t remote_clock_offset = 0; @@ -1033,7 +1033,7 @@ static void parse_arguments (int argc, char *argv[]) { } else if (arg == "-bt709") { bt709_fix = true; } else if (arg == "-nohold") { - max_connections = 3; + nohold = 1; } else if (arg == "-al") { int n; char *end; @@ -1825,8 +1825,8 @@ static int start_raop_server (unsigned short display[5], unsigned short tcp[3], } raop_set_log_callback(raop, log_callback, NULL); raop_set_log_level(raop, log_level); - /* set max number of connections = 2 to protect against capture by new client */ - if (raop_init2(raop, max_connections, mac_address.c_str(), keyfile.c_str())){ + /* set nohold = 1 to allow capture by new client */ + if (raop_init2(raop, nohold, mac_address.c_str(), keyfile.c_str())){ LOGE("Error initializing raop (2)!"); free (raop); return -1;