static void *rtsp_conversation_thread_func(void *pconn) { // SIGUSR1 is used to interrupt this thread if blocked for read sigset_t set; sigemptyset(&set); sigaddset(&set, SIGUSR1); pthread_sigmask(SIG_UNBLOCK, &set, NULL); rtsp_conn_info *conn = pconn; rtsp_message *req, *resp; char *hdr, *auth_nonce = NULL; while ((req = rtsp_read_request(conn->fd))) { resp = msg_init(); resp->respcode = 400; apple_challenge(conn->fd, req, resp); hdr = msg_get_header(req, "CSeq"); if (hdr) msg_add_header(resp, "CSeq", hdr); msg_add_header(resp, "Audio-Jack-Status", "connected; type=analog"); if (rtsp_auth(&auth_nonce, req, resp)) goto respond; struct method_handler *mh; for (mh=method_handlers; mh->method; mh++) { if (!strcmp(mh->method, req->method)) { mh->handler(conn, req, resp); break; } } respond: msg_write_response(conn->fd, resp); msg_free(req); msg_free(resp); } debug(1, "closing RTSP connection\n"); if (conn->fd > 0) close(conn->fd); if (rtsp_playing()) { rtp_shutdown(); debug(1, "stop player\n"); player_stop(); debug(1, "player stoped\n"); please_shutdown = 0; pthread_mutex_unlock(&playing_mutex); } if (auth_nonce) free(auth_nonce); conn->running = 0; debug(2, "terminating RTSP thread\n"); return NULL; }
static void handle_teardown(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { if (!rtsp_playing()) return; resp->respcode = 200; msg_add_header(resp, "Connection", "close"); please_shutdown = 1; }
static void handle_options(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { resp->respcode = 200; msg_add_header(resp, "Public", "ANNOUNCE, SETUP, RECORD, " "PAUSE, FLUSH, TEARDOWN, " "OPTIONS, GET_PARAMETER, SET_PARAMETER"); }
static void apple_challenge(int fd, rtsp_message *req, rtsp_message *resp) { char *hdr = msg_get_header(req, "Apple-Challenge"); if (!hdr) return; SOCKADDR fdsa; socklen_t sa_len = sizeof(fdsa); getsockname(fd, (struct sockaddr*)&fdsa, &sa_len); int chall_len; uint8_t *chall = base64_dec(hdr, &chall_len); uint8_t buf[48], *bp = buf; int i; memset(buf, 0, sizeof(buf)); if (chall_len > 16) { warn("oversized Apple-Challenge!"); free(chall); return; } memcpy(bp, chall, chall_len); free(chall); bp += chall_len; #ifdef AF_INET6 if (fdsa.SAFAMILY == AF_INET6) { struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)(&fdsa); memcpy(bp, sa6->sin6_addr.s6_addr, 16); bp += 16; } else #endif { struct sockaddr_in *sa = (struct sockaddr_in*)(&fdsa); memcpy(bp, &sa->sin_addr.s_addr, 4); bp += 4; } for (i=0; i<6; i++) *bp++ = config.hw_addr[i]; int buflen, resplen; buflen = bp-buf; if (buflen < 0x20) buflen = 0x20; uint8_t *challresp = rsa_apply(buf, buflen, &resplen, RSA_MODE_AUTH); char *encoded = base64_enc(challresp, resplen); // strip the padding. char *padding = strchr(encoded, '='); if (padding) *padding = 0; msg_add_header(resp, "Apple-Response", encoded); free(challresp); free(encoded); }
static int msg_handle_line(rtsp_message **pmsg, char *line) { rtsp_message *msg = *pmsg; if (!msg) { msg = msg_init(); *pmsg = msg; char *sp, *p; debug(1, "received request: %s\n", line); p = strtok_r(line, " ", &sp); if (!p) goto fail; strncpy(msg->method, p, sizeof(msg->method)-1); p = strtok_r(NULL, " ", &sp); if (!p) goto fail; p = strtok_r(NULL, " ", &sp); if (!p) goto fail; if (strcmp(p, "RTSP/1.0")) goto fail; return -1; } if (strlen(line)) { char *p; p = strstr(line, ": "); if (!p) { warn("bad header: >>%s<<", line); goto fail; } *p = 0; p += 2; msg_add_header(msg, line, p); debug(2, " %s: %s\n", line, p); return -1; } else { char *cl = msg_get_header(msg, "Content-Length"); if (cl) return atoi(cl); else return 0; } fail: *pmsg = NULL; msg_free(msg); return 0; }
static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { int cport, tport; char *hdr = msg_get_header(req, "Transport"); if (!hdr) return; char *p; p = strstr(hdr, "control_port="); if (!p) return; p = strchr(p, '=') + 1; cport = atoi(p); p = strstr(hdr, "timing_port="); if (!p) return; p = strchr(p, '=') + 1; tport = atoi(p); rtsp_take_player(); int sport = rtp_setup(&conn->remote, cport, tport); if (!sport) return; player_play(&conn->stream); char resphdr[128]={0}; snprintf(resphdr, sizeof(resphdr), "RTP/AVP/UDP;unicast;mode=record;server_port=%d;control_port=%d;timing_port=%d", sport, sport, sport); msg_add_header(resp, "Transport", resphdr); msg_add_header(resp, "Session", "1"); resp->respcode = 200; }
static void handle_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { int cport, tport; char *hdr = msg_get_header(req, "Transport"); if (!hdr) return; char *p; p = strstr(hdr, "control_port="); if (!p) return; p = strchr(p, '=') + 1; cport = atoi(p); p = strstr(hdr, "timing_port="); if (!p) return; p = strchr(p, '=') + 1; tport = atoi(p); rtsp_take_player(); int sport = rtp_setup(&conn->remote, cport, tport); if (!sport) return; player_play(&conn->stream); char *resphdr = malloc(strlen(hdr) + 20); strcpy(resphdr, hdr); sprintf(resphdr + strlen(resphdr), ";server_port=%d", sport); msg_add_header(resp, "Transport", resphdr); msg_add_header(resp, "Session", "1"); resp->respcode = 200; }
static int rtsp_auth(char **nonce, rtsp_message *req, rtsp_message *resp) { if (!config.password) return 0; if (!*nonce) { *nonce = make_nonce(); goto authenticate; } char *hdr = msg_get_header(req, "Authorization"); if (!hdr || strncmp(hdr, "Digest ", 7)) goto authenticate; char *realm = strstr(hdr, "realm=\""); char *username = strstr(hdr, "username=\""); char *response = strstr(hdr, "response=\""); char *uri = strstr(hdr, "uri=\""); if (!realm || !username || !response || !uri) goto authenticate; char *quote; realm = strchr(realm, '"') + 1; if (!(quote = strchr(realm, '"'))) goto authenticate; *quote = 0; username = strchr(username, '"') + 1; if (!(quote = strchr(username, '"'))) goto authenticate; *quote = 0; response = strchr(response, '"') + 1; if (!(quote = strchr(response, '"'))) goto authenticate; *quote = 0; uri = strchr(uri, '"') + 1; if (!(quote = strchr(uri, '"'))) goto authenticate; *quote = 0; uint8_t digest_urp[16], digest_mu[16], digest_total[16]; MD5_CTX ctx; MD5_Init(&ctx); MD5_Update(&ctx, username, strlen(username)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, realm, strlen(realm)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, config.password, strlen(config.password)); MD5_Final(digest_urp, &ctx); MD5_Init(&ctx); MD5_Update(&ctx, req->method, strlen(req->method)); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, uri, strlen(uri)); MD5_Final(digest_mu, &ctx); int i; char buf[33]; for (i=0; i<16; i++) sprintf(buf + 2*i, "%02X", digest_urp[i]); MD5_Init(&ctx); MD5_Update(&ctx, buf, 32); MD5_Update(&ctx, ":", 1); MD5_Update(&ctx, *nonce, strlen(*nonce)); MD5_Update(&ctx, ":", 1); for (i=0; i<16; i++) sprintf(buf + 2*i, "%02X", digest_mu[i]); MD5_Update(&ctx, buf, 32); MD5_Final(digest_total, &ctx); for (i=0; i<16; i++) sprintf(buf + 2*i, "%02X", digest_total[i]); if (!strcmp(response, buf)) return 0; warn("auth failed"); authenticate: resp->respcode = 401; int hdrlen = strlen(*nonce) + 40; char *authhdr = malloc(hdrlen); snprintf(authhdr, hdrlen, "Digest realm=\"taco\", nonce=\"%s\"", *nonce); msg_add_header(resp, "WWW-Authenticate", authhdr); free(authhdr); return 1; }