cleaned up -nohold feature + rejection of new connections when in use

(for compatibility with possible future video streaming additions)
This commit is contained in:
F. Duncanh
2024-05-30 13:08:42 -04:00
parent 23030f1b43
commit 3579e84023
5 changed files with 93 additions and 36 deletions

View File

@@ -31,6 +31,7 @@ struct http_connection_s {
int socket_fd; int socket_fd;
void *user_data; void *user_data;
connection_type_t type;
http_request_t *request; http_request_t *request;
}; };
typedef struct http_connection_s http_connection_t; typedef struct http_connection_s http_connection_t;
@@ -42,6 +43,7 @@ struct httpd_s {
int max_connections; int max_connections;
int open_connections; int open_connections;
http_connection_t *connections; http_connection_t *connections;
char nohold;
/* These variables only edited mutex locked */ /* These variables only edited mutex locked */
int running; int running;
@@ -54,14 +56,43 @@ struct httpd_s {
int server_fd6; 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_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; httpd_t *httpd;
assert(logger); assert(logger);
assert(callbacks); assert(callbacks);
assert(max_connections > 0);
/* Allocate the httpd_t structure */ /* Allocate the httpd_t structure */
httpd = calloc(1, sizeof(httpd_t)); httpd = calloc(1, sizeof(httpd_t));
@@ -69,8 +100,10 @@ httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int max_connections)
return NULL; 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) { if (!httpd->connections) {
free(httpd); free(httpd);
return NULL; return NULL;
@@ -111,6 +144,8 @@ httpd_remove_connection(httpd_t *httpd, http_connection_t *connection)
shutdown(connection->socket_fd, SHUT_WR); shutdown(connection->socket_fd, SHUT_WR);
closesocket(connection->socket_fd); closesocket(connection->socket_fd);
connection->connected = 0; connection->connected = 0;
connection->user_data = NULL;
connection->type = CONNECTION_TYPE_UNKNOWN;
httpd->open_connections--; 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"); logger_log(httpd->logger, LOGGER_INFO, "Max connections reached");
return -1; return -1;
} }
user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len, zone_id); user_data = httpd->callbacks.conn_init(httpd->callbacks.opaque, local, local_len, remote, remote_len, zone_id);
if (!user_data) { if (!user_data) {
logger_log(httpd->logger, LOGGER_ERR, "Error initializing HTTP request handler"); 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].socket_fd = fd;
httpd->connections[i].connected = 1; httpd->connections[i].connected = 1;
httpd->connections[i].user_data = user_data; httpd->connections[i].user_data = user_data;
httpd->connections[i].type = CONNECTION_TYPE_UNKNOWN; //should not be necessary ...
return 0; 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); remote = netutils_get_address(&remote_saddr, &remote_len, &remote_zone_id);
assert (local_zone_id == remote_zone_id); assert (local_zone_id == remote_zone_id);
#ifdef NOHOLD /* remove existing connections to make way for new connections, if http->nohold is set:
/* remove existing connections to make way for new connections: * this will only occur if open_connections >= 2 and a connection with CONNECTION_TYPE_RAOP already exists */
* this will only occur if max_connections > 2 */ if (httpd->nohold && httpd->open_connections >= 2) {
if (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"); logger_log(httpd->logger, LOGGER_INFO, "Destroying current connections to allow connection by new client");
for (int i = 0; i<httpd->max_connections; i++) { for (int i = 0; i<httpd->max_connections; i++) {
http_connection_t *connection = &httpd->connections[i]; http_connection_t *connection = &httpd->connections[i];
if (!connection->connected) { if (!connection->connected) {
continue; 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); ret = httpd_add_connection(httpd, fd, local, local_len, remote, remote_len, local_zone_id);
if (ret == -1) { if (ret == -1) {
@@ -476,4 +511,3 @@ httpd_stop(httpd_t *httpd)
httpd->joined = 1; httpd->joined = 1;
MUTEX_UNLOCK(httpd->run_mutex); MUTEX_UNLOCK(httpd->run_mutex);
} }

View File

@@ -21,17 +21,24 @@
typedef struct httpd_s httpd_t; typedef struct httpd_s httpd_t;
typedef enum connectype_type_e {
CONNECTION_TYPE_UNKNOWN,
CONNECTION_TYPE_RAOP
} connection_type_t;
struct httpd_callbacks_s { struct httpd_callbacks_s {
void* opaque; void* opaque;
void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote, void* (*conn_init)(void *opaque, unsigned char *local, int locallen, unsigned char *remote,
int remotelen, unsigned int zone_id); int remotelen, unsigned int zone_id);
void (*conn_request)(void *ptr, http_request_t *request, http_response_t **response); void (*conn_request)(void *ptr, http_request_t *request, http_response_t **response);
void (*conn_destroy)(void *ptr); void (*conn_destroy)(void *ptr);
}; };
typedef struct httpd_callbacks_s httpd_callbacks_t; 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); int httpd_is_running(httpd_t *httpd);

View File

@@ -90,6 +90,8 @@ struct raop_conn_s {
unsigned int zone_id; unsigned int zone_id;
connection_type_t connection_type;
bool have_active_remote; bool have_active_remote;
}; };
typedef struct raop_conn_s raop_conn_t; 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); memcpy(conn->remote, remote, remotelen);
conn->zone_id = zone_id; conn->zone_id = zone_id;
conn->locallen = locallen; conn->locallen = locallen;
conn->remotelen = remotelen; conn->remotelen = remotelen;
conn->connection_type = CONNECTION_TYPE_UNKNOWN;
conn->have_active_remote = false; conn->have_active_remote = false;
if (raop->callbacks.conn_init) { 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; raop_conn_t *conn = ptr;
logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request"); 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 *method = http_request_get_method(request);
const char *url = http_request_get_url(request); const char *url = http_request_get_url(request);
const char *protocol = http_request_get_protocol(request); const char *protocol = http_request_get_protocol(request);
const char *cseq = http_request_get_header(request, "CSeq"); 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) { if (!conn->have_active_remote) {
const char *active_remote = http_request_get_header(request, "Active-Remote"); const char *active_remote = http_request_get_header(request, "Active-Remote");
if (active_remote) { if (active_remote) {
@@ -264,6 +282,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
if (handler != NULL) { if (handler != NULL) {
handler(conn, request, *response, &response_data, &response_datalen); handler(conn, request, *response, &response_data, &response_datalen);
} }
finish:;
http_response_finish(*response, response_data, response_datalen); http_response_finish(*response, response_data, response_datalen);
int len; int len;
@@ -395,14 +414,11 @@ raop_init(raop_callbacks_t *callbacks) {
} }
int 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; pairing_t *pairing;
httpd_t *httpd; httpd_t *httpd;
httpd_callbacks_t httpd_cbs; httpd_callbacks_t httpd_cbs;
assert(max_clients > 0);
assert(max_clients < 100);
/* create a new public key for pairing */ /* create a new public key for pairing */
int new_key; int new_key;
pairing = pairing_init_generate(device_id, keyfile, &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; httpd_cbs.conn_destroy = &conn_destroy;
/* Initialize the http daemon */ /* Initialize the http daemon */
httpd = httpd_init(raop->logger, &httpd_cbs, max_clients); httpd = httpd_init(raop->logger, &httpd_cbs, nohold);
if (!httpd) { if (!httpd) {
logger_log(raop->logger, LOGGER_ERR, "failed to initialize http daemon"); logger_log(raop->logger, LOGGER_ERR, "failed to initialize http daemon");
pairing_destroy(pairing); pairing_destroy(pairing);

View File

@@ -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); 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 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_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 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); RAOP_API int raop_set_plist(raop_t *raop, const char *plist_item, const int value);

View File

@@ -121,7 +121,7 @@ static unsigned short display[5] = {0}, tcp[3] = {0}, udp[3] = {0};
static bool debug_log = DEFAULT_DEBUG_LOG; static bool debug_log = DEFAULT_DEBUG_LOG;
static int log_level = LOGGER_INFO; static int log_level = LOGGER_INFO;
static bool bt709_fix = false; static bool bt709_fix = false;
static int max_connections = 2; static int nohold = 0;
static unsigned short raop_port; static unsigned short raop_port;
static unsigned short airplay_port; static unsigned short airplay_port;
static uint64_t remote_clock_offset = 0; static uint64_t remote_clock_offset = 0;
@@ -1033,7 +1033,7 @@ static void parse_arguments (int argc, char *argv[]) {
} else if (arg == "-bt709") { } else if (arg == "-bt709") {
bt709_fix = true; bt709_fix = true;
} else if (arg == "-nohold") { } else if (arg == "-nohold") {
max_connections = 3; nohold = 1;
} else if (arg == "-al") { } else if (arg == "-al") {
int n; int n;
char *end; 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_callback(raop, log_callback, NULL);
raop_set_log_level(raop, log_level); raop_set_log_level(raop, log_level);
/* set max number of connections = 2 to protect against capture by new client */ /* set nohold = 1 to allow capture by new client */
if (raop_init2(raop, max_connections, mac_address.c_str(), keyfile.c_str())){ if (raop_init2(raop, nohold, mac_address.c_str(), keyfile.c_str())){
LOGE("Error initializing raop (2)!"); LOGE("Error initializing raop (2)!");
free (raop); free (raop);
return -1; return -1;