fix unsafe strcmp (AirMyPC segfault) + timing fix for AirMyPC client

This commit is contained in:
F. Duncanh
2023-05-30 09:34:41 -04:00
parent d38f8690c2
commit a800fe213e
6 changed files with 83 additions and 20 deletions

View File

@@ -70,8 +70,9 @@ struct raop_callbacks_s {
void (*video_report_size)(void *cls, float *width_source, float *height_source, float *width, float *height);
};
typedef struct raop_callbacks_s raop_callbacks_t;
raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const unsigned char *remote_addr, int remote_addr_len, unsigned short timing_rport);
raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const unsigned char *remote_addr, int remote_addr_len,
unsigned short timing_rport, timing_protocol_t *time_protocol);
RAOP_API raop_t *raop_init(int max_clients, raop_callbacks_t *callbacks);
RAOP_API void raop_set_log_level(raop_t *raop, int level);
RAOP_API void raop_set_log_callback(raop_t *raop, raop_log_callback_t callback, void *cls);

View File

@@ -445,13 +445,28 @@ raop_handler_setup(raop_conn_t *conn,
" Only AirPlay v1 protocol (using NTP and timing port) is supported");
}
}
uint64_t string_len;
uint64_t string_len = 0;
const char *timing_protocol;
timing_protocol_t time_protocol;
plist_t req_timing_protocol_node = plist_dict_get_item(req_root_node, "timingProtocol");
timing_protocol = plist_get_string_ptr(req_timing_protocol_node, &string_len);
if (strcmp(timing_protocol, "NTP")) {
logger_log(conn->raop->logger, LOGGER_ERR, "Client specified timingProtocol=%s, but timingProtocol= NTP is required here", timing_protocol);
}
if (string_len) {
if (strncmp(timing_protocol, "NTP", string_len) == 0) {
time_protocol = NTP;
} else if (strncmp(timing_protocol, "None", string_len) == 0) {
time_protocol = TP_NONE;
} else {
time_protocol = TP_OTHER;
}
if (time_protocol != NTP) {
logger_log(conn->raop->logger, LOGGER_ERR, "Client specified timingProtocol=%s,"
" but timingProtocol= NTP is required here", timing_protocol);
}
} else {
logger_log(conn->raop->logger, LOGGER_DEBUG, "Client did not specify timingProtocol,"
" old protocol without offset will be used");
time_protocol = TP_UNSPECIFIED;
}
timing_protocol = NULL;
uint64_t timing_rport = 0;
plist_t req_timing_port_node = plist_dict_get_item(req_root_node, "timingPort");
@@ -461,14 +476,18 @@ raop_handler_setup(raop_conn_t *conn,
if (timing_rport) {
logger_log(conn->raop->logger, LOGGER_DEBUG, "timing_rport = %llu", timing_rport);
} else {
logger_log(conn->raop->logger, LOGGER_ERR, "Client did not supply timing_rport, may be using unsupported AirPlay2 \"Remote Control\" protocol");
logger_log(conn->raop->logger, LOGGER_ERR, "Client did not supply timing_rport,"
" may be using unsupported AirPlay2 \"Remote Control\" protocol");
}
unsigned short timing_lport = conn->raop->timing_lport;
conn->raop_ntp = raop_ntp_init(conn->raop->logger, &conn->raop->callbacks, conn->remote, conn->remotelen, timing_rport);
conn->raop_ntp = raop_ntp_init(conn->raop->logger, &conn->raop->callbacks, conn->remote,
conn->remotelen, (unsigned short) timing_rport, &time_protocol);
raop_ntp_start(conn->raop_ntp, &timing_lport, conn->raop->max_ntp_timeouts);
conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, conn->remote, conn->remotelen, aeskey, aesiv);
conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp, conn->remote, conn->remotelen, aeskey);
conn->raop_rtp = raop_rtp_init(conn->raop->logger, &conn->raop->callbacks, conn->raop_ntp,
conn->remote, conn->remotelen, aeskey, aesiv);
conn->raop_rtp_mirror = raop_rtp_mirror_init(conn->raop->logger, &conn->raop->callbacks,
conn->raop_ntp, conn->remote, conn->remotelen, aeskey);
plist_t res_event_port_node = plist_new_uint(conn->raop->port);
plist_t res_timing_port_node = plist_new_uint(timing_lport);
@@ -498,7 +517,8 @@ raop_handler_setup(raop_conn_t *conn,
plist_t stream_id_node = plist_dict_get_item(req_stream_node, "streamConnectionID");
uint64_t 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 key and iv): %llu", stream_connection_id);
logger_log(conn->raop->logger, LOGGER_DEBUG, "streamConnectionID (needed for AES-CTR video decryption"
" key and iv): %llu", stream_connection_id);
if (conn->raop_rtp_mirror) {
raop_rtp_init_mirror_aes(conn->raop_rtp_mirror, &stream_connection_id);

View File

@@ -91,6 +91,8 @@ struct raop_ntp_s {
// UDP socket
int tsock;
timing_protocol_t time_protocol;
};
@@ -140,7 +142,7 @@ raop_ntp_parse_remote_address(raop_ntp_t *raop_ntp, const unsigned char *remote_
}
raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const unsigned char *remote_addr,
int remote_addr_len, unsigned short timing_rport) {
int remote_addr_len, unsigned short timing_rport, timing_protocol_t *time_protocol) {
raop_ntp_t *raop_ntp;
assert(logger);
@@ -150,6 +152,7 @@ raop_ntp_t *raop_ntp_init(logger_t *logger, raop_callbacks_t *callbacks, const u
if (!raop_ntp) {
return NULL;
}
raop_ntp->time_protocol = *time_protocol;
raop_ntp->logger = logger;
memcpy(&raop_ntp->callbacks, callbacks, sizeof(raop_callbacks_t));
raop_ntp->timing_rport = timing_rport;
@@ -322,10 +325,10 @@ raop_ntp_thread(void *arg)
int64_t t0 = (int64_t) byteutils_get_ntp_timestamp(response, 8);
// Local time of the client when the NTP request packet arrives at the client
int64_t t1 = (int64_t) byteutils_get_ntp_timestamp(response, 16);
int64_t t1 = (int64_t) raop_remote_timestamp_to_nano_seconds(raop_ntp, byteutils_get_long_be(response, 16));
// Local time of the client when the response message leaves the client
int64_t t2 = (int64_t) byteutils_get_ntp_timestamp(response, 24);
int64_t t2 = (int64_t) raop_remote_timestamp_to_nano_seconds(raop_ntp, byteutils_get_long_be(response, 24));
if (logger_debug) {
char *str = utils_data_to_string(response, response_len, 16);
@@ -480,6 +483,12 @@ uint64_t raop_ntp_timestamp_to_nano_seconds(uint64_t ntp_timestamp, bool account
return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32);
}
uint64_t raop_remote_timestamp_to_nano_seconds(raop_ntp_t *raop_ntp, uint64_t timestamp) {
uint64_t seconds = ((timestamp >> 32) & 0xffffffff);
if (raop_ntp->time_protocol == NTP) seconds -= SECONDS_FROM_1900_TO_1970;
uint64_t fraction = (timestamp & 0xffffffff);
return (seconds * SECOND_IN_NSECS) + ((fraction * SECOND_IN_NSECS) >> 32);
}
/**
* Returns the current time in nano seconds according to the local wall clock.
* The system Unix time is used as the local wall clock.

View File

@@ -25,6 +25,7 @@
typedef struct raop_ntp_s raop_ntp_t;
typedef enum timing_protocol_e { NTP, TP_NONE, TP_OTHER, TP_UNSPECIFIED } timing_protocol_t;
void raop_ntp_start(raop_ntp_t *raop_ntp, unsigned short *timing_lport, int max_ntp_timeouts);
@@ -35,6 +36,7 @@ unsigned short raop_ntp_get_port(raop_ntp_t *raop_ntp);
void raop_ntp_destroy(raop_ntp_t *raop_rtp);
uint64_t raop_ntp_timestamp_to_nano_seconds(uint64_t ntp_timestamp, bool account_for_epoch_diff);
uint64_t raop_remote_timestamp_to_nano_seconds(raop_ntp_t *raop_ntp, uint64_t timestamp);
uint64_t raop_ntp_get_local_time(raop_ntp_t *raop_ntp);
uint64_t raop_ntp_get_remote_time(raop_ntp_t *raop_ntp);

View File

@@ -557,7 +557,7 @@ raop_rtp_thread_udp(void *arg)
have_synced = true;
}
uint64_t sync_ntp_raw = byteutils_get_long_be(packet, 8);
uint64_t sync_ntp_remote = raop_ntp_timestamp_to_nano_seconds(sync_ntp_raw, true);
uint64_t sync_ntp_remote = raop_remote_timestamp_to_nano_seconds(raop_rtp->ntp, sync_ntp_raw);
if (logger_debug) {
uint64_t sync_ntp_local = raop_ntp_convert_remote_time(raop_rtp->ntp, sync_ntp_remote);
char *str = utils_data_to_string(packet, packetlen, 20);

View File

@@ -321,11 +321,13 @@ raop_rtp_mirror_thread(void *arg)
/* packet[4] + packet[5] identify the payload type: values seen are: *
* 0x00 0x00: encrypted packet containing a non-IDR type 1 VCL NAL unit *
* 0x00 0x10: encrypted packet containing an IDR type 5 VCL NAL unit *
* 0x01 0x00 unencrypted packet containing a type 7 SPS NAL + a type 8 PPS NAL unit *
* 0x01 0x00: unencrypted packet containing a type 7 SPS NAL + a type 8 PPS NAL unit *
* 0x02 0x00: unencryted packet (old protocol) no payload, sent once every second *
* 0x05 0x00 unencrypted packet with a "streaming report", sent once per second. */
/* packet[6] + packet[7] may list a payload "option": values seen are: *
* 0x00 0x00 : encrypted and "streaming report" packets *
* 0x1e 0x00 : old protocol (seen in AirMyPC) no-payload once-per-second packets *
* 0x16 0x01 : seen in most unencrypted SPS+PPS packets *
* 0x56 0x01 : occasionally seen in unencrypted SPS+PPS packets (why different?) */
@@ -400,14 +402,20 @@ raop_rtp_mirror_thread(void *arg)
* that has not yet been sent. This will trigger prepending it to the current NAL, and the prepend_sps_pps
* flag will be set to false after it has been prepended. */
if (prepend_sps_pps & (ntp_timestamp_raw != ntp_timestamp_nal)) {
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
"raop_rtp_mirror: prepended sps_pps timestamp does not match timestamp of "
"video payload\n%llu\n%llu , discarding", ntp_timestamp_raw, ntp_timestamp_nal);
free (sps_pps);
sps_pps = NULL;
prepend_sps_pps = false;
}
if (prepend_sps_pps) {
assert(sps_pps);
payload_out = (unsigned char*) malloc(payload_size + sps_pps_len);
payload_decrypted = payload_out + sps_pps_len;
if (ntp_timestamp_raw != ntp_timestamp_nal) {
logger_log(raop_rtp_mirror->logger, LOGGER_WARNING,
"raop_rtp_mirror: prepended sps_pps timestamp does not match timestamp of "
"video payload\n%llu\n%llu", ntp_timestamp_raw, ntp_timestamp_nal);
}
memcpy(payload_out, sps_pps, sps_pps_len);
free (sps_pps);
@@ -441,6 +449,7 @@ raop_rtp_mirror_thread(void *arg)
int nalu_type = payload_decrypted[nalu_size] & 0x1f;
int ref_idc = (payload_decrypted[nalu_size] >> 5);
switch (nalu_type) {
case 14: /* Prefix NALu , seen before all VCL Nalu's in AirMyPc */
case 5: /*IDR, slice_layer_without_partitioning */
case 1: /*non-IDR, slice_layer_without_partitioning */
break;
@@ -461,6 +470,24 @@ raop_rtp_mirror_thread(void *arg)
free(str);
}
break;
case 7:
if (logger_debug) {
char *str = utils_data_to_string(payload_decrypted + nalu_size, nc_len, 16);
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror SPS NAL size = %d", nc_len);
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
"raop_rtp_mirror h264 Sequence Parameter Set:\n%s", str);
free(str);
}
break;
case 8:
if (logger_debug) {
char *str = utils_data_to_string(payload_decrypted + nalu_size, nc_len, 16);
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "raop_rtp_mirror PPS NAL size = %d", nc_len);
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG,
"raop_rtp_mirror h264 Picture Parameter Set :\n%s", str);
free(str);
}
break;
default:
logger_log(raop_rtp_mirror->logger, LOGGER_INFO,
"unexpected non-VCL NAL unit: nalu_type = %d, ref_idc = %d, nalu_size = %d,"
@@ -580,6 +607,10 @@ raop_rtp_mirror_thread(void *arg)
// memcpy(h264.picture_parameter_set, picture_parameter_set, pps_size);
break;
case 0x02:
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "\nReceived old-protocol once-per-second packet from client:"
" payload_size %d header %s ts_raw = %llu", payload_size, packet_description, ntp_timestamp_raw);
/* "old protocol" (used by AirMyPC), rest of 128-byte packet is empty */
case 0x05:
logger_log(raop_rtp_mirror->logger, LOGGER_DEBUG, "\nReceived video streaming performance info packet from client:"
" payload_size %d header %s ts_raw = %llu", payload_size, packet_description, ntp_timestamp_raw);
@@ -615,7 +646,7 @@ raop_rtp_mirror_thread(void *arg)
break;
default:
logger_log(raop_rtp_mirror->logger, LOGGER_WARNING, "\nReceived unexpected TCP packet from client, "
"size %d, %s ts_raw = raw%llu", payload_size, packet_description, ntp_timestamp_raw);
"size %d, %s ts_raw = %llu", payload_size, packet_description, ntp_timestamp_raw);
break;
}