static void conn_request(void *ptr, http_request_t *request, http_response_t **response) { const char realm[] = "airplay"; raop_conn_t *conn = ptr; raop_t *raop = conn->raop; http_response_t *res; const char *method; const char *cseq; const char *challenge; int require_auth = 0; method = http_request_get_method(request); cseq = http_request_get_header(request, "CSeq"); if (!method || !cseq) { return; } res = http_response_init("RTSP/1.0", 200, "OK"); /* We need authorization for everything else than OPTIONS request */ if (strcmp(method, "OPTIONS") != 0 && strlen(raop->password)) { const char *authorization; authorization = http_request_get_header(request, "Authorization"); if (authorization) { logger_log(conn->raop->logger, LOGGER_DEBUG, "Our nonce: %s", conn->nonce); logger_log(conn->raop->logger, LOGGER_DEBUG, "Authorization: %s", authorization); } if (!digest_is_valid(realm, raop->password, conn->nonce, method, http_request_get_url(request), authorization)) { char *authstr; int authstrlen; /* Allocate the authenticate string */ authstrlen = sizeof("Digest realm=\"\", nonce=\"\"") + sizeof(realm) + sizeof(conn->nonce) + 1; authstr = malloc(authstrlen); /* Concatenate the authenticate string */ memset(authstr, 0, authstrlen); strcat(authstr, "Digest realm=\""); strcat(authstr, realm); strcat(authstr, "\", nonce=\""); strcat(authstr, conn->nonce); strcat(authstr, "\""); /* Construct a new response */ require_auth = 1; http_response_destroy(res); res = http_response_init("RTSP/1.0", 401, "Unauthorized"); http_response_add_header(res, "WWW-Authenticate", authstr); free(authstr); logger_log(conn->raop->logger, LOGGER_DEBUG, "Authentication unsuccessful, sending Unauthorized"); } else { logger_log(conn->raop->logger, LOGGER_DEBUG, "Authentication successful!"); } } http_response_add_header(res, "CSeq", cseq); http_response_add_header(res, "Apple-Jack-Status", "connected; type=analog"); challenge = http_request_get_header(request, "Apple-Challenge"); if (!require_auth && challenge) { char signature[MAX_SIGNATURE_LEN]; memset(signature, 0, sizeof(signature)); rsakey_sign(raop->rsakey, signature, sizeof(signature), challenge, conn->local, conn->locallen, raop->hwaddr, raop->hwaddrlen); http_response_add_header(res, "Apple-Response", signature); logger_log(conn->raop->logger, LOGGER_DEBUG, "Got challenge: %s", challenge); logger_log(conn->raop->logger, LOGGER_DEBUG, "Got response: %s", signature); } if (require_auth) { /* Do nothing in case of authentication request */ } else if (!strcmp(method, "OPTIONS")) { http_response_add_header(res, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER"); } else if (!strcmp(method, "ANNOUNCE")) { const char *data; int datalen; unsigned char aeskey[16]; unsigned char aesiv[16]; int aeskeylen, aesivlen; data = http_request_get_data(request, &datalen); if (data) { sdp_t *sdp; const char *remotestr, *rtpmapstr, *fmtpstr, *aeskeystr, *aesivstr; sdp = sdp_init(data, datalen); remotestr = sdp_get_connection(sdp); rtpmapstr = sdp_get_rtpmap(sdp); fmtpstr = sdp_get_fmtp(sdp); aeskeystr = sdp_get_rsaaeskey(sdp); aesivstr = sdp_get_aesiv(sdp); logger_log(conn->raop->logger, LOGGER_DEBUG, "connection: %s", remotestr); logger_log(conn->raop->logger, LOGGER_DEBUG, "rtpmap: %s", rtpmapstr); logger_log(conn->raop->logger, LOGGER_DEBUG, "fmtp: %s", fmtpstr); logger_log(conn->raop->logger, LOGGER_DEBUG, "rsaaeskey: %s", aeskeystr); logger_log(conn->raop->logger, LOGGER_DEBUG, "aesiv: %s", aesivstr); aeskeylen = rsakey_decrypt(raop->rsakey, aeskey, sizeof(aeskey), aeskeystr); aesivlen = rsakey_parseiv(raop->rsakey, aesiv, sizeof(aesiv), aesivstr); logger_log(conn->raop->logger, LOGGER_DEBUG, "aeskeylen: %d", aeskeylen); logger_log(conn->raop->logger, LOGGER_DEBUG, "aesivlen: %d", aesivlen); if (conn->raop_rtp) { /* This should never happen */ raop_rtp_destroy(conn->raop_rtp); conn->raop_rtp = NULL; } conn->raop_rtp = raop_rtp_init(raop->logger, &raop->callbacks, remotestr, rtpmapstr, fmtpstr, aeskey, aesiv); if (!conn->raop_rtp) { logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing the audio decoder"); http_response_set_disconnect(res, 1); } sdp_destroy(sdp); } } else if (!strcmp(method, "SETUP")) { unsigned short remote_cport=0, remote_tport=0; unsigned short cport=0, tport=0, dport=0; const char *transport; char buffer[1024]; int use_udp; const char *dacp_id; const char *active_remote_header; dacp_id = http_request_get_header(request, "DACP-ID"); active_remote_header = http_request_get_header(request, "Active-Remote"); if (dacp_id && active_remote_header) { logger_log(conn->raop->logger, LOGGER_DEBUG, "DACP-ID: %s", dacp_id); logger_log(conn->raop->logger, LOGGER_DEBUG, "Active-Remote: %s", active_remote_header); if (conn->raop_rtp) { raop_rtp_remote_control_id(conn->raop_rtp, dacp_id, active_remote_header); } } transport = http_request_get_header(request, "Transport"); assert(transport); logger_log(conn->raop->logger, LOGGER_INFO, "Transport: %s", transport); use_udp = strncmp(transport, "RTP/AVP/TCP", 11); if (use_udp) { char *original, *current, *tmpstr; current = original = strdup(transport); if (original) { while ((tmpstr = utils_strsep(¤t, ";")) != NULL) { unsigned short value; int ret; ret = sscanf(tmpstr, "control_port=%hu", &value); if (ret == 1) { logger_log(conn->raop->logger, LOGGER_DEBUG, "Found remote control port: %hu", value); remote_cport = value; } ret = sscanf(tmpstr, "timing_port=%hu", &value); if (ret == 1) { logger_log(conn->raop->logger, LOGGER_DEBUG, "Found remote timing port: %hu", value); remote_tport = value; } } } free(original); } if (conn->raop_rtp) { raop_rtp_start(conn->raop_rtp, use_udp, remote_cport, remote_tport, &cport, &tport, &dport); } else { logger_log(conn->raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!"); http_response_set_disconnect(res, 1); } memset(buffer, 0, sizeof(buffer)); if (use_udp) { snprintf(buffer, sizeof(buffer)-1, "RTP/AVP/UDP;unicast;mode=record;timing_port=%hu;events;control_port=%hu;server_port=%hu", tport, cport, dport); } else { snprintf(buffer, sizeof(buffer)-1, "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record;server_port=%u", dport); } logger_log(conn->raop->logger, LOGGER_INFO, "Responding with %s", buffer); http_response_add_header(res, "Transport", buffer); http_response_add_header(res, "Session", "DEADBEEF"); } else if (!strcmp(method, "SET_PARAMETER")) { const char *content_type; const char *data; int datalen; content_type = http_request_get_header(request, "Content-Type"); data = http_request_get_data(request, &datalen); if (!strcmp(content_type, "text/parameters")) { char *datastr; datastr = calloc(1, datalen+1); if (data && datastr && conn->raop_rtp) { memcpy(datastr, data, datalen); if (!strncmp(datastr, "volume: ", 8)) { float vol = 0.0; sscanf(datastr+8, "%f", &vol); raop_rtp_set_volume(conn->raop_rtp, vol); } else if (!strncmp(datastr, "progress: ", 10)) { unsigned int start, curr, end; sscanf(datastr+10, "%u/%u/%u", &start, &curr, &end); raop_rtp_set_progress(conn->raop_rtp, start, curr, end); } } else if (!conn->raop_rtp) { logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER"); } free(datastr); } else if (!strcmp(content_type, "image/jpeg") || !strcmp(content_type, "image/png")) { logger_log(conn->raop->logger, LOGGER_INFO, "Got image data of %d bytes", datalen); if (conn->raop_rtp) { raop_rtp_set_coverart(conn->raop_rtp, data, datalen); } else { logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER coverart"); } } else if (!strcmp(content_type, "application/x-dmap-tagged")) { logger_log(conn->raop->logger, LOGGER_INFO, "Got metadata of %d bytes", datalen); if (conn->raop_rtp) { raop_rtp_set_metadata(conn->raop_rtp, data, datalen); } else { logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER metadata"); } } } else if (!strcmp(method, "FLUSH")) { const char *rtpinfo; int next_seq = -1; rtpinfo = http_request_get_header(request, "RTP-Info"); if (rtpinfo) { logger_log(conn->raop->logger, LOGGER_INFO, "Flush with RTP-Info: %s", rtpinfo); if (!strncmp(rtpinfo, "seq=", 4)) { next_seq = strtol(rtpinfo+4, NULL, 10); } } if (conn->raop_rtp) { raop_rtp_flush(conn->raop_rtp, next_seq); } else { logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at FLUSH"); } } else if (!strcmp(method, "TEARDOWN")) { http_response_add_header(res, "Connection", "close"); if (conn->raop_rtp) { /* Destroy our RTP session */ raop_rtp_stop(conn->raop_rtp); raop_rtp_destroy(conn->raop_rtp); conn->raop_rtp = NULL; } } http_response_finish(res, NULL, 0); logger_log(conn->raop->logger, LOGGER_DEBUG, "Handled request %s with URL %s", method, http_request_get_url(request)); *response = res; }
static void conn_request(void *ptr, http_request_t *request, http_response_t **response) { const char realm[] = "airplay"; raop_conn_t *conn = ptr; raop_t *raop = conn->raop; http_response_t *res; const char *method; const char *url; const char *cseq; const char *challenge; int require_auth = 0; char *data = NULL; int length_res = 0; method = http_request_get_method(request); url = http_request_get_url(request); cseq = http_request_get_header(request, "CSeq"); if (!method || !cseq) { return; } printf("-----------------\n"); res = http_response_init("RTSP/1.0", 200, "OK"); /* We need authorization for everything else than OPTIONS request */ if (strcmp(method, "OPTIONS") != 0 && strlen(raop->password)) { const char *authorization; authorization = http_request_get_header(request, "Authorization"); if (authorization) { logger_log(conn->raop->logger, LOGGER_DEBUG, "Our nonce: %s", conn->nonce); logger_log(conn->raop->logger, LOGGER_DEBUG, "Authorization: %s", authorization); } if (!digest_is_valid(realm, raop->password, conn->nonce, method, http_request_get_url(request), authorization)) { char *authstr; int authstrlen; /* Allocate the authenticate string */ authstrlen = sizeof("Digest realm=\"\", nonce=\"\"") + sizeof(realm) + sizeof(conn->nonce) + 1; authstr = malloc(authstrlen); /* Concatenate the authenticate string */ memset(authstr, 0, authstrlen); strcat(authstr, "Digest realm=\""); strcat(authstr, realm); strcat(authstr, "\", nonce=\""); strcat(authstr, conn->nonce); strcat(authstr, "\""); /* Construct a new response */ require_auth = 1; http_response_destroy(res); res = http_response_init("RTSP/1.0", 401, "Unauthorized"); http_response_add_header(res, "WWW-Authenticate", authstr); free(authstr); logger_log(conn->raop->logger, LOGGER_DEBUG, "Authentication unsuccessful, sending Unauthorized"); } else { logger_log(conn->raop->logger, LOGGER_DEBUG, "Authentication successful!"); } } http_response_add_header(res, "CSeq", cseq); // http_response_add_header(res, "Apple-Jack-Status", "connected; type=analog"); challenge = http_request_get_header(request, "Apple-Challenge"); if (!require_auth && challenge) { char signature[MAX_SIGNATURE_LEN]; memset(signature, 0, sizeof(signature)); rsakey_sign(raop->rsakey, signature, sizeof(signature), challenge, conn->local, conn->locallen, raop->hwaddr, raop->hwaddrlen); http_response_add_header(res, "Apple-Response", signature); logger_log(conn->raop->logger, LOGGER_DEBUG, "Got challenge: %s", challenge); logger_log(conn->raop->logger, LOGGER_DEBUG, "Got response: %s", signature); } if (require_auth) { /* Do nothing in case of authentication request */ } else if (!strcmp(method, "OPTIONS")) { http_response_add_header(res, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER"); } else if (!strcmp(method, "ANNOUNCE")) { const char *data; int datalen; unsigned char aeskey[16]; unsigned char aesiv[16]; int aeskeylen, aesivlen; data = http_request_get_data(request, &datalen); if (data) { sdp_t *sdp; const char *remotestr, *rtpmapstr, *fmtpstr, *aeskeystr, *aesivstr; sdp = sdp_init(data, datalen); remotestr = sdp_get_connection(sdp); rtpmapstr = sdp_get_rtpmap(sdp); fmtpstr = sdp_get_fmtp(sdp); aeskeystr = sdp_get_rsaaeskey(sdp); aesivstr = sdp_get_aesiv(sdp); logger_log(conn->raop->logger, LOGGER_DEBUG, "connection: %s", remotestr); logger_log(conn->raop->logger, LOGGER_DEBUG, "rtpmap: %s", rtpmapstr); logger_log(conn->raop->logger, LOGGER_DEBUG, "fmtp: %s", fmtpstr); logger_log(conn->raop->logger, LOGGER_DEBUG, "rsaaeskey: %s", aeskeystr); logger_log(conn->raop->logger, LOGGER_DEBUG, "aesiv: %s", aesivstr); aeskeylen = rsakey_decrypt(raop->rsakey, aeskey, sizeof(aeskey), aeskeystr); aesivlen = rsakey_parseiv(raop->rsakey, aesiv, sizeof(aesiv), aesivstr); logger_log(conn->raop->logger, LOGGER_DEBUG, "aeskeylen: %d", aeskeylen); logger_log(conn->raop->logger, LOGGER_DEBUG, "aesivlen: %d", aesivlen); if (conn->raop_rtp) { /* This should never happen */ raop_rtp_destroy(conn->raop_rtp); conn->raop_rtp = NULL; } conn->raop_rtp = raop_rtp_init(raop->logger, &raop->callbacks, remotestr, rtpmapstr, fmtpstr, aeskey, aesiv); if (!conn->raop_rtp) { logger_log(conn->raop->logger, LOGGER_ERR, "Error initializing the audio decoder"); http_response_set_disconnect(res, 1); } sdp_destroy(sdp); } } else if (!strcmp(method, "SETUP")) { unsigned short remote_cport = 0, remote_tport = 0; unsigned short cport = 0, tport = 0, dport = 0; const char *transport; char buffer[1024]; int use_udp; transport = http_request_get_header(request, "Transport"); assert(transport); logger_log(conn->raop->logger, LOGGER_INFO, "Transport: %s", transport); use_udp = strncmp(transport, "RTP/AVP/TCP", 11); if (use_udp) { char *original, *current, *tmpstr; current = original = strdup(transport); if (original) { while ((tmpstr = utils_strsep(¤t, ";")) != NULL) { unsigned short value; int ret; ret = sscanf(tmpstr, "control_port=%hu", &value); if (ret == 1) { logger_log(conn->raop->logger, LOGGER_DEBUG, "Found remote control port: %hu", value); remote_cport = value; } ret = sscanf(tmpstr, "timing_port=%hu", &value); if (ret == 1) { logger_log(conn->raop->logger, LOGGER_DEBUG, "Found remote timing port: %hu", value); remote_tport = value; } } } free(original); } if (conn->raop_rtp) { raop_rtp_start(conn->raop_rtp, use_udp, remote_cport, remote_tport, &cport, &tport, &dport); } else { logger_log(conn->raop->logger, LOGGER_ERR, "RAOP not initialized at SETUP, playing will fail!"); http_response_set_disconnect(res, 1); } memset(buffer, 0, sizeof(buffer)); if (use_udp) { snprintf(buffer, sizeof(buffer) - 1, "RTP/AVP/UDP;unicast;mode=record;timing_port=%hu;events;control_port=%hu;server_port=%hu", tport, cport, dport); } else { snprintf(buffer, sizeof(buffer) - 1, "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record;server_port=%u", dport); } logger_log(conn->raop->logger, LOGGER_INFO, "Responding with %s", buffer); http_response_add_header(res, "Transport", buffer); http_response_add_header(res, "Session", "DEADBEEF"); } else if (!strcmp(method, "SET_PARAMETER")) { const char *content_type; const char *data; int datalen; content_type = http_request_get_header(request, "Content-Type"); data = http_request_get_data(request, &datalen); if (!strcmp(content_type, "text/parameters")) { char *datastr; datastr = calloc(1, datalen + 1); if (data && datastr && conn->raop_rtp) { memcpy(datastr, data, datalen); if (!strncmp(datastr, "volume: ", 8)) { float vol = 0.0; sscanf(datastr + 8, "%f", &vol); raop_rtp_set_volume(conn->raop_rtp, vol); } } else if (!conn->raop_rtp) { logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER volume"); } free(datastr); } else if (!strcmp(content_type, "image/jpeg")) { logger_log(conn->raop->logger, LOGGER_INFO, "Got image data of %d bytes", datalen); if (conn->raop_rtp) { raop_rtp_set_coverart(conn->raop_rtp, data, datalen); } else { logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER coverart"); } } else if (!strcmp(content_type, "application/x-dmap-tagged")) { logger_log(conn->raop->logger, LOGGER_INFO, "Got metadata of %d bytes", datalen); if (conn->raop_rtp) { raop_rtp_set_metadata(conn->raop_rtp, data, datalen); } else { logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at SET_PARAMETER metadata"); } } } else if (!strcmp(method, "FLUSH")) { const char *rtpinfo; int next_seq = -1; rtpinfo = http_request_get_header(request, "RTP-Info"); if (rtpinfo) { logger_log(conn->raop->logger, LOGGER_INFO, "Flush with RTP-Info: %s", rtpinfo); if (!strncmp(rtpinfo, "seq=", 4)) { next_seq = strtol(rtpinfo + 4, NULL, 10); } } if (conn->raop_rtp) { raop_rtp_flush(conn->raop_rtp, next_seq); } else { logger_log(conn->raop->logger, LOGGER_WARNING, "RAOP not initialized at FLUSH"); } } else if (!strcmp(method, "TEARDOWN")) { http_response_add_header(res, "Connection", "close"); if (conn->raop_rtp) { /* Destroy our RTP session */ raop_rtp_stop(conn->raop_rtp); raop_rtp_destroy(conn->raop_rtp); conn->raop_rtp = NULL; } } else if (!strcmp(method, "POST")) { if (!strcmp(url, "/fp-setup")) { printf("POST fp-setup\n"); int datalen; uint8_t fply_1[] __attribute__((unused)) = { 0x46, 0x50, 0x4c, 0x59, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x02, 0xbb }; // 2 1 2 -> 130 : 02 02 xxx uint8_t fply_2[] = { 0x46, 0x50, 0x4c, 0x59, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x82, 0x02, 0x02, 0x2f, 0x7b, 0x69, 0xe6, 0xb2, 0x7e, 0xbb, 0xf0, 0x68, 0x5f, 0x98, 0x54, 0x7f, 0x37, 0xce, 0xcf, 0x87, 0x06, 0x99, 0x6e, 0x7e, 0x6b, 0x0f, 0xb2, 0xfa, 0x71, 0x20, 0x53, 0xe3, 0x94, 0x83, 0xda, 0x22, 0xc7, 0x83, 0xa0, 0x72, 0x40, 0x4d, 0xdd, 0x41, 0xaa, 0x3d, 0x4c, 0x6e, 0x30, 0x22, 0x55, 0xaa, 0xa2, 0xda, 0x1e, 0xb4, 0x77, 0x83, 0x8c, 0x79, 0xd5, 0x65, 0x17, 0xc3, 0xfa, 0x01, 0x54, 0x33, 0x9e, 0xe3, 0x82, 0x9f, 0x30, 0xf0, 0xa4, 0x8f, 0x76, 0xdf, 0x77, 0x11, 0x7e, 0x56, 0x9e, 0xf3, 0x95, 0xe8, 0xe2, 0x13, 0xb3, 0x1e, 0xb6, 0x70, 0xec, 0x5a, 0x8a, 0xf2, 0x6a, 0xfc, 0xbc, 0x89, 0x31, 0xe6, 0x7e, 0xe8, 0xb9, 0xc5, 0xf2, 0xc7, 0x1d, 0x78, 0xf3, 0xef, 0x8d, 0x61, 0xf7, 0x3b, 0xcc, 0x17, 0xc3, 0x40, 0x23, 0x52, 0x4a, 0x8b, 0x9c, 0xb1, 0x75, 0x05, 0x66, 0xe6, 0xb3 }; // 2 1 3 -> 152 // 4 : 02 8f 1a 9c // 128 : xxx // 20 : 5b ed 04 ed c3 cd 5f e6 a8 28 90 3b 42 58 15 cb 74 7d ee 85 uint8_t fply_3[] __attribute__((unused)) = { 0x46, 0x50, 0x4c, 0x59, 0x02, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x98, 0x02, 0x8f, 0x1a, 0x9c, 0x6e, 0x73, 0xd2, 0xfa, 0x62, 0xb2, 0xb2, 0x07, 0x6f, 0x52, 0x5f, 0xe5, 0x72, 0xa5, 0xac, 0x4d, 0x19, 0xb4, 0x7c, 0xd8, 0x07, 0x1e, 0xdb, 0xbc, 0x98, 0xae, 0x7e, 0x4b, 0xb4, 0xb7, 0x2a, 0x7b, 0x5e, 0x2b, 0x8a, 0xde, 0x94, 0x4b, 0x1d, 0x59, 0xdf, 0x46, 0x45, 0xa3, 0xeb, 0xe2, 0x6d, 0xa2, 0x83, 0xf5, 0x06, 0x53, 0x8f, 0x76, 0xe7, 0xd3, 0x68, 0x3c, 0xeb, 0x1f, 0x80, 0x0e, 0x68, 0x9e, 0x27, 0xfc, 0x47, 0xbe, 0x3d, 0x8f, 0x73, 0xaf, 0xa1, 0x64, 0x39, 0xf7, 0xa8, 0xf7, 0xc2, 0xc8, 0xb0, 0x20, 0x0c, 0x85, 0xd6, 0xae, 0xb7, 0xb2, 0xd4, 0x25, 0x96, 0x77, 0x91, 0xf8, 0x83, 0x68, 0x10, 0xa1, 0xa9, 0x15, 0x4a, 0xa3, 0x37, 0x8c, 0xb7, 0xb9, 0x89, 0xbf, 0x86, 0x6e, 0xfb, 0x95, 0x41, 0xff, 0x03, 0x57, 0x61, 0x05, 0x00, 0x73, 0xcc, 0x06, 0x7e, 0x4f, 0xc7, 0x96, 0xae, 0xba, 0x5b, 0xed, 0x04, 0xed, 0xc3, 0xcd, 0x5f, 0xe6, 0xa8, 0x28, 0x90, 0x3b, 0x42, 0x58, 0x15, 0xcb, 0x74, 0x7d, 0xee, 0x85 }; // 2 1 4 -> 20 : 5b ed 04 ed c3 cd 5f e6 a8 28 90 3b 42 58 15 cb 74 7d ee 85 uint8_t fply_4[] = { 0x46, 0x50, 0x4c, 0x59, 0x02, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x14, 0x5b, 0xed, 0x04, 0xed, 0xc3, 0xcd, 0x5f, 0xe6, 0xa8, 0x28, 0x90, 0x3b, 0x42, 0x58, 0x15, 0xcb, 0x74, 0x7d, 0xee, 0x85 }; uint8_t fply_header[12]; char *content = http_request_get_data(request, &datalen); memcpy(fply_header, content, sizeof(fply_header)); char payload[datalen - sizeof(fply_header)]; memcpy(payload, content + sizeof(fply_header), datalen - sizeof(fply_header)); if (fply_header[6] == 1) { printf("fh == 1\n"); memcpy(fply_2 + 13, content + 14, 1); data = (char *) malloc(sizeof(fply_2)); length_res = sizeof(fply_2); memcpy(data, fply_2, sizeof(fply_2)); } else if (fply_header[6] == 3) { printf("fh == 3\n"); data = (char *) malloc(12 + 20); length_res = 12 + 20; memcpy(data, fply_4, 12); memcpy(data + 12, payload + datalen - 20, 20); } http_response_add_header(res, "Content-Type", "application/octet-stream"); http_response_add_header(res, "Server", "AirTunes/110.92"); } }