From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Tue, 2 Dec 2025 13:02:15 +0000 (+0000) Subject: Move debug stuff into utilites folder. Move structured_buffer stuff into its own... X-Git-Tag: 5.0-post-dev~54 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2de1ecf0dd3a32d26d71d3461080f4f5065f0052;p=thirdparty%2Fshairport-sync.git Move debug stuff into utilites folder. Move structured_buffer stuff into its own files. Duplicate rtp_event_receiver to ap2_event_handler and ap2_rc_event_handler. --- diff --git a/ap2_event_receiver.c b/ap2_event_receiver.c new file mode 100644 index 00000000..3dffc0de --- /dev/null +++ b/ap2_event_receiver.c @@ -0,0 +1,157 @@ +/* + * Apple AirPlay 2 Event Receiver. This file is part of Shairport Sync. + * Copyright (c) Mike Brady 2014--2025 + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "ap2_event_receiver.h" +#include "player.h" +#include "common.h" +#include "rtsp.h" +#include "utilities/structured_buffer.h" + +void ap2_event_receiver_cleanup_handler(void *arg) { + rtsp_conn_info *conn = (rtsp_conn_info *)arg; + debug(2, "Connection %d: AP2 Event Receiver Cleanup.", conn->connection_number); +} + +void *ap2_event_receiver(void *arg) { + // #include + // debug(1, "rtp_event_receiver PID %d", syscall(SYS_gettid)); + rtsp_conn_info *conn = (rtsp_conn_info *)arg; + if (conn->airplay_stream_category == remote_control_stream) + debug(2, "Connection %d (RC): AP2 Event Receiver started", conn->connection_number); + else + debug(2, "Connection %d: AP2 Event Receiver started", conn->connection_number); + + structured_buffer *sbuf = sbuf_new(4096); + if (sbuf != NULL) { + pthread_cleanup_push(sbuf_cleanup, sbuf); + + pthread_cleanup_push(ap2_event_receiver_cleanup_handler, arg); + + // listen(conn->event_socket, 5); // this is now done in the handle_setup_2 code + + uint8_t packet[4096]; + ssize_t nread; + SOCKADDR remote_addr; + memset(&remote_addr, 0, sizeof(remote_addr)); + socklen_t addr_size = sizeof(remote_addr); + + int fd = accept(conn->event_socket, (struct sockaddr *)&remote_addr, &addr_size); + debug(2, + "Connection %d: rtp_event_receiver accepted a connection on socket %d and moved to a new " + "socket %d.", + conn->connection_number, conn->event_socket, fd); + intptr_t pfd = fd; + pthread_cleanup_push(socket_cleanup, (void *)pfd); + int finished = 0; + do { + + plist_t value_plist = generateInfoPlist(conn); + if (value_plist != NULL) { + void *txtData = NULL; + size_t txtDataLength = 0; + generateTxtDataValueInfo(conn, &txtData, &txtDataLength); + plist_dict_set_item(value_plist, "txtAirPlay", plist_new_data(txtData, txtDataLength)); + free(txtData); + plist_t update_info_plist = plist_new_dict(); + if (update_info_plist != NULL) { + plist_dict_set_item(update_info_plist, "type", plist_new_string("updateInfo")); + plist_dict_set_item(update_info_plist, "value", value_plist); + char *plistString = NULL; + uint32_t plistStringLength = 0; + plist_to_bin(update_info_plist, &plistString, &plistStringLength); + if (plistString != NULL) { + char *plist_as_string = plist_as_xml_text(update_info_plist); + if (plist_as_string != NULL) { + debug(3, "Plist is: \"%s\".", plist_as_string); + free(plist_as_string); + } + sbuf_printf(sbuf, "POST /command RTSP/1.0\r\nContent-Length: %u\r\n", + plistStringLength); + sbuf_printf(sbuf, "Content-Type: application/x-apple-binary-plist\r\n\r\n"); + sbuf_append(sbuf, plistString, plistStringLength); + + free(plistString); // should be plist_to_bin_free, but it's not defined in older + // libraries + char *b = 0; + size_t l = 0; + sbuf_buf_and_length(sbuf, &b, &l); + ssize_t wres = + write_encrypted(fd, &conn->ap2_pairing_context.event_cipher_bundle, b, l); + if ((wres == -1) || ((size_t)wres != l)) + debug(1, "Encrypted write error"); + + sbuf_clear(sbuf); + } else { + debug(1, "plist string not created!"); + } + plist_free(update_info_plist); + } else { + debug(1, "Could not build an updateInfo plist"); + } + // plist_free(value_plist); + } else { + debug(1, "Could not build an value plist"); + } + + while (finished == 0) { + nread = read_encrypted(fd, &conn->ap2_pairing_context.event_cipher_bundle, packet, + sizeof(packet)); + + // nread = recv(fd, packet, sizeof(packet), 0); + + if (nread < 0) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug( + 1, + "Connection %d: error in ap2 rtp_event_receiver %d: \"%s\". Could not recv a packet.", + conn->connection_number, errno, errorstring); + // if ((config.diagnostic_drop_packet_fraction == 0.0) || + // (drand48() > config.diagnostic_drop_packet_fraction)) { + } else if (nread > 0) { + + // ssize_t plen = nread; + packet[nread] = '\0'; + debug(3, "Connection %d: Packet Received on Event Port with contents: \"%s\".", + conn->connection_number, packet); + } else { + debug(1, "Connection %d: Event Port connection closed by client", + conn->connection_number); + finished = 1; + } + } + + } while (finished == 0); + pthread_cleanup_pop(1); // close the socket + pthread_cleanup_pop(1); // do the cleanup + pthread_cleanup_pop(1); // delete the structured buffer + debug(2, "Connection %d: AP2 Event Receiver RTP thread \"normal\" exit.", + conn->connection_number); + } else { + debug(1, "Could not allocate a structured buffer!"); + } + pthread_exit(NULL); +} diff --git a/ap2_event_receiver.h b/ap2_event_receiver.h new file mode 100644 index 00000000..ae4000b8 --- /dev/null +++ b/ap2_event_receiver.h @@ -0,0 +1,6 @@ +#ifndef _AP2_EVENT_RECEIVER_H +#define _AP2_EVENT_RECEIVER_H + +void *ap2_event_receiver(void *arg); + +#endif // _AP2_EVENT_RECEIVER_H diff --git a/ap2_rc_event_receiver.c b/ap2_rc_event_receiver.c new file mode 100644 index 00000000..edb842a9 --- /dev/null +++ b/ap2_rc_event_receiver.c @@ -0,0 +1,157 @@ +/* + * Apple AirPlay 2 Remote Control (RC) Event Receiver. This file is part of Shairport Sync. + * Copyright (c) Mike Brady 2014--2025 + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "ap2_rc_event_receiver.h" +#include "player.h" +#include "common.h" +#include "rtsp.h" +#include "utilities/structured_buffer.h" + +void ap2_rc_event_receiver_cleanup_handler(void *arg) { + rtsp_conn_info *conn = (rtsp_conn_info *)arg; + debug(2, "Connection %d: AP2 Event Receiver Cleanup.", conn->connection_number); +} + +void *ap2_rc_event_receiver(void *arg) { + // #include + // debug(1, "rtp_event_receiver PID %d", syscall(SYS_gettid)); + rtsp_conn_info *conn = (rtsp_conn_info *)arg; + if (conn->airplay_stream_category == remote_control_stream) + debug(2, "Connection %d (RC): AP2 RC Event Receiver started", conn->connection_number); + else + debug(2, "Connection %d: AP2 RC Event Receiver started", conn->connection_number); + + structured_buffer *sbuf = sbuf_new(4096); + if (sbuf != NULL) { + pthread_cleanup_push(sbuf_cleanup, sbuf); + + pthread_cleanup_push(ap2_rc_event_receiver_cleanup_handler, arg); + + // listen(conn->event_socket, 5); // this is now done in the handle_setup_2 code + + uint8_t packet[4096]; + ssize_t nread; + SOCKADDR remote_addr; + memset(&remote_addr, 0, sizeof(remote_addr)); + socklen_t addr_size = sizeof(remote_addr); + + int fd = accept(conn->event_socket, (struct sockaddr *)&remote_addr, &addr_size); + debug(2, + "Connection %d: ap2_rc_event_receiver accepted a connection on socket %d and moved to a new " + "socket %d.", + conn->connection_number, conn->event_socket, fd); + intptr_t pfd = fd; + pthread_cleanup_push(socket_cleanup, (void *)pfd); + int finished = 0; + do { + + plist_t value_plist = generateInfoPlist(conn); + if (value_plist != NULL) { + void *txtData = NULL; + size_t txtDataLength = 0; + generateTxtDataValueInfo(conn, &txtData, &txtDataLength); + plist_dict_set_item(value_plist, "txtAirPlay", plist_new_data(txtData, txtDataLength)); + free(txtData); + plist_t update_info_plist = plist_new_dict(); + if (update_info_plist != NULL) { + plist_dict_set_item(update_info_plist, "type", plist_new_string("updateInfo")); + plist_dict_set_item(update_info_plist, "value", value_plist); + char *plistString = NULL; + uint32_t plistStringLength = 0; + plist_to_bin(update_info_plist, &plistString, &plistStringLength); + if (plistString != NULL) { + char *plist_as_string = plist_as_xml_text(update_info_plist); + if (plist_as_string != NULL) { + debug(3, "Plist is: \"%s\".", plist_as_string); + free(plist_as_string); + } + sbuf_printf(sbuf, "POST /command RTSP/1.0\r\nContent-Length: %u\r\n", + plistStringLength); + sbuf_printf(sbuf, "Content-Type: application/x-apple-binary-plist\r\n\r\n"); + sbuf_append(sbuf, plistString, plistStringLength); + + free(plistString); // should be plist_to_bin_free, but it's not defined in older + // libraries + char *b = 0; + size_t l = 0; + sbuf_buf_and_length(sbuf, &b, &l); + ssize_t wres = + write_encrypted(fd, &conn->ap2_pairing_context.event_cipher_bundle, b, l); + if ((wres == -1) || ((size_t)wres != l)) + debug(1, "Encrypted write error"); + + sbuf_clear(sbuf); + } else { + debug(1, "plist string not created!"); + } + plist_free(update_info_plist); + } else { + debug(1, "Could not build an updateInfo plist"); + } + // plist_free(value_plist); + } else { + debug(1, "Could not build an value plist"); + } + + while (finished == 0) { + nread = read_encrypted(fd, &conn->ap2_pairing_context.event_cipher_bundle, packet, + sizeof(packet)); + + // nread = recv(fd, packet, sizeof(packet), 0); + + if (nread < 0) { + char errorstring[1024]; + strerror_r(errno, (char *)errorstring, sizeof(errorstring)); + debug( + 1, + "Connection %d: error in ap2_rc_event_receiver %d: \"%s\". Could not recv a packet.", + conn->connection_number, errno, errorstring); + // if ((config.diagnostic_drop_packet_fraction == 0.0) || + // (drand48() > config.diagnostic_drop_packet_fraction)) { + } else if (nread > 0) { + + // ssize_t plen = nread; + packet[nread] = '\0'; + debug(3, "Connection %d: ap2_rc_event_receiver Packet Received on Event Port with contents: \"%s\".", + conn->connection_number, packet); + } else { + debug(1, "Connection %d: ap2_rc_event_receiver Event Port connection closed by client", + conn->connection_number); + finished = 1; + } + } + + } while (finished == 0); + pthread_cleanup_pop(1); // close the socket + pthread_cleanup_pop(1); // do the cleanup + pthread_cleanup_pop(1); // delete the structured buffer + debug(2, "Connection %d: AP2 ap2_rc_event_receiver \"normal\" exit.", + conn->connection_number); + } else { + debug(1, "Could not allocate a structured buffer!"); + } + pthread_exit(NULL); +} diff --git a/ap2_rc_event_receiver.h b/ap2_rc_event_receiver.h new file mode 100644 index 00000000..200e785b --- /dev/null +++ b/ap2_rc_event_receiver.h @@ -0,0 +1,8 @@ +// RC means Remote Control + +#ifndef _AP2_RC_EVENT_RECEIVER_H +#define _AP2_RC_EVENT_RECEIVER_H + +void *ap2_rc_event_receiver(void *arg); + +#endif // _AP2_RC_EVENT_RECEIVER_H diff --git a/debug.c b/utilities/debug.c similarity index 100% rename from debug.c rename to utilities/debug.c diff --git a/debug.h b/utilities/debug.h similarity index 100% rename from debug.h rename to utilities/debug.h diff --git a/utilities/structured_buffer.c b/utilities/structured_buffer.c new file mode 100644 index 00000000..668fdf40 --- /dev/null +++ b/utilities/structured_buffer.c @@ -0,0 +1,123 @@ +/* + * Structured Buffer. This file is part of Shairport Sync + * Copyright (c) Mike Brady 2025 + + * Modifications, including those associated with audio synchronization, multithreading and + * metadata handling copyright (c) Mike Brady 2014--2025 + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + + +typedef struct { + char *buf; + size_t buf_size; + size_t buf_pos; +} structured_buffer; + +structured_buffer *sbuf_new(size_t size) { + structured_buffer *sbuf = (structured_buffer *)malloc(sizeof(structured_buffer)); + if (sbuf != NULL) { + memset(sbuf, 0, sizeof(structured_buffer)); + char *buf = malloc(size + 1); // extra space for a possible NULL + if (buf == NULL) { + free(sbuf); + sbuf = NULL; + } else { + sbuf->buf_size = size; + sbuf->buf = buf; + } + } + return sbuf; +} + +int sbuf_clear(structured_buffer *sbuf) { + int response = -1; + if ((sbuf != NULL) && (sbuf->buf != NULL)) { + sbuf->buf_pos = 0; + response = 0; + } + return response; +} + +void sbuf_free(structured_buffer *sbuf) { + if (sbuf != NULL) { + if (sbuf->buf != NULL) + free(sbuf->buf); + free(sbuf); + } +} + +void sbuf_cleanup(void *arg) { + structured_buffer *sbuf = (structured_buffer *)arg; + debug(3, "structured_buffer cleanup"); + sbuf_free(sbuf); +} + +int sbuf_printf(structured_buffer *sbuf, const char *format, ...) { + int response = -1; + if ((sbuf != NULL) && (sbuf->buf != NULL)) { + char *p = sbuf->buf + sbuf->buf_pos; + va_list args; + va_start(args, format); + vsnprintf(p, sbuf->buf_size - sbuf->buf_pos, format, args); + sbuf->buf_pos = sbuf->buf_pos + strlen(p); + response = strlen(p); + va_end(args); + } + return response; +} + +int sbuf_append(structured_buffer *sbuf, char *plistString, uint32_t plistStringLength) { + int response = -1; + if ((sbuf != NULL) && (sbuf->buf != NULL) && (plistString != NULL)) { + if (plistStringLength == 0) { + response = 0; + } else { + if (plistStringLength < (sbuf->buf_size - sbuf->buf_pos)) { + memcpy(sbuf->buf + sbuf->buf_pos, plistString, plistStringLength); + sbuf->buf_pos = sbuf->buf_pos + plistStringLength; + response = 0; + } else { + debug(1, "plist too large -- omitted"); + } + } + } + return response; +} + +int sbuf_buf_and_length(structured_buffer *sbuf, char **b, size_t *l) { + int response = 0; + if ((sbuf != NULL) && (sbuf->buf != NULL)) { + *b = sbuf->buf; + *l = sbuf->buf_pos; + } else { + response = -1; + } + return response; +} diff --git a/utilities/structured_buffer.h b/utilities/structured_buffer.h new file mode 100644 index 00000000..ac3dd04e --- /dev/null +++ b/utilities/structured_buffer.h @@ -0,0 +1,18 @@ +#ifndef _STRUCTURED_BUFFER_H +#define _STRUCTURED_BUFFER_H + +typedef struct { + char *buf; + size_t buf_size; + size_t buf_pos; +} structured_buffer; + +structured_buffer *sbuf_new(size_t size); +int sbuf_clear(structured_buffer *sbuf); +void sbuf_free(structured_buffer *sbuf); +void sbuf_cleanup(void *arg); +int sbuf_printf(structured_buffer *sbuf, const char *format, ...); +int sbuf_append(structured_buffer *sbuf, char *plistString, uint32_t plistStringLength); +int sbuf_buf_and_length(structured_buffer *sbuf, char **b, size_t *l); + +#endif // _STRUCTURED_BUFFER_H