more conn->raop to raop cleanup

This commit is contained in:
F. Duncanh
2025-12-15 00:58:14 -05:00
parent 2e83032686
commit 32562100ee
3 changed files with 224 additions and 209 deletions

View File

@@ -70,12 +70,13 @@ char *create_fcup_request(const char *url, int request_id, const char *client_se
int fcup_request(void *conn_opaque, const char *media_url, const char *client_session_id, int request_id) { int fcup_request(void *conn_opaque, const char *media_url, const char *client_session_id, int request_id) {
raop_conn_t *conn = (raop_conn_t *) conn_opaque; raop_conn_t *conn = (raop_conn_t *) conn_opaque;
raop_t *raop = conn->raop;
int datalen = 0; int datalen = 0;
int requestlen = 0; int requestlen = 0;
int socket_fd = httpd_get_connection_socket_by_type(conn->raop->httpd, CONNECTION_TYPE_PTTH, 1); int socket_fd = httpd_get_connection_socket_by_type(raop->httpd, CONNECTION_TYPE_PTTH, 1);
logger_log(conn->raop->logger, LOGGER_DEBUG, "fcup_request send socket = %d", socket_fd); logger_log(raop->logger, LOGGER_DEBUG, "fcup_request send socket = %d", socket_fd);
/* create xml plist request data */ /* create xml plist request data */
char *plist_xml = create_fcup_request(media_url, request_id, client_session_id, &datalen); char *plist_xml = create_fcup_request(media_url, request_id, client_session_id, &datalen);
@@ -93,20 +94,20 @@ int fcup_request(void *conn_opaque, const char *media_url, const char *client_se
int send_len = send(socket_fd, http_request, requestlen, 0); int send_len = send(socket_fd, http_request, requestlen, 0);
if (send_len < 0) { if (send_len < 0) {
int sock_err = SOCKET_GET_ERROR(); int sock_err = SOCKET_GET_ERROR();
logger_log(conn->raop->logger, LOGGER_ERR, "fcup_request: send error %d:%s\n", logger_log(raop->logger, LOGGER_ERR, "fcup_request: send error %d:%s\n",
sock_err, SOCKET_ERROR_STRING(sock_err)); sock_err, SOCKET_ERROR_STRING(sock_err));
http_response_destroy(request); http_response_destroy(request);
/* shut down connection? */ /* shut down connection? */
return -1; return -1;
} }
if (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG) { if (logger_get_level(raop->logger) >= LOGGER_DEBUG) {
char *request_str = utils_data_to_text(http_request, requestlen); char *request_str = utils_data_to_text(http_request, requestlen);
logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s", request_str); logger_log(raop->logger, LOGGER_DEBUG, "\n%s", request_str);
free (request_str); free (request_str);
} }
http_response_destroy(request); http_response_destroy(request);
logger_log(conn->raop->logger, LOGGER_DEBUG,"fcup_request: send sent Request of %d bytes from socket %d\n", logger_log(raop->logger, LOGGER_DEBUG,"fcup_request: send sent Request of %d bytes from socket %d\n",
send_len, socket_fd); send_len, socket_fd);
return 0; return 0;
} }

View File

@@ -155,10 +155,10 @@ conn_init(void *opaque, unsigned char *local, int locallen, unsigned char *remot
} }
utils_ipaddress_to_string(locallen, local, zone_id, ip_address, (int) sizeof(ip_address)); utils_ipaddress_to_string(locallen, local, zone_id, ip_address, (int) sizeof(ip_address));
logger_log(conn->raop->logger, LOGGER_INFO, "Local : %s", ip_address); logger_log(raop->logger, LOGGER_INFO, "Local : %s", ip_address);
utils_ipaddress_to_string(remotelen, remote, zone_id, ip_address, (int) sizeof(ip_address)); utils_ipaddress_to_string(remotelen, remote, zone_id, ip_address, (int) sizeof(ip_address));
logger_log(conn->raop->logger, LOGGER_INFO, "Remote: %s", ip_address); logger_log(raop->logger, LOGGER_INFO, "Remote: %s", ip_address);
conn->local = (unsigned char *) malloc(locallen); conn->local = (unsigned char *) malloc(locallen);
assert(conn->local); assert(conn->local);
@@ -192,10 +192,11 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
char *response_data = NULL; char *response_data = NULL;
int response_datalen = 0; int response_datalen = 0;
raop_conn_t *conn = ptr; raop_conn_t *conn = ptr;
raop_t *raop = conn->raop;
bool hls_request = false; bool hls_request = false;
logger_log(conn->raop->logger, LOGGER_DEBUG, "conn_request"); logger_log(raop->logger, LOGGER_DEBUG, "conn_request");
bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG); bool logger_debug = (logger_get_level(raop->logger) >= LOGGER_DEBUG);
bool logger_debug_data = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG_DATA); bool logger_debug_data = (logger_get_level(raop->logger) >= LOGGER_DEBUG_DATA);
/* /*
All requests arriving here have been parsed by llhttp to obtain All requests arriving here have been parsed by llhttp to obtain
@@ -224,13 +225,13 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
const char *cseq = http_request_get_header(request, "CSeq"); const char *cseq = http_request_get_header(request, "CSeq");
bool ble = false; bool ble = false;
if (!strcmp(protocol,"RTSP/1.0") && !cseq && (strstr(url, "txtAirPlay") || strstr(url, "txtRAOP") )) { if (!strcmp(protocol,"RTSP/1.0") && !cseq && (strstr(url, "txtAirPlay") || strstr(url, "txtRAOP") )) {
logger_log(conn->raop->logger, LOGGER_INFO, "response to Bluetooth LE beacon advertisement received)"); logger_log(raop->logger, LOGGER_INFO, "response to Bluetooth LE beacon advertisement received)");
ble = true; ble = true;
} }
/* this rejects messages from _airplay._tcp for video streaming protocol unless bool raop->hls_support is true*/ /* this rejects messages from _airplay._tcp for video streaming protocol unless bool raop->hls_support is true*/
if (!cseq && !conn->raop->hls_support && !ble) { if (!cseq && !raop->hls_support && !ble) {
logger_log(conn->raop->logger, LOGGER_INFO, "ignoring AirPlay video streaming request (use option -hls to activate HLS support)"); logger_log(raop->logger, LOGGER_INFO, "ignoring AirPlay video streaming request (use option -hls to activate HLS support)");
return; return;
} }
@@ -240,62 +241,62 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
if (conn->connection_type == CONNECTION_TYPE_UNKNOWN) { if (conn->connection_type == CONNECTION_TYPE_UNKNOWN) {
if (cseq || ble) { if (cseq || ble) {
if (httpd_count_connection_type(conn->raop->httpd, CONNECTION_TYPE_RAOP)) { if (httpd_count_connection_type(raop->httpd, CONNECTION_TYPE_RAOP)) {
char ipaddr[40] = { '\0' }; char ipaddr[40] = { '\0' };
utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, ipaddr, (int) (sizeof(ipaddr))); utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, ipaddr, (int) (sizeof(ipaddr)));
if (httpd_nohold(conn->raop->httpd)) { if (httpd_nohold(raop->httpd)) {
logger_log(conn->raop->logger, LOGGER_INFO, "*****\"nohold\" feature: switch to new connection request from %s", ipaddr); logger_log(raop->logger, LOGGER_INFO, "*****\"nohold\" feature: switch to new connection request from %s", ipaddr);
httpd_remove_known_connections(conn->raop->httpd); httpd_remove_known_connections(raop->httpd);
if (conn->raop->callbacks.video_reset) { if (raop->callbacks.video_reset) {
conn->raop->callbacks.video_reset(conn->raop->callbacks.cls, false, true); raop->callbacks.video_reset(raop->callbacks.cls, false, true);
} }
} else { } else {
logger_log(conn->raop->logger, LOGGER_WARNING, "rejecting new connection request from %s", ipaddr); logger_log(raop->logger, LOGGER_WARNING, "rejecting new connection request from %s", ipaddr);
*response = http_response_create(); *response = http_response_create();
http_response_init(*response, protocol, 409, "Conflict: Server is connected to another client"); http_response_init(*response, protocol, 409, "Conflict: Server is connected to another client");
goto finish; goto finish;
} }
} }
logger_log(conn->raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type RAOP", ptr); logger_log(raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type RAOP", ptr);
httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_RAOP); httpd_set_connection_type(raop->httpd, ptr, CONNECTION_TYPE_RAOP);
conn->connection_type = CONNECTION_TYPE_RAOP; conn->connection_type = CONNECTION_TYPE_RAOP;
} else if (client_session_id) { } else if (client_session_id) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type AirPlay", ptr); logger_log(raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type AirPlay", ptr);
httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_AIRPLAY); httpd_set_connection_type(raop->httpd, ptr, CONNECTION_TYPE_AIRPLAY);
conn->connection_type = CONNECTION_TYPE_AIRPLAY; conn->connection_type = CONNECTION_TYPE_AIRPLAY;
conn->client_session_id = (char *) calloc(strlen(client_session_id) + 1, sizeof(char)); conn->client_session_id = (char *) calloc(strlen(client_session_id) + 1, sizeof(char));
assert(conn->client_session_id); assert(conn->client_session_id);
memcpy(conn->client_session_id, client_session_id, strlen(client_session_id)); memcpy(conn->client_session_id, client_session_id, strlen(client_session_id));
/* airplay video has been requested: shut down any running RAOP udp services */ /* airplay video has been requested: shut down any running RAOP udp services */
raop_conn_t *raop_conn = (raop_conn_t *) httpd_get_connection_by_type(conn->raop->httpd, CONNECTION_TYPE_RAOP, 1); raop_conn_t *raop_conn = (raop_conn_t *) httpd_get_connection_by_type(raop->httpd, CONNECTION_TYPE_RAOP, 1);
if (raop_conn) { if (raop_conn) {
raop_rtp_mirror_t *raop_rtp_mirror = raop_conn->raop_rtp_mirror; raop_rtp_mirror_t *raop_rtp_mirror = raop_conn->raop_rtp_mirror;
if (raop_rtp_mirror) { if (raop_rtp_mirror) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping RAOP mirror" logger_log(raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping RAOP mirror"
" service on RAOP connection %p", raop_conn); " service on RAOP connection %p", raop_conn);
raop_rtp_mirror_stop(raop_rtp_mirror); raop_rtp_mirror_stop(raop_rtp_mirror);
} }
raop_rtp_t *raop_rtp = raop_conn->raop_rtp; raop_rtp_t *raop_rtp = raop_conn->raop_rtp;
if (raop_rtp) { if (raop_rtp) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping RAOP audio" logger_log(raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping RAOP audio"
" service on RAOP connection %p", raop_conn); " service on RAOP connection %p", raop_conn);
raop_rtp_stop(raop_rtp); raop_rtp_stop(raop_rtp);
} }
raop_ntp_t *raop_ntp = raop_conn->raop_ntp; raop_ntp_t *raop_ntp = raop_conn->raop_ntp;
if (raop_rtp) { if (raop_rtp) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping NTP time" logger_log(raop->logger, LOGGER_DEBUG, "New AirPlay connection: stopping NTP time"
" service on RAOP connection %p", raop_conn); " service on RAOP connection %p", raop_conn);
raop_ntp_stop(raop_ntp); raop_ntp_stop(raop_ntp);
} }
} }
} else if (host) { } else if (host) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type HLS", ptr); logger_log(raop->logger, LOGGER_DEBUG, "New connection %p identified as Connection type HLS", ptr);
httpd_set_connection_type(conn->raop->httpd, ptr, CONNECTION_TYPE_HLS); httpd_set_connection_type(raop->httpd, ptr, CONNECTION_TYPE_HLS);
conn->connection_type = CONNECTION_TYPE_HLS; conn->connection_type = CONNECTION_TYPE_HLS;
} else { } else {
logger_log(conn->raop->logger, LOGGER_WARNING, "connection from unknown connection type"); logger_log(raop->logger, LOGGER_WARNING, "connection from unknown connection type");
} }
} }
@@ -312,21 +313,21 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
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) {
conn->have_active_remote = true; conn->have_active_remote = true;
if (conn->raop->callbacks.export_dacp) { if (raop->callbacks.export_dacp) {
const char *dacp_id = http_request_get_header(request, "DACP-ID"); const char *dacp_id = http_request_get_header(request, "DACP-ID");
conn->raop->callbacks.export_dacp(conn->raop->callbacks.cls, active_remote, dacp_id); raop->callbacks.export_dacp(raop->callbacks.cls, active_remote, dacp_id);
} }
} }
} }
logger_log(conn->raop->logger, LOGGER_DEBUG, "\n%s %s %s", method, url, protocol); logger_log(raop->logger, LOGGER_DEBUG, "\n%s %s %s", method, url, protocol);
if (!strcmp(url,"/playback-info")) { if (!strcmp(url,"/playback-info")) {
logger_debug = logger_debug_data; logger_debug = logger_debug_data;
} }
char *header_str= NULL; char *header_str= NULL;
http_request_get_header_string(request, &header_str); http_request_get_header_string(request, &header_str);
if (header_str && logger_debug) { if (header_str && logger_debug) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", header_str); logger_log(raop->logger, LOGGER_DEBUG, "%s", header_str);
bool data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL); bool data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL);
bool data_is_text = (strstr(header_str,"text/") != NULL); bool data_is_text = (strstr(header_str,"text/") != NULL);
int request_datalen = 0; int request_datalen = 0;
@@ -342,7 +343,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
uint32_t plist_len = 0; uint32_t plist_len = 0;
plist_to_xml(req_root_node, &plist_xml, &plist_len); plist_to_xml(req_root_node, &plist_xml, &plist_len);
stripped_xml = utils_strip_data_from_plist_xml(plist_xml); stripped_xml = utils_strip_data_from_plist_xml(plist_xml);
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", (stripped_xml ? stripped_xml : plist_xml)); logger_log(raop->logger, LOGGER_DEBUG, "%s", (stripped_xml ? stripped_xml : plist_xml));
if (stripped_xml) { if (stripped_xml) {
free(stripped_xml); free(stripped_xml);
} }
@@ -352,11 +353,11 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
plist_free(req_root_node); plist_free(req_root_node);
} else if (data_is_text) { } else if (data_is_text) {
char *data_str = utils_data_to_text((char *) request_data, request_datalen); char *data_str = utils_data_to_text((char *) request_data, request_datalen);
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str); logger_log(raop->logger, LOGGER_DEBUG, "%s", data_str);
free(data_str); free(data_str);
} else { } else {
char *data_str = utils_data_to_string((unsigned char *) request_data, request_datalen, 16); char *data_str = utils_data_to_string((unsigned char *) request_data, request_datalen, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str); logger_log(raop->logger, LOGGER_DEBUG, "%s", data_str);
free(data_str); free(data_str);
} }
} }
@@ -370,7 +371,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
assert(!strcmp(client_session_id, conn->client_session_id)); assert(!strcmp(client_session_id, conn->client_session_id));
} }
logger_log(conn->raop->logger, LOGGER_DEBUG, "Handling request %s with URL %s", method, url); logger_log(raop->logger, LOGGER_DEBUG, "Handling request %s with URL %s", method, url);
raop_handler_t handler = NULL; raop_handler_t handler = NULL;
if (!hls_request && !strcmp(protocol, "RTSP/1.0")) { if (!hls_request && !strcmp(protocol, "RTSP/1.0")) {
if (!strcmp(method, "POST")) { if (!strcmp(method, "POST")) {
@@ -447,7 +448,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);
} else { } else {
logger_log(conn->raop->logger, LOGGER_INFO, logger_log(raop->logger, LOGGER_INFO,
"Unhandled Client Request: %s %s %s", method, url, protocol); "Unhandled Client Request: %s %s %s", method, url, protocol);
} }
@@ -476,7 +477,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
char *ptr = strchr(header_str, '\n'); char *ptr = strchr(header_str, '\n');
*(++ptr) = '\0'; *(++ptr) = '\0';
} }
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", header_str); logger_log(raop->logger, LOGGER_DEBUG, "%s", header_str);
free(header_str); free(header_str);
if (response_data) { if (response_data) {
if (response_datalen > 0 && logger_debug) { if (response_datalen > 0 && logger_debug) {
@@ -489,7 +490,7 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
uint32_t plist_len; uint32_t plist_len;
plist_to_xml(res_root_node, &plist_xml, &plist_len); plist_to_xml(res_root_node, &plist_xml, &plist_len);
stripped_xml = utils_strip_data_from_plist_xml(plist_xml); stripped_xml = utils_strip_data_from_plist_xml(plist_xml);
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", (stripped_xml ? stripped_xml : plist_xml)); logger_log(raop->logger, LOGGER_DEBUG, "%s", (stripped_xml ? stripped_xml : plist_xml));
if (stripped_xml) { if (stripped_xml) {
free(stripped_xml); free(stripped_xml);
} }
@@ -499,11 +500,11 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
plist_free(res_root_node); plist_free(res_root_node);
} else if (data_is_text) { } else if (data_is_text) {
char *data_str = utils_data_to_text((char*) response_data, response_datalen); char *data_str = utils_data_to_text((char*) response_data, response_datalen);
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str); logger_log(raop->logger, LOGGER_DEBUG, "%s", data_str);
free(data_str); free(data_str);
} else { } else {
char *data_str = utils_data_to_string((unsigned char *) response_data, response_datalen, 16); char *data_str = utils_data_to_string((unsigned char *) response_data, response_datalen, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "%s", data_str); logger_log(raop->logger, LOGGER_DEBUG, "%s", data_str);
free(data_str); free(data_str);
} }
} }
@@ -516,11 +517,11 @@ conn_request(void *ptr, http_request_t *request, http_response_t **response) {
static void static void
conn_destroy(void *ptr) { conn_destroy(void *ptr) {
raop_conn_t *conn = ptr; raop_conn_t *conn = ptr;
raop_t *raop = conn->raop;
logger_log(raop->logger, LOGGER_DEBUG, "Destroying connection");
logger_log(conn->raop->logger, LOGGER_DEBUG, "Destroying connection"); if (raop->callbacks.conn_destroy) {
raop->callbacks.conn_destroy(raop->callbacks.cls);
if (conn->raop->callbacks.conn_destroy) {
conn->raop->callbacks.conn_destroy(conn->raop->callbacks.cls);
} }
if (conn->raop_rtp) { if (conn->raop_rtp) {
@@ -535,8 +536,8 @@ conn_destroy(void *ptr) {
raop_ntp_destroy(conn->raop_ntp); raop_ntp_destroy(conn->raop_ntp);
} }
if (conn->raop->callbacks.video_flush) { if (raop->callbacks.video_flush) {
conn->raop->callbacks.video_flush(conn->raop->callbacks.cls); raop->callbacks.video_flush(raop->callbacks.cls);
} }
free(conn->local); free(conn->local);

View File

@@ -38,7 +38,8 @@ raop_handler_info(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
assert(conn->raop->dnssd); raop_t *raop = conn->raop;
assert(raop->dnssd);
/* There are three possible RTSP/1.0 GET/info requests /* There are three possible RTSP/1.0 GET/info requests
(1) with a CSeq number and with a plist (1) with a CSeq number and with a plist
@@ -93,13 +94,13 @@ raop_handler_info(raop_conn_t *conn,
} }
if (add_txt_airplay) { if (add_txt_airplay) {
const char *txt = dnssd_get_airplay_txt(conn->raop->dnssd, &len); const char *txt = dnssd_get_airplay_txt(raop->dnssd, &len);
plist_t txt_airplay_node = plist_new_data(txt, len); plist_t txt_airplay_node = plist_new_data(txt, len);
plist_dict_set_item(res_node, txtAirPlay, txt_airplay_node); plist_dict_set_item(res_node, txtAirPlay, txt_airplay_node);
} }
if (add_txt_raop) { if (add_txt_raop) {
const char *txt = dnssd_get_raop_txt(conn->raop->dnssd, &len); const char *txt = dnssd_get_raop_txt(raop->dnssd, &len);
plist_t txt_raop_node = plist_new_data(txt, len); plist_t txt_raop_node = plist_new_data(txt, len);
plist_dict_set_item(res_node, txtRAOP, txt_raop_node); plist_dict_set_item(res_node, txtRAOP, txt_raop_node);
} }
@@ -111,7 +112,7 @@ raop_handler_info(raop_conn_t *conn,
/* deviceID is the physical hardware address, and will not change */ /* deviceID is the physical hardware address, and will not change */
int hw_addr_raw_len = 0; int hw_addr_raw_len = 0;
const char *hw_addr_raw = dnssd_get_hw_addr(conn->raop->dnssd, &hw_addr_raw_len); const char *hw_addr_raw = dnssd_get_hw_addr(raop->dnssd, &hw_addr_raw_len);
char *hw_addr = calloc(1, 3 * hw_addr_raw_len); char *hw_addr = calloc(1, 3 * hw_addr_raw_len);
//int hw_addr_len = //int hw_addr_len =
utils_hwaddr_airplay(hw_addr, 3 * hw_addr_raw_len, hw_addr_raw, hw_addr_raw_len); utils_hwaddr_airplay(hw_addr, 3 * hw_addr_raw_len, hw_addr_raw, hw_addr_raw_len);
@@ -124,17 +125,17 @@ raop_handler_info(raop_conn_t *conn,
/* Persistent Public Key */ /* Persistent Public Key */
int pk_len = 0; int pk_len = 0;
char *pk = utils_parse_hex(conn->raop->pk_str, strlen(conn->raop->pk_str), &pk_len); char *pk = utils_parse_hex(raop->pk_str, strlen(raop->pk_str), &pk_len);
plist_t pk_node = plist_new_data(pk, pk_len); plist_t pk_node = plist_new_data(pk, pk_len);
plist_dict_set_item(res_node, "pk", pk_node); plist_dict_set_item(res_node, "pk", pk_node);
free(pk); free(pk);
uint64_t features = dnssd_get_airplay_features(conn->raop->dnssd); uint64_t features = dnssd_get_airplay_features(raop->dnssd);
plist_t features_node = plist_new_uint(features); plist_t features_node = plist_new_uint(features);
plist_dict_set_item(res_node, "features", features_node); plist_dict_set_item(res_node, "features", features_node);
int name_len = 0; int name_len = 0;
const char *name = dnssd_get_name(conn->raop->dnssd, &name_len); const char *name = dnssd_get_name(raop->dnssd, &name_len);
plist_t name_node = plist_new_string(name); plist_t name_node = plist_new_string(name);
plist_dict_set_item(res_node, "name", name_node); plist_dict_set_item(res_node, "name", name_node);
@@ -216,14 +217,14 @@ raop_handler_info(raop_conn_t *conn,
plist_t displays_0_width_physical_node = plist_new_uint(0); plist_t displays_0_width_physical_node = plist_new_uint(0);
plist_t displays_0_height_physical_node = plist_new_uint(0); plist_t displays_0_height_physical_node = plist_new_uint(0);
plist_t displays_0_uuid_node = plist_new_string("e0ff8a27-6738-3d56-8a16-cc53aacee925"); plist_t displays_0_uuid_node = plist_new_string("e0ff8a27-6738-3d56-8a16-cc53aacee925");
plist_t displays_0_width_node = plist_new_uint(conn->raop->width); plist_t displays_0_width_node = plist_new_uint(raop->width);
plist_t displays_0_height_node = plist_new_uint(conn->raop->height); plist_t displays_0_height_node = plist_new_uint(raop->height);
plist_t displays_0_width_pixels_node = plist_new_uint(conn->raop->width); plist_t displays_0_width_pixels_node = plist_new_uint(raop->width);
plist_t displays_0_height_pixels_node = plist_new_uint(conn->raop->height); plist_t displays_0_height_pixels_node = plist_new_uint(raop->height);
plist_t displays_0_rotation_node = plist_new_bool(0); /* set to true in AppleTV gen 3 (which has features bit 8 set */ plist_t displays_0_rotation_node = plist_new_bool(0); /* set to true in AppleTV gen 3 (which has features bit 8 set */
plist_t displays_0_refresh_rate_node = plist_new_real((double) 1.0 / conn->raop->refreshRate); /* set as real 0.166666 = 60hz in AppleTV gen 3 */ plist_t displays_0_refresh_rate_node = plist_new_real((double) 1.0 / raop->refreshRate); /* set as real 0.166666 = 60hz in AppleTV gen 3 */
plist_t displays_0_max_fps_node = plist_new_uint(conn->raop->maxFPS); plist_t displays_0_max_fps_node = plist_new_uint(raop->maxFPS);
plist_t displays_0_overscanned_node = plist_new_bool(conn->raop->overscanned); plist_t displays_0_overscanned_node = plist_new_bool(raop->overscanned);
plist_t displays_0_features = plist_new_uint(14); plist_t displays_0_features = plist_new_uint(14);
plist_dict_set_item(displays_0_node, "uuid", displays_0_uuid_node); plist_dict_set_item(displays_0_node, "uuid", displays_0_uuid_node);
@@ -251,24 +252,25 @@ static void
raop_handler_pairpinstart(raop_conn_t *conn, raop_handler_pairpinstart(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) { char **response_data, int *response_datalen) {
logger_log(conn->raop->logger, LOGGER_INFO, "client sent PAIR-PIN-START request"); raop_t *raop = conn->raop;
logger_log(raop->logger, LOGGER_INFO, "client sent PAIR-PIN-START request");
int pin_4 = 0; int pin_4 = 0;
if (conn->raop->pin > 9999) { if (raop->pin > 9999) {
pin_4 = conn->raop->pin % 10000; pin_4 = raop->pin % 10000;
} else { } else {
pin_4 = random_pin(); pin_4 = random_pin();
if (pin_4 < 0) { if (pin_4 < 0) {
logger_log(conn->raop->logger, LOGGER_ERR, "Failed to generate random pin"); logger_log(raop->logger, LOGGER_ERR, "Failed to generate random pin");
} else { } else {
conn->raop->pin = (unsigned short) pin_4 % 10000; raop->pin = (unsigned short) pin_4 % 10000;
} }
} }
char pin[6] = { '\0' }; char pin[6] = { '\0' };
snprintf(pin, 5, "%04u", pin_4); snprintf(pin, 5, "%04u", pin_4);
if (conn->raop->callbacks.display_pin) { if (raop->callbacks.display_pin) {
conn->raop->callbacks.display_pin(conn->raop->callbacks.cls, pin); raop->callbacks.display_pin(raop->callbacks.cls, pin);
} }
logger_log(conn->raop->logger, LOGGER_INFO, "*** CLIENT MUST NOW ENTER PIN = \"%s\" AS AIRPLAY PASSWORD", pin); logger_log(raop->logger, LOGGER_INFO, "*** CLIENT MUST NOW ENTER PIN = \"%s\" AS AIRPLAY PASSWORD", pin);
*response_data = NULL; *response_data = NULL;
response_datalen = 0; response_datalen = 0;
} }
@@ -277,22 +279,22 @@ static void
raop_handler_pairsetup_pin(raop_conn_t *conn, raop_handler_pairsetup_pin(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) { char **response_data, int *response_datalen) {
raop_t *raop = conn->raop;
const char *request_data = NULL;; const char *request_data = NULL;;
int request_datalen = 0; int request_datalen = 0;
bool data_is_plist = false; bool data_is_plist = false;
bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG); bool logger_debug = (logger_get_level(raop->logger) >= LOGGER_DEBUG);
request_data = http_request_get_data(request, &request_datalen); request_data = http_request_get_data(request, &request_datalen);
logger_log(conn->raop->logger, LOGGER_INFO, "client requested pair-setup-pin, datalen = %d", request_datalen); logger_log(raop->logger, LOGGER_INFO, "client requested pair-setup-pin, datalen = %d", request_datalen);
if (request_datalen > 0) { if (request_datalen > 0) {
char *header_str= NULL; char *header_str= NULL;
http_request_get_header_string(request, &header_str); http_request_get_header_string(request, &header_str);
logger_log(conn->raop->logger, LOGGER_INFO, "request header: %s", header_str); logger_log(raop->logger, LOGGER_INFO, "request header: %s", header_str);
data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL); data_is_plist = (strstr(header_str,"apple-binary-plist") != NULL);
free(header_str); free(header_str);
} }
if (!data_is_plist) { if (!data_is_plist) {
logger_log(conn->raop->logger, LOGGER_INFO, "did not receive expected plist from client, request_datalen = %d", logger_log(raop->logger, LOGGER_INFO, "did not receive expected plist from client, request_datalen = %d",
request_datalen); request_datalen);
goto authentication_failed; goto authentication_failed;
} }
@@ -317,7 +319,7 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
char *user = NULL; char *user = NULL;
plist_get_string_val(req_method_node, &method); plist_get_string_val(req_method_node, &method);
if (strncmp(method, "pin", strlen (method))) { if (strncmp(method, "pin", strlen (method))) {
logger_log(conn->raop->logger, LOGGER_ERR, "error, required method is \"pin\", client requested \"%s\"", method); logger_log(raop->logger, LOGGER_ERR, "error, required method is \"pin\", client requested \"%s\"", method);
*response_data = NULL; *response_data = NULL;
response_datalen = 0; response_datalen = 0;
free (method); free (method);
@@ -327,18 +329,18 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
plist_mem_free(method); plist_mem_free(method);
method = NULL; method = NULL;
plist_get_string_val(req_user_node, &user); plist_get_string_val(req_user_node, &user);
logger_log(conn->raop->logger, LOGGER_INFO, "pair-setup-pin: device_id = %s", user); logger_log(raop->logger, LOGGER_INFO, "pair-setup-pin: device_id = %s", user);
snprintf(pin, 6, "%04u", conn->raop->pin % 10000); snprintf(pin, 6, "%04u", raop->pin % 10000);
if (conn->raop->pin < 10000) { if (raop->pin < 10000) {
conn->raop->pin = 0; raop->pin = 0;
} }
int ret = srp_new_user(conn->session, conn->raop->pairing, (const char *) user, int ret = srp_new_user(conn->session, raop->pairing, (const char *) user,
(const char *) pin, &salt, &len_salt, &pk, &len_pk); (const char *) pin, &salt, &len_salt, &pk, &len_pk);
plist_mem_free(user); plist_mem_free(user);
user = NULL; user = NULL;
plist_free(req_root_node); plist_free(req_root_node);
if (ret < 0) { if (ret < 0) {
logger_log(conn->raop->logger, LOGGER_ERR, "failed to create user, err = %d", ret); logger_log(raop->logger, LOGGER_ERR, "failed to create user, err = %d", ret);
goto authentication_failed; goto authentication_failed;
} }
plist_t res_root_node = plist_new_dict(); plist_t res_root_node = plist_new_dict();
@@ -362,22 +364,22 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
plist_get_data_val(req_proof_node, &client_proof, &client_proof_len); plist_get_data_val(req_proof_node, &client_proof, &client_proof_len);
if (logger_debug) { if (logger_debug) {
char *str = utils_data_to_string((const unsigned char *) client_proof, client_proof_len, 20); char *str = utils_data_to_string((const unsigned char *) client_proof, client_proof_len, 20);
logger_log(conn->raop->logger, LOGGER_DEBUG, "client SRP6a proof <M> :\n%s", str); logger_log(raop->logger, LOGGER_DEBUG, "client SRP6a proof <M> :\n%s", str);
free (str); free (str);
} }
memcpy(proof, client_proof, (int) client_proof_len); memcpy(proof, client_proof, (int) client_proof_len);
free (client_proof); free (client_proof);
int ret = srp_validate_proof(conn->session, conn->raop->pairing, (const unsigned char *) client_pk, int ret = srp_validate_proof(conn->session, raop->pairing, (const unsigned char *) client_pk,
(int) client_pk_len, proof, (int) client_proof_len, (int) sizeof(proof)); (int) client_pk_len, proof, (int) client_proof_len, (int) sizeof(proof));
free (client_pk); free (client_pk);
plist_free(req_root_node); plist_free(req_root_node);
if (ret < 0) { if (ret < 0) {
logger_log(conn->raop->logger, LOGGER_ERR, "Client Authentication Failure (client proof not validated)"); logger_log(raop->logger, LOGGER_ERR, "Client Authentication Failure (client proof not validated)");
goto authentication_failed; goto authentication_failed;
} }
if (logger_debug) { if (logger_debug) {
char *str = utils_data_to_string((const unsigned char *) proof, sizeof(proof), 20); char *str = utils_data_to_string((const unsigned char *) proof, sizeof(proof), 20);
logger_log(conn->raop->logger, LOGGER_DEBUG, "server SRP6a proof <M1> :\n%s", str); logger_log(raop->logger, LOGGER_DEBUG, "server SRP6a proof <M1> :\n%s", str);
free (str); free (str);
} }
plist_t res_root_node = plist_new_dict(); plist_t res_root_node = plist_new_dict();
@@ -401,9 +403,9 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
if (logger_debug) { if (logger_debug) {
char *str = utils_data_to_string((const unsigned char *) client_epk, client_epk_len, 16); char *str = utils_data_to_string((const unsigned char *) client_epk, client_epk_len, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "client_epk %d:\n%s\n", (int) client_epk_len, str); logger_log(raop->logger, LOGGER_DEBUG, "client_epk %d:\n%s\n", (int) client_epk_len, str);
str = utils_data_to_string((const unsigned char *) client_authtag, client_authtag_len, 16); str = utils_data_to_string((const unsigned char *) client_authtag, client_authtag_len, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "client_authtag %d:\n%s\n", (int) client_authtag_len, str); logger_log(raop->logger, LOGGER_DEBUG, "client_authtag %d:\n%s\n", (int) client_authtag_len, str);
free (str); free (str);
} }
@@ -412,12 +414,12 @@ raop_handler_pairsetup_pin(raop_conn_t *conn,
free (client_authtag); free (client_authtag);
free (client_epk); free (client_epk);
plist_free(req_root_node); plist_free(req_root_node);
ret = srp_confirm_pair_setup(conn->session, conn->raop->pairing, epk, authtag); ret = srp_confirm_pair_setup(conn->session, raop->pairing, epk, authtag);
if (ret < 0) { if (ret < 0) {
logger_log(conn->raop->logger, LOGGER_ERR, "pair-pin-setup (step 3): client authentication failed\n"); logger_log(raop->logger, LOGGER_ERR, "pair-pin-setup (step 3): client authentication failed\n");
goto authentication_failed; goto authentication_failed;
} else { } else {
logger_log(conn->raop->logger, LOGGER_DEBUG, "pair-pin-setup success\n"); logger_log(raop->logger, LOGGER_DEBUG, "pair-pin-setup success\n");
} }
pairing_session_set_setup_status(conn->session); pairing_session_set_setup_status(conn->session);
plist_t res_root_node = plist_new_dict(); plist_t res_root_node = plist_new_dict();
@@ -439,6 +441,7 @@ raop_handler_pairsetup(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
raop_t *raop = conn->raop;
unsigned char public_key[ED25519_KEY_SIZE]; unsigned char public_key[ED25519_KEY_SIZE];
//const char *data; //const char *data;
int datalen = 0; int datalen = 0;
@@ -446,11 +449,11 @@ raop_handler_pairsetup(raop_conn_t *conn,
//data = //data =
http_request_get_data(request, &datalen); http_request_get_data(request, &datalen);
if (datalen != 32) { if (datalen != 32) {
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-setup data"); logger_log(raop->logger, LOGGER_ERR, "Invalid pair-setup data");
return; return;
} }
pairing_get_public_key(conn->raop->pairing, public_key); pairing_get_public_key(raop->pairing, public_key);
pairing_session_set_setup_status(conn->session); pairing_session_set_setup_status(conn->session);
*response_data = calloc(1, sizeof(public_key)); *response_data = calloc(1, sizeof(public_key));
@@ -466,9 +469,10 @@ raop_handler_pairverify(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
raop_t *raop = conn->raop;
bool register_check = false; bool register_check = false;
if (pairing_session_check_handshake_status(conn->session)) { if (pairing_session_check_handshake_status(conn->session)) {
if (conn->raop->use_pin) { if (raop->use_pin) {
pairing_session_set_setup_status(conn->session); pairing_session_set_setup_status(conn->session);
register_check = true; register_check = true;
} else { } else {
@@ -482,32 +486,32 @@ raop_handler_pairverify(raop_conn_t *conn,
data = (unsigned char *) http_request_get_data(request, &datalen); data = (unsigned char *) http_request_get_data(request, &datalen);
if (datalen < 4) { if (datalen < 4) {
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data"); logger_log(raop->logger, LOGGER_ERR, "Invalid pair-verify data");
return; return;
} }
switch (data[0]) { switch (data[0]) {
case 1: case 1:
if (datalen != 4 + X25519_KEY_SIZE + ED25519_KEY_SIZE) { if (datalen != 4 + X25519_KEY_SIZE + ED25519_KEY_SIZE) {
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data"); logger_log(raop->logger, LOGGER_ERR, "Invalid pair-verify data");
return; return;
} }
/* We can fall through these errors, the result will just be garbage... */ /* We can fall through these errors, the result will just be garbage... */
if (pairing_session_handshake(conn->session, data + 4, data + 4 + X25519_KEY_SIZE)) { if (pairing_session_handshake(conn->session, data + 4, data + 4 + X25519_KEY_SIZE)) {
logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing pair-verify handshake"); logger_log(raop->logger, LOGGER_ERR, "Error initializing pair-verify handshake");
} }
if (pairing_session_get_public_key(conn->session, public_key)) { if (pairing_session_get_public_key(conn->session, public_key)) {
logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ECDH public key"); logger_log(raop->logger, LOGGER_ERR, "Error getting ECDH public key");
} }
if (pairing_session_get_signature(conn->session, signature)) { if (pairing_session_get_signature(conn->session, signature)) {
logger_log(conn->raop->logger, LOGGER_ERR, "Error getting ED25519 signature"); logger_log(raop->logger, LOGGER_ERR, "Error getting ED25519 signature");
} }
if (register_check) { if (register_check) {
bool registered_client = true; bool registered_client = true;
if (conn->raop->callbacks.check_register) { if (raop->callbacks.check_register) {
const unsigned char *pk = data + 4 + X25519_KEY_SIZE; const unsigned char *pk = data + 4 + X25519_KEY_SIZE;
char *pk64; char *pk64;
ed25519_pk_to_base64(pk, &pk64); ed25519_pk_to_base64(pk, &pk64);
registered_client = conn->raop->callbacks.check_register(conn->raop->callbacks.cls, pk64); registered_client = raop->callbacks.check_register(raop->callbacks.cls, pk64);
free (pk64); free (pk64);
} }
@@ -524,18 +528,18 @@ raop_handler_pairverify(raop_conn_t *conn,
} }
break; break;
case 0: case 0:
logger_log(conn->raop->logger, LOGGER_DEBUG, "2nd pair-verify step: checking signature"); logger_log(raop->logger, LOGGER_DEBUG, "2nd pair-verify step: checking signature");
if (datalen != 4 + PAIRING_SIG_SIZE) { if (datalen != 4 + PAIRING_SIG_SIZE) {
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid pair-verify data"); logger_log(raop->logger, LOGGER_ERR, "Invalid pair-verify data");
return; return;
} }
if (pairing_session_finish(conn->session, data + 4)) { if (pairing_session_finish(conn->session, data + 4)) {
logger_log(conn->raop->logger, LOGGER_ERR, "Incorrect pair-verify signature"); logger_log(raop->logger, LOGGER_ERR, "Incorrect pair-verify signature");
http_response_set_disconnect(response, 1); http_response_set_disconnect(response, 1);
return; return;
} }
logger_log(conn->raop->logger, LOGGER_DEBUG, "pair-verify: signature is verified"); logger_log(raop->logger, LOGGER_DEBUG, "pair-verify: signature is verified");
http_response_add_header(response, "Content-Type", "application/octet-stream"); http_response_add_header(response, "Content-Type", "application/octet-stream");
break; break;
} }
@@ -546,6 +550,7 @@ raop_handler_fpsetup(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
raop_t *raop = conn->raop;
const unsigned char *data = NULL; const unsigned char *data = NULL;
int datalen = 0; int datalen = 0;
@@ -575,7 +580,7 @@ raop_handler_fpsetup(raop_conn_t *conn,
} }
} }
} else { } else {
logger_log(conn->raop->logger, LOGGER_ERR, "Invalid fp-setup data length"); logger_log(raop->logger, LOGGER_ERR, "Invalid fp-setup data length");
return; return;
} }
} }
@@ -593,9 +598,10 @@ raop_handler_setup(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
raop_t *raop = conn->raop;
const char *dacp_id = NULL; const char *dacp_id = NULL;
const char *active_remote_header = NULL; const char *active_remote_header = NULL;
bool logger_debug = (logger_get_level(conn->raop->logger) >= LOGGER_DEBUG); bool logger_debug = (logger_get_level(raop->logger) >= LOGGER_DEBUG);
const char *data = NULL; const char *data = NULL;
int data_len = 0; int data_len = 0;
@@ -605,8 +611,8 @@ raop_handler_setup(raop_conn_t *conn,
active_remote_header = http_request_get_header(request, "Active-Remote"); active_remote_header = http_request_get_header(request, "Active-Remote");
if (dacp_id && active_remote_header) { if (dacp_id && active_remote_header) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "DACP-ID: %s", dacp_id); logger_log(raop->logger, LOGGER_DEBUG, "DACP-ID: %s", dacp_id);
logger_log(conn->raop->logger, LOGGER_DEBUG, "Active-Remote: %s", active_remote_header); logger_log(raop->logger, LOGGER_DEBUG, "Active-Remote: %s", active_remote_header);
if (conn->raop_rtp) { if (conn->raop_rtp) {
raop_rtp_remote_control_id(conn->raop_rtp, dacp_id, active_remote_header); raop_rtp_remote_control_id(conn->raop_rtp, dacp_id, active_remote_header);
} }
@@ -628,7 +634,7 @@ raop_handler_setup(raop_conn_t *conn,
unsigned char aeskey[16] = { 0 }; unsigned char aeskey[16] = { 0 };
unsigned char eaeskey[72] = { 0 }; unsigned char eaeskey[72] = { 0 };
logger_log(conn->raop->logger, LOGGER_DEBUG, "SETUP 1"); logger_log(raop->logger, LOGGER_DEBUG, "SETUP 1");
// First setup // First setup
@@ -638,50 +644,50 @@ raop_handler_setup(raop_conn_t *conn,
/* RFC2617 Digest authentication (md5 hash) of uxplay client-access password, if set */ /* RFC2617 Digest authentication (md5 hash) of uxplay client-access password, if set */
if (!conn->authenticated && conn->raop->callbacks.passwd) { if (!conn->authenticated && raop->callbacks.passwd) {
size_t pin_len = 4; size_t pin_len = 4;
const char *authorization = NULL; const char *authorization = NULL;
authorization = http_request_get_header(request, "Authorization"); authorization = http_request_get_header(request, "Authorization");
if (!authorization) { if (!authorization) {
// if random_pw is set, but client has changed, unset it // if random_pw is set, but client has changed, unset it
if (conn->raop->random_pw && strncmp(conn->raop->random_pw + pin_len + 1, deviceID, 17)) { if (raop->random_pw && strncmp(raop->random_pw + pin_len + 1, deviceID, 17)) {
free(conn->raop->random_pw); free(raop->random_pw);
conn->raop->random_pw = NULL; raop->random_pw = NULL;
} }
} }
int len = 0; int len = 0;
const char *password = conn->raop->callbacks.passwd(conn->raop->callbacks.cls, &len); const char *password = raop->callbacks.passwd(raop->callbacks.cls, &len);
// len = -1 means use a random password for this connection; len = 0 means no password // len = -1 means use a random password for this connection; len = 0 means no password
if (len == -1 && conn->raop->random_pw && conn->raop->auth_fail_count >= MAX_PW_ATTEMPTS) { if (len == -1 && raop->random_pw && raop->auth_fail_count >= MAX_PW_ATTEMPTS) {
// change random_pw after MAX_PW_ATTEMPTS failed authentication attempts // change random_pw after MAX_PW_ATTEMPTS failed authentication attempts
logger_log(conn->raop->logger, LOGGER_INFO, "Too many authentication failures: generate new random password"); logger_log(raop->logger, LOGGER_INFO, "Too many authentication failures: generate new random password");
free(conn->raop->random_pw); free(raop->random_pw);
conn->raop->random_pw = NULL; raop->random_pw = NULL;
} }
if (len == -1 && !conn->raop->random_pw) { if (len == -1 && !raop->random_pw) {
// get and store 4 random digits // get and store 4 random digits
int pin_4 = random_pin(); int pin_4 = random_pin();
if (pin_4 < 0) { if (pin_4 < 0) {
logger_log(conn->raop->logger, LOGGER_ERR, "Failed to generate random pin"); logger_log(raop->logger, LOGGER_ERR, "Failed to generate random pin");
pin_4 = 1234; pin_4 = 1234;
} }
conn->raop->random_pw = (char *) calloc(pin_len + 1 + 18, sizeof(char)); raop->random_pw = (char *) calloc(pin_len + 1 + 18, sizeof(char));
char *pin = conn->raop->random_pw; char *pin = raop->random_pw;
snprintf(pin, pin_len + 1, "%04u", pin_4 % 10000); snprintf(pin, pin_len + 1, "%04u", pin_4 % 10000);
pin[pin_len] = '\0'; pin[pin_len] = '\0';
snprintf(pin + pin_len + 1, 18, "%s", deviceID); snprintf(pin + pin_len + 1, 18, "%s", deviceID);
conn->raop->auth_fail_count = 0; raop->auth_fail_count = 0;
} }
if (len == -1 && !authorization && conn->raop->random_pw) { if (len == -1 && !authorization && raop->random_pw) {
if (conn->raop->callbacks.display_pin) { if (raop->callbacks.display_pin) {
conn->raop->callbacks.display_pin(conn->raop->callbacks.cls, conn->raop->random_pw); raop->callbacks.display_pin(raop->callbacks.cls, raop->random_pw);
} }
logger_log(conn->raop->logger, LOGGER_INFO, "*** CLIENT MUST NOW ENTER PIN = \"%s\" AS AIRPLAY PASSWORD", conn->raop->random_pw); logger_log(raop->logger, LOGGER_INFO, "*** CLIENT MUST NOW ENTER PIN = \"%s\" AS AIRPLAY PASSWORD", raop->random_pw);
conn->raop->auth_fail_count++; raop->auth_fail_count++;
} }
if (len && !conn->authenticated) { if (len && !conn->authenticated) {
if (len == -1) { if (len == -1) {
password = (const char *) conn->raop->random_pw; password = (const char *) raop->random_pw;
} }
char nonce_string[33] = { '\0' }; char nonce_string[33] = { '\0' };
//bool stale = false; //not implemented //bool stale = false; //not implemented
@@ -693,27 +699,27 @@ raop_handler_setup(raop_conn_t *conn,
if (!conn->authenticated) { if (!conn->authenticated) {
// if random_pw is used, the auth_fail_count will be the number of times it is displayed after creation // if random_pw is used, the auth_fail_count will be the number of times it is displayed after creation
if (len != -1) { if (len != -1) {
conn->raop->auth_fail_count++; raop->auth_fail_count++;
} }
logger_log(conn->raop->logger, LOGGER_INFO, "*** authentication failure: count = %u", conn->raop->auth_fail_count); logger_log(raop->logger, LOGGER_INFO, "*** authentication failure: count = %u", raop->auth_fail_count);
} }
if (conn->authenticated) { if (conn->authenticated) {
//printf("initial authenticatication OK\n"); //printf("initial authenticatication OK\n");
conn->authenticated = conn->authenticated && !strcmp(nonce_string, conn->raop->nonce); conn->authenticated = conn->authenticated && !strcmp(nonce_string, raop->nonce);
if (!conn->authenticated) { if (!conn->authenticated) {
logger_log(conn->raop->logger, LOGGER_INFO, "authentication rejected (nonce mismatch) %s %s", logger_log(raop->logger, LOGGER_INFO, "authentication rejected (nonce mismatch) %s %s",
nonce_string, conn->raop->nonce); nonce_string, raop->nonce);
} }
} }
if (conn->authenticated && conn->raop->random_pw) { if (conn->authenticated && raop->random_pw) {
free (conn->raop->random_pw); free (raop->random_pw);
conn->raop->random_pw = NULL; raop->random_pw = NULL;
} }
if (conn->raop->nonce) { if (raop->nonce) {
free(conn->raop->nonce); free(raop->nonce);
conn->raop->nonce = NULL; raop->nonce = NULL;
} }
logger_log(conn->raop->logger, LOGGER_INFO, "Client authentication %s", (conn->authenticated ? "success" : "failure")); logger_log(raop->logger, LOGGER_INFO, "Client authentication %s", (conn->authenticated ? "success" : "failure"));
} }
if (!conn->authenticated) { if (!conn->authenticated) {
/* create a nonce */ /* create a nonce */
@@ -722,12 +728,12 @@ raop_handler_setup(raop_conn_t *conn,
int len = 16; int len = 16;
uint64_t now = raop_ntp_get_local_time(); uint64_t now = raop_ntp_get_local_time();
assert (!pairing_session_make_nonce(conn->session, &now, url, nonce, len)); assert (!pairing_session_make_nonce(conn->session, &now, url, nonce, len));
if (conn->raop->nonce) { if (raop->nonce) {
free(conn->raop->nonce); free(raop->nonce);
} }
conn->raop->nonce = utils_hex_to_string(nonce, len); raop->nonce = utils_hex_to_string(nonce, len);
char response_text[80] = "Digest realm=\"raop\", nonce=\""; char response_text[80] = "Digest realm=\"raop\", nonce=\"";
strncat(response_text, conn->raop->nonce, 80 - strlen(response_text) - 1); strncat(response_text, raop->nonce, 80 - strlen(response_text) - 1);
strncat(response_text, "\"", 80 - strlen(response_text) - 1); strncat(response_text, "\"", 80 - strlen(response_text) - 1);
http_response_init(response, "RTSP/1.0", 401, "Unauthorized"); http_response_init(response, "RTSP/1.0", 401, "Unauthorized");
http_response_add_header(response, "WWW-Authenticate", response_text); http_response_add_header(response, "WWW-Authenticate", response_text);
@@ -745,15 +751,15 @@ raop_handler_setup(raop_conn_t *conn,
plist_get_string_val(req_model_node, &model); plist_get_string_val(req_model_node, &model);
plist_t req_name_node = plist_dict_get_item(req_root_node, "name"); plist_t req_name_node = plist_dict_get_item(req_root_node, "name");
plist_get_string_val(req_name_node, &name); plist_get_string_val(req_name_node, &name);
if (conn->raop->callbacks.report_client_request) { if (raop->callbacks.report_client_request) {
conn->raop->callbacks.report_client_request(conn->raop->callbacks.cls, deviceID, model, name, &admit_client); raop->callbacks.report_client_request(raop->callbacks.cls, deviceID, model, name, &admit_client);
} }
if (admit_client && deviceID && name && conn->raop->callbacks.register_client) { if (admit_client && deviceID && name && raop->callbacks.register_client) {
char *client_device_id = NULL; char *client_device_id = NULL;
char *client_pk = NULL; /* encoded as null-terminated base64 string, must be freed*/ char *client_pk = NULL; /* encoded as null-terminated base64 string, must be freed*/
get_pairing_session_client_data(conn->session, &client_device_id, &client_pk); get_pairing_session_client_data(conn->session, &client_device_id, &client_pk);
if (client_pk && !strcmp(deviceID, client_device_id)) { if (client_pk && !strcmp(deviceID, client_device_id)) {
conn->raop->callbacks.register_client(conn->raop->callbacks.cls, client_device_id, client_pk, name); raop->callbacks.register_client(raop->callbacks.cls, client_device_id, client_pk, name);
free (client_pk); free (client_pk);
} }
} }
@@ -773,10 +779,10 @@ raop_handler_setup(raop_conn_t *conn,
plist_get_data_val(req_eiv_node, &eiv, &eiv_len); plist_get_data_val(req_eiv_node, &eiv, &eiv_len);
memcpy(aesiv, eiv, 16); memcpy(aesiv, eiv, 16);
free(eiv); free(eiv);
logger_log(conn->raop->logger, LOGGER_DEBUG, "eiv_len = %llu", eiv_len); logger_log(raop->logger, LOGGER_DEBUG, "eiv_len = %llu", eiv_len);
if (logger_debug) { if (logger_debug) {
char* str = utils_data_to_string(aesiv, 16, 16); char* str = utils_data_to_string(aesiv, 16, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aesiv (needed for AES-CBC audio decryption iv):\n%s", str); logger_log(raop->logger, LOGGER_DEBUG, "16 byte aesiv (needed for AES-CBC audio decryption iv):\n%s", str);
free(str); free(str);
} }
@@ -785,23 +791,23 @@ raop_handler_setup(raop_conn_t *conn,
plist_get_data_val(req_ekey_node, &ekey, &ekey_len); plist_get_data_val(req_ekey_node, &ekey, &ekey_len);
memcpy(eaeskey,ekey,72); memcpy(eaeskey,ekey,72);
free(ekey); free(ekey);
logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len); logger_log(raop->logger, LOGGER_DEBUG, "ekey_len = %llu", ekey_len);
// eaeskey is 72 bytes, aeskey is 16 bytes // eaeskey is 72 bytes, aeskey is 16 bytes
if (logger_debug) { if (logger_debug) {
char *str = utils_data_to_string((unsigned char *) eaeskey, ekey_len, 16); char *str = utils_data_to_string((unsigned char *) eaeskey, ekey_len, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "ekey:\n%s", str); logger_log(raop->logger, LOGGER_DEBUG, "ekey:\n%s", str);
free (str); free (str);
} }
int ret = fairplay_decrypt(conn->fairplay, (unsigned char*) eaeskey, aeskey); int ret = fairplay_decrypt(conn->fairplay, (unsigned char*) eaeskey, aeskey);
logger_log(conn->raop->logger, LOGGER_DEBUG, "fairplay_decrypt ret = %d", ret); logger_log(raop->logger, LOGGER_DEBUG, "fairplay_decrypt ret = %d", ret);
if (logger_debug) { if (logger_debug) {
char *str = utils_data_to_string(aeskey, 16, 16); char *str = utils_data_to_string(aeskey, 16, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey (fairplay-decrypted from ekey):\n%s", str); logger_log(raop->logger, LOGGER_DEBUG, "16 byte aeskey (fairplay-decrypted from ekey):\n%s", str);
free(str); free(str);
} }
const char *user_agent = http_request_get_header(request, "User-Agent"); const char *user_agent = http_request_get_header(request, "User-Agent");
logger_log(conn->raop->logger, LOGGER_INFO, "Client identified as User-Agent: %s", user_agent); logger_log(raop->logger, LOGGER_INFO, "Client identified as User-Agent: %s", user_agent);
bool old_protocol = false; bool old_protocol = false;
#ifdef OLD_PROTOCOL_CLIENT_USER_AGENT_LIST /* set in global.h */ #ifdef OLD_PROTOCOL_CLIENT_USER_AGENT_LIST /* set in global.h */
@@ -809,7 +815,7 @@ raop_handler_setup(raop_conn_t *conn,
if (strstr(user_agent, "AirMyPC")) old_protocol = true; //AirMyPC/7200 still uses old protocol: unlikely to change (?) if (strstr(user_agent, "AirMyPC")) old_protocol = true; //AirMyPC/7200 still uses old protocol: unlikely to change (?)
#endif #endif
if (old_protocol) { /* some windows AirPlay-client emulators use old AirPlay 1 protocol with unhashed AES key */ if (old_protocol) { /* some windows AirPlay-client emulators use old AirPlay 1 protocol with unhashed AES key */
logger_log(conn->raop->logger, LOGGER_INFO, "Client identifed as using old protocol (unhashed) AES audio key)"); logger_log(raop->logger, LOGGER_INFO, "Client identifed as using old protocol (unhashed) AES audio key)");
} else { } else {
unsigned char ecdh_secret[X25519_KEY_SIZE]; unsigned char ecdh_secret[X25519_KEY_SIZE];
if (pairing_get_ecdh_secret_key(conn->session, ecdh_secret)) { if (pairing_get_ecdh_secret_key(conn->session, ecdh_secret)) {
@@ -829,7 +835,7 @@ raop_handler_setup(raop_conn_t *conn,
if (logger_debug) { if (logger_debug) {
char *str = utils_data_to_string(ecdh_secret, X25519_KEY_SIZE, 16); char *str = utils_data_to_string(ecdh_secret, X25519_KEY_SIZE, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "32 byte shared ecdh_secret:\n%s", str); logger_log(raop->logger, LOGGER_DEBUG, "32 byte shared ecdh_secret:\n%s", str);
free(str); free(str);
} }
memcpy(eaeskey, aeskey, 16); memcpy(eaeskey, aeskey, 16);
@@ -841,7 +847,7 @@ raop_handler_setup(raop_conn_t *conn,
memcpy(aeskey, eaeskey, 16); memcpy(aeskey, eaeskey, 16);
if (logger_debug) { if (logger_debug) {
char *str = utils_data_to_string(aeskey, 16, 16); char *str = utils_data_to_string(aeskey, 16, 16);
logger_log(conn->raop->logger, LOGGER_DEBUG, "16 byte aeskey after sha-256 hash with ecdh_secret:\n%s", str); logger_log(raop->logger, LOGGER_DEBUG, "16 byte aeskey after sha-256 hash with ecdh_secret:\n%s", str);
free(str); free(str);
} }
} }
@@ -853,7 +859,7 @@ raop_handler_setup(raop_conn_t *conn,
uint8_t bool_val = 0; uint8_t bool_val = 0;
plist_get_bool_val(req_is_remote_control_only_node, &bool_val); plist_get_bool_val(req_is_remote_control_only_node, &bool_val);
if (bool_val) { if (bool_val) {
logger_log(conn->raop->logger, LOGGER_ERR, "Client specified AirPlay2 \"Remote Control\" protocol\n" logger_log(raop->logger, LOGGER_ERR, "Client specified AirPlay2 \"Remote Control\" protocol\n"
" Only AirPlay v1 protocol (using NTP and timing port) is supported"); " Only AirPlay v1 protocol (using NTP and timing port) is supported");
} }
} }
@@ -871,13 +877,13 @@ raop_handler_setup(raop_conn_t *conn,
time_protocol = TP_OTHER; time_protocol = TP_OTHER;
} }
if (time_protocol != NTP) { if (time_protocol != NTP) {
logger_log(conn->raop->logger, LOGGER_ERR, "Client specified timingProtocol=%s," logger_log(raop->logger, LOGGER_ERR, "Client specified timingProtocol=%s,"
" but timingProtocol= NTP is required here", timing_protocol); " but timingProtocol= NTP is required here", timing_protocol);
} }
plist_mem_free (timing_protocol); plist_mem_free (timing_protocol);
timing_protocol = NULL; timing_protocol = NULL;
} else { } else {
logger_log(conn->raop->logger, LOGGER_DEBUG, "Client did not specify timingProtocol," logger_log(raop->logger, LOGGER_DEBUG, "Client did not specify timingProtocol,"
" old protocol without offset will be used"); " old protocol without offset will be used");
time_protocol = TP_UNSPECIFIED; time_protocol = TP_UNSPECIFIED;
} }
@@ -887,12 +893,12 @@ raop_handler_setup(raop_conn_t *conn,
plist_get_uint_val(req_timing_port_node, &timing_rport); plist_get_uint_val(req_timing_port_node, &timing_rport);
} }
if (timing_rport) { if (timing_rport) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "timing_rport = %llu", timing_rport); logger_log(raop->logger, LOGGER_DEBUG, "timing_rport = %llu", timing_rport);
} else { } else {
logger_log(conn->raop->logger, LOGGER_ERR, "Client did not supply timing_rport," logger_log(raop->logger, LOGGER_ERR, "Client did not supply timing_rport,"
" may be using unsupported AirPlay2 \"Remote Control\" protocol"); " may be using unsupported AirPlay2 \"Remote Control\" protocol");
} }
unsigned short timing_lport = conn->raop->timing_lport; unsigned short timing_lport = raop->timing_lport;
conn->raop_ntp = NULL; conn->raop_ntp = NULL;
conn->raop_rtp = NULL; conn->raop_rtp = NULL;
@@ -901,18 +907,18 @@ raop_handler_setup(raop_conn_t *conn,
int len = utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, remote, (int) sizeof(remote)); int len = utils_ipaddress_to_string(conn->remotelen, conn->remote, conn->zone_id, remote, (int) sizeof(remote));
if (!len || len > sizeof(remote)) { if (!len || len > sizeof(remote)) {
char *str = utils_data_to_string(conn->remote, conn->remotelen, 16); char *str = utils_data_to_string(conn->remote, conn->remotelen, 16);
logger_log(conn->raop->logger, LOGGER_ERR, "failed to extract valid client ip address:\n" logger_log(raop->logger, LOGGER_ERR, "failed to extract valid client ip address:\n"
"*** UxPlay will be unable to send communications to client.\n" "*** UxPlay will be unable to send communications to client.\n"
"*** address length %d, zone_id %u address data:\n%sparser returned \"%s\"\n", "*** address length %d, zone_id %u address data:\n%sparser returned \"%s\"\n",
conn->remotelen, conn->zone_id, str, remote); conn->remotelen, conn->zone_id, str, remote);
free(str); free(str);
} }
conn->raop_ntp = raop_ntp_init(conn->raop->logger, &conn->raop->callbacks, remote, conn->raop_ntp = raop_ntp_init(raop->logger, &raop->callbacks, remote,
conn->remotelen, (unsigned short) timing_rport, &time_protocol); conn->remotelen, (unsigned short) timing_rport, &time_protocol);
raop_ntp_start(conn->raop_ntp, &timing_lport); raop_ntp_start(conn->raop_ntp, &timing_lport);
conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, conn->raop_rtp = raop_rtp_init(raop->logger, &raop->callbacks, conn->raop_ntp,
remote, conn->remotelen, aeskey, aesiv); remote, conn->remotelen, aeskey, aesiv);
conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_rtp_mirror = raop_rtp_mirror_init(raop->logger, &raop->callbacks,
conn->raop_ntp, remote, conn->remotelen, aeskey); conn->raop_ntp, remote, conn->remotelen, aeskey);
/* the event port is not used in mirror mode or audio mode */ /* the event port is not used in mirror mode or audio mode */
@@ -922,7 +928,7 @@ raop_handler_setup(raop_conn_t *conn,
plist_dict_set_item(res_root_node, "timingPort", res_timing_port_node); plist_dict_set_item(res_root_node, "timingPort", res_timing_port_node);
plist_dict_set_item(res_root_node, "eventPort", res_event_port_node); plist_dict_set_item(res_root_node, "eventPort", res_event_port_node);
logger_log(conn->raop->logger, LOGGER_DEBUG, "eport = %d, tport = %d", event_port, timing_lport); logger_log(raop->logger, LOGGER_DEBUG, "eport = %d, tport = %d", event_port, timing_lport);
} }
// Process stream setup requests // Process stream setup requests
@@ -936,24 +942,24 @@ raop_handler_setup(raop_conn_t *conn,
plist_t req_stream_type_node = plist_dict_get_item(req_stream_node, "type"); plist_t req_stream_type_node = plist_dict_get_item(req_stream_node, "type");
uint64_t type; uint64_t type;
plist_get_uint_val(req_stream_type_node, &type); plist_get_uint_val(req_stream_type_node, &type);
logger_log(conn->raop->logger, LOGGER_DEBUG, "type = %llu", type); logger_log(raop->logger, LOGGER_DEBUG, "type = %llu", type);
switch (type) { switch (type) {
case 110: { case 110: {
// Mirroring // Mirroring
unsigned short dport = conn->raop->mirror_data_lport; unsigned short dport = raop->mirror_data_lport;
plist_t stream_id_node = plist_dict_get_item(req_stream_node, "streamConnectionID"); plist_t stream_id_node = plist_dict_get_item(req_stream_node, "streamConnectionID");
uint64_t stream_connection_id = 0; uint64_t stream_connection_id = 0;
plist_get_uint_val(stream_id_node, &stream_connection_id); plist_get_uint_val(stream_id_node, &stream_connection_id);
logger_log(conn->raop->logger, LOGGER_DEBUG, "streamConnectionID (needed for AES-CTR video decryption" logger_log(raop->logger, LOGGER_DEBUG, "streamConnectionID (needed for AES-CTR video decryption"
" key and iv): %llu", stream_connection_id); " key and iv): %llu", stream_connection_id);
if (conn->raop_rtp_mirror) { if (conn->raop_rtp_mirror) {
raop_rtp_mirror_init_aes(conn->raop_rtp_mirror, &stream_connection_id); raop_rtp_mirror_init_aes(conn->raop_rtp_mirror, &stream_connection_id);
raop_rtp_mirror_start(conn->raop_rtp_mirror, &dport, conn->raop->clientFPSdata); raop_rtp_mirror_start(conn->raop_rtp_mirror, &dport, raop->clientFPSdata);
logger_log(conn->raop->logger, LOGGER_DEBUG, "Mirroring initialized successfully"); logger_log(raop->logger, LOGGER_DEBUG, "Mirroring initialized successfully");
} else { } else {
logger_log(conn->raop->logger, LOGGER_ERR, "Mirroring not initialized at SETUP, playing will fail!"); logger_log(raop->logger, LOGGER_ERR, "Mirroring not initialized at SETUP, playing will fail!");
http_response_set_disconnect(response, 1); http_response_set_disconnect(response, 1);
} }
@@ -968,7 +974,7 @@ raop_handler_setup(raop_conn_t *conn,
} }
case 96: { case 96: {
// Audio // Audio
unsigned short cport = conn->raop->control_lport, dport = conn->raop->data_lport; unsigned short cport = raop->control_lport, dport = raop->data_lport;
unsigned short remote_cport = 0; unsigned short remote_cport = 0;
unsigned char ct = 0; unsigned char ct = 0;
unsigned int sr = AUDIO_SAMPLE_RATE; /* all AirPlay audio formats supported so far have sample rate 44.1kHz */ unsigned int sr = AUDIO_SAMPLE_RATE; /* all AirPlay audio formats supported so far have sample rate 44.1kHz */
@@ -982,7 +988,7 @@ raop_handler_setup(raop_conn_t *conn,
plist_get_uint_val(req_stream_ct_node, &uint_val); plist_get_uint_val(req_stream_ct_node, &uint_val);
ct = (unsigned char) uint_val; ct = (unsigned char) uint_val;
if (conn->raop->callbacks.audio_get_format) { if (raop->callbacks.audio_get_format) {
/* get additional audio format parameters */ /* get additional audio format parameters */
uint64_t audioFormat = 0; uint64_t audioFormat = 0;
unsigned short spf = 0; unsigned short spf = 0;
@@ -1013,14 +1019,14 @@ raop_handler_setup(raop_conn_t *conn,
usingScreen = false; usingScreen = false;
} }
conn->raop->callbacks.audio_get_format(conn->raop->callbacks.cls, &ct, &spf, &usingScreen, &isMedia, &audioFormat); raop->callbacks.audio_get_format(raop->callbacks.cls, &ct, &spf, &usingScreen, &isMedia, &audioFormat);
} }
if (conn->raop_rtp) { if (conn->raop_rtp) {
raop_rtp_start_audio(conn->raop_rtp, &remote_cport, &cport, &dport, &ct, &sr); raop_rtp_start_audio(conn->raop_rtp, &remote_cport, &cport, &dport, &ct, &sr);
logger_log(conn->raop->logger, LOGGER_DEBUG, "RAOP initialized success"); logger_log(raop->logger, LOGGER_DEBUG, "RAOP initialized success");
} else { } else {
logger_log(conn->raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!"); logger_log(raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!");
http_response_set_disconnect(response, 1); http_response_set_disconnect(response, 1);
} }
@@ -1037,7 +1043,7 @@ raop_handler_setup(raop_conn_t *conn,
} }
default: default:
logger_log(conn->raop->logger, LOGGER_ERR, "SETUP tries to setup stream of unknown type %llu", type); logger_log(raop->logger, LOGGER_ERR, "SETUP tries to setup stream of unknown type %llu", type);
http_response_set_disconnect(response, 1); http_response_set_disconnect(response, 1);
break; break;
} }
@@ -1057,6 +1063,7 @@ raop_handler_get_parameter(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
raop_t *raop = conn->raop;
const char *content_type = NULL; const char *content_type = NULL;
const char *data = NULL; const char *data = NULL;
int datalen = 0; int datalen = 0;
@@ -1077,8 +1084,8 @@ raop_handler_get_parameter(raop_conn_t *conn,
/* This is a bit ugly, but seems to be how airport works too */ /* This is a bit ugly, but seems to be how airport works too */
if ((datalen - (current - data) >= 8) && !strncmp(current, "volume\r\n", 8)) { if ((datalen - (current - data) >= 8) && !strncmp(current, "volume\r\n", 8)) {
char volume[25] = "volume: 0.0\r\n"; char volume[25] = "volume: 0.0\r\n";
if (conn->raop->callbacks.audio_set_client_volume) { if (raop->callbacks.audio_set_client_volume) {
snprintf(volume, 25, "volume: %9.6f\r\n", conn->raop->callbacks.audio_set_client_volume(conn->raop->callbacks.cls)); snprintf(volume, 25, "volume: %9.6f\r\n", raop->callbacks.audio_set_client_volume(raop->callbacks.cls));
} }
http_response_add_header(response, "Content-Type", "text/parameters"); http_response_add_header(response, "Content-Type", "text/parameters");
*response_data = strdup(volume); *response_data = strdup(volume);
@@ -1096,7 +1103,7 @@ raop_handler_get_parameter(raop_conn_t *conn,
if ((datalen - (next - data) >= 2) && !strncmp(next, "\r\n", 2)) { if ((datalen - (next - data) >= 2) && !strncmp(next, "\r\n", 2)) {
if ((next - current) > 0) { if ((next - current) > 0) {
logger_log(conn->raop->logger, LOGGER_WARNING, logger_log(raop->logger, LOGGER_WARNING,
"Found an unknown parameter: %.*s", (next - current), current); "Found an unknown parameter: %.*s", (next - current), current);
} }
current = next + 2; current = next + 2;
@@ -1112,6 +1119,7 @@ raop_handler_set_parameter(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
raop_t *raop = conn->raop;
const char *content_type = NULL; const char *content_type = NULL;
const char *data = NULL; const char *data = NULL;
int datalen = 0; int datalen = 0;
@@ -1137,22 +1145,22 @@ raop_handler_set_parameter(raop_conn_t *conn,
raop_rtp_set_progress(conn->raop_rtp, start, curr, end); raop_rtp_set_progress(conn->raop_rtp, start, curr, end);
} }
} else if (!conn->raop_rtp) { } else if (!conn->raop_rtp) {
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER"); logger_log(raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER");
} }
free(datastr); free(datastr);
} else if (!strcmp(content_type, "image/jpeg") || !strcmp(content_type, "image/png")) { } else if (!strcmp(content_type, "image/jpeg") || !strcmp(content_type, "image/png")) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "Got image data of %d bytes", datalen); logger_log(raop->logger, LOGGER_DEBUG, "Got image data of %d bytes", datalen);
if (conn->raop_rtp) { if (conn->raop_rtp) {
raop_rtp_set_coverart(conn->raop_rtp, data, datalen); raop_rtp_set_coverart(conn->raop_rtp, data, datalen);
} else { } else {
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER coverart"); logger_log(raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER coverart");
} }
} else if (!strcmp(content_type, "application/x-dmap-tagged")) { } else if (!strcmp(content_type, "application/x-dmap-tagged")) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "Got metadata of %d bytes", datalen); logger_log(raop->logger, LOGGER_DEBUG, "Got metadata of %d bytes", datalen);
if (conn->raop_rtp) { if (conn->raop_rtp) {
raop_rtp_set_metadata(conn->raop_rtp, data, datalen); raop_rtp_set_metadata(conn->raop_rtp, data, datalen);
} else { } else {
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER metadata"); logger_log(raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER metadata");
} }
} }
} }
@@ -1162,6 +1170,7 @@ raop_handler_audiomode(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
raop_t *raop = conn->raop;
const char *data = NULL; const char *data = NULL;
char *audiomode = NULL; char *audiomode = NULL;
int data_len = 0; int data_len = 0;
@@ -1172,7 +1181,7 @@ raop_handler_audiomode(raop_conn_t *conn,
plist_get_string_val(req_audiomode_node, &audiomode); plist_get_string_val(req_audiomode_node, &audiomode);
/* not sure what should be done with this request: usually audioMode requested is "default" */ /* not sure what should be done with this request: usually audioMode requested is "default" */
int log_level = (strstr(audiomode, "default") ? LOGGER_DEBUG : LOGGER_INFO); int log_level = (strstr(audiomode, "default") ? LOGGER_DEBUG : LOGGER_INFO);
logger_log(conn->raop->logger, log_level, "Unhandled RTSP request \"audioMode: %s\"", audiomode); logger_log(raop->logger, log_level, "Unhandled RTSP request \"audioMode: %s\"", audiomode);
plist_mem_free(audiomode); plist_mem_free(audiomode);
plist_free(req_root_node); plist_free(req_root_node);
} }
@@ -1182,9 +1191,10 @@ raop_handler_feedback(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_feedback"); raop_t *raop = conn->raop;
logger_log(raop->logger, LOGGER_DEBUG, "raop_handler_feedback");
/* register receipt of client's "heartbeat" signal */ /* register receipt of client's "heartbeat" signal */
conn->raop->callbacks.conn_feedback(conn->raop->callbacks.cls); raop->callbacks.conn_feedback(raop->callbacks.cls);
} }
static void static void
@@ -1192,10 +1202,11 @@ raop_handler_record(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
raop_t *raop = conn->raop;
char audio_latency[12] = { '\0' }; char audio_latency[12] = { '\0' };
unsigned int ad = (unsigned int) (((uint64_t) conn->raop->audio_delay_micros) * AUDIO_SAMPLE_RATE / SECOND_IN_USECS); unsigned int ad = (unsigned int) (((uint64_t) raop->audio_delay_micros) * AUDIO_SAMPLE_RATE / SECOND_IN_USECS);
snprintf(audio_latency, sizeof(audio_latency), "%u", ad); snprintf(audio_latency, sizeof(audio_latency), "%u", ad);
logger_log(conn->raop->logger, LOGGER_DEBUG, "raop_handler_record"); logger_log(raop->logger, LOGGER_DEBUG, "raop_handler_record");
http_response_add_header(response, "Audio-Latency", audio_latency); http_response_add_header(response, "Audio-Latency", audio_latency);
http_response_add_header(response, "Audio-Jack-Status", "connected; type=analog"); http_response_add_header(response, "Audio-Jack-Status", "connected; type=analog");
} }
@@ -1205,12 +1216,13 @@ raop_handler_flush(raop_conn_t *conn,
http_request_t *request, http_response_t *response, http_request_t *request, http_response_t *response,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
raop_t *raop = conn->raop;
const char *rtpinfo = NULL; const char *rtpinfo = NULL;
int next_seq = -1; int next_seq = -1;
rtpinfo = http_request_get_header(request, "RTP-Info"); rtpinfo = http_request_get_header(request, "RTP-Info");
if (rtpinfo) { if (rtpinfo) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "Flush with RTP-Info: %s", rtpinfo); logger_log(raop->logger, LOGGER_DEBUG, "Flush with RTP-Info: %s", rtpinfo);
if (!strncmp(rtpinfo, "seq=", 4)) { if (!strncmp(rtpinfo, "seq=", 4)) {
next_seq = strtol(rtpinfo + 4, NULL, 10); next_seq = strtol(rtpinfo + 4, NULL, 10);
} }
@@ -1218,7 +1230,7 @@ raop_handler_flush(raop_conn_t *conn,
if (conn->raop_rtp) { if (conn->raop_rtp) {
raop_rtp_flush(conn->raop_rtp, next_seq); raop_rtp_flush(conn->raop_rtp, next_seq);
} else { } else {
logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at FLUSH"); logger_log(raop->logger, LOGGER_WARNING, "RAOP not initialized at FLUSH");
} }
} }
@@ -1228,6 +1240,7 @@ raop_handler_teardown(raop_conn_t *conn,
char **response_data, int *response_datalen) char **response_data, int *response_datalen)
{ {
/* get the teardown request type(s): (type 96, 110, or none) */ /* get the teardown request type(s): (type 96, 110, or none) */
raop_t *raop = conn->raop;
const char *data = NULL; const char *data = NULL;
int data_len = 0; int data_len = 0;
bool teardown_96 = false, teardown_110 = false; bool teardown_96 = false, teardown_110 = false;
@@ -1251,7 +1264,7 @@ raop_handler_teardown(raop_conn_t *conn,
} }
} }
plist_free(req_root_node); plist_free(req_root_node);
logger_log(conn->raop->logger, LOGGER_DEBUG, "TEARDOWN request, 96=%d, 110=%d", teardown_96, teardown_110); logger_log(raop->logger, LOGGER_DEBUG, "TEARDOWN request, 96=%d, 110=%d", teardown_96, teardown_110);
http_response_add_header(response, "Connection", "close"); http_response_add_header(response, "Connection", "close");
@@ -1260,12 +1273,12 @@ raop_handler_teardown(raop_conn_t *conn,
/* Stop our audio RTP session */ /* Stop our audio RTP session */
raop_rtp_stop(conn->raop_rtp); raop_rtp_stop(conn->raop_rtp);
/* stop any coverart rendering */ /* stop any coverart rendering */
if (conn->raop->callbacks.audio_stop_coverart_rendering) { if (raop->callbacks.audio_stop_coverart_rendering) {
conn->raop->callbacks.audio_stop_coverart_rendering(conn->raop->callbacks.cls); raop->callbacks.audio_stop_coverart_rendering(raop->callbacks.cls);
} }
} }
} else if (teardown_110) { } else if (teardown_110) {
conn->raop->callbacks.video_reset(conn->raop->callbacks.cls, false, false); raop->callbacks.video_reset(raop->callbacks.cls, false, false);
if (conn->raop_rtp_mirror) { if (conn->raop_rtp_mirror) {
/* Stop our video RTP session */ /* Stop our video RTP session */
raop_rtp_mirror_stop(conn->raop_rtp_mirror); raop_rtp_mirror_stop(conn->raop_rtp_mirror);
@@ -1281,12 +1294,12 @@ raop_handler_teardown(raop_conn_t *conn,
conn->raop_rtp_mirror = NULL; conn->raop_rtp_mirror = NULL;
} }
/* shut down any HLS connections */ /* shut down any HLS connections */
int hls_count = httpd_count_connection_type(conn->raop->httpd, CONNECTION_TYPE_HLS); int hls_count = httpd_count_connection_type(raop->httpd, CONNECTION_TYPE_HLS);
if (hls_count) { if (hls_count) {
conn->raop->callbacks.video_reset(conn->raop->callbacks.cls, true, false); raop->callbacks.video_reset(raop->callbacks.cls, true, false);
} }
} }
if (conn->raop->callbacks.conn_teardown) { if (raop->callbacks.conn_teardown) {
conn->raop->callbacks.conn_teardown(conn->raop->callbacks.cls, &teardown_96, &teardown_110); raop->callbacks.conn_teardown(raop->callbacks.cls, &teardown_96, &teardown_110);
} }
} }