| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- #include "rtsp_message.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <strings.h>
- #include <sys/socket.h>
- #include "esp_log.h"
- #include "rtsp_crypto.h"
- static const char *TAG = "rtsp_message";
- const uint8_t *rtsp_find_header_end(const uint8_t *data, size_t len) {
- for (size_t i = 0; i + 3 < len; i++) {
- if (data[i] == '\r' && data[i + 1] == '\n' && data[i + 2] == '\r' &&
- data[i + 3] == '\n') {
- return data + i;
- }
- }
- return NULL;
- }
- int rtsp_parse_cseq(const char *request) {
- const char *cseq = strstr(request, "CSeq:");
- if (cseq) {
- return (int)strtol(cseq + 5, NULL, 10);
- }
- return 1;
- }
- int rtsp_parse_content_length(const char *request) {
- const char *cl = strstr(request, "Content-Length:");
- if (!cl) {
- cl = strstr(request, "content-length:");
- }
- if (cl) {
- return (int)strtol(cl + 15, NULL, 10);
- }
- return 0;
- }
- const uint8_t *rtsp_get_body(const char *request, size_t request_len,
- size_t *body_len) {
- const char *body = strstr(request, "\r\n\r\n");
- if (body) {
- body += 4;
- *body_len = request_len - (body - request);
- return (const uint8_t *)body;
- }
- *body_len = 0;
- return NULL;
- }
- // Parse Transport header for client ports (AirPlay 1)
- // Format: Transport:
- // RTP/AVP/UDP;unicast;mode=record;control_port=6001;timing_port=6002
- void rtsp_parse_transport(const char *request, uint16_t *control_port,
- uint16_t *timing_port) {
- if (control_port) {
- *control_port = 0;
- }
- if (timing_port) {
- *timing_port = 0;
- }
- // RTSP header names and Transport-header parameter names are
- // case-insensitive (RFC 2326), so match accordingly.
- const char *transport = strcasestr(request, "Transport:");
- if (!transport) {
- return;
- }
- // Find end of Transport header line
- const char *line_end = strstr(transport, "\r\n");
- if (!line_end) {
- line_end = transport + strlen(transport);
- }
- // Parse control_port
- const char *cp = strcasestr(transport, "control_port=");
- if (cp && cp < line_end && control_port) {
- *control_port = (uint16_t)strtoul(cp + 13, NULL, 10);
- }
- // Parse timing_port
- const char *tp = strcasestr(transport, "timing_port=");
- if (tp && tp < line_end && timing_port) {
- *timing_port = (uint16_t)strtoul(tp + 12, NULL, 10);
- }
- }
- int rtsp_request_parse(const uint8_t *data, size_t len, rtsp_request_t *req) {
- if (!data || !req || len == 0) {
- return -1;
- }
- memset(req, 0, sizeof(*req));
- // Find header end
- const uint8_t *header_end = rtsp_find_header_end(data, len);
- if (!header_end) {
- return -1;
- }
- // Parse first line: METHOD PATH PROTOCOL. The socket buffer is not
- // guaranteed to be NUL-terminated, so copy only the request line.
- const uint8_t *line_end = memchr(data, '\n', (size_t)(header_end - data));
- if (!line_end) {
- return -1;
- }
- size_t line_len = (size_t)(line_end - data);
- if (line_len > 0 && data[line_len - 1] == '\r') {
- line_len--;
- }
- char first_line[320];
- if (line_len >= sizeof(first_line)) {
- return -1;
- }
- memcpy(first_line, data, line_len);
- first_line[line_len] = '\0';
- if (sscanf(first_line, "%31s %255s %15s", req->method, req->path,
- req->protocol) < 2) {
- return -1;
- }
- // Parse CSeq
- req->cseq = rtsp_parse_cseq((const char *)data);
- // Parse Content-Length
- req->content_length = (size_t)rtsp_parse_content_length((const char *)data);
- // Parse Content-Type
- const char *ct = strstr((const char *)data, "Content-Type:");
- if (ct) {
- sscanf(ct, "Content-Type: %63s", req->content_type);
- }
- // Get body
- req->body = rtsp_get_body((const char *)data, len, &req->body_len);
- return 0;
- }
- // Internal: send all data, handling partial sends
- static int send_all(int socket, const uint8_t *data, size_t len) {
- size_t sent = 0;
- while (sent < len) {
- ssize_t r = send(socket, data + sent, len - sent, 0);
- if (r <= 0) {
- return -1;
- }
- sent += (size_t)r;
- }
- return 0;
- }
- int rtsp_send_response(int socket, rtsp_conn_t *conn, int status_code,
- const char *status_text, int cseq,
- const char *extra_headers, const char *body,
- size_t body_len) {
- char header[1024];
- int header_len;
- if (extra_headers && body && body_len > 0) {
- header_len =
- snprintf(header, sizeof(header),
- "RTSP/1.0 %d %s\r\n"
- "CSeq: %d\r\n"
- "Server: AirTunes/377.40.00\r\n"
- "%s"
- "Content-Length: %zu\r\n"
- "\r\n",
- status_code, status_text, cseq, extra_headers, body_len);
- } else if (extra_headers) {
- header_len = snprintf(header, sizeof(header),
- "RTSP/1.0 %d %s\r\n"
- "CSeq: %d\r\n"
- "Server: AirTunes/377.40.00\r\n"
- "%s"
- "\r\n",
- status_code, status_text, cseq, extra_headers);
- } else if (body && body_len > 0) {
- header_len = snprintf(header, sizeof(header),
- "RTSP/1.0 %d %s\r\n"
- "CSeq: %d\r\n"
- "Server: AirTunes/377.40.00\r\n"
- "Content-Length: %zu\r\n"
- "\r\n",
- status_code, status_text, cseq, body_len);
- } else {
- header_len = snprintf(header, sizeof(header),
- "RTSP/1.0 %d %s\r\n"
- "CSeq: %d\r\n"
- "Server: AirTunes/377.40.00\r\n"
- "\r\n",
- status_code, status_text, cseq);
- }
- // Build complete response
- size_t total_len = (size_t)header_len + body_len;
- uint8_t *response = malloc(total_len);
- if (!response) {
- ESP_LOGE(TAG, "Failed to allocate response buffer");
- return -1;
- }
- memcpy(response, header, (size_t)header_len);
- if (body && body_len > 0) {
- memcpy(response + header_len, body, body_len);
- }
- // Send encrypted or plain depending on mode
- int result;
- if (conn && conn->encrypted_mode) {
- result = rtsp_crypto_write_frame(socket, conn, response, total_len);
- } else {
- result = (send_all(socket, response, total_len) < 0) ? -1 : 0;
- if (result < 0) {
- ESP_LOGE(TAG, "Failed to send RTSP response");
- }
- }
- free(response);
- return result;
- }
- int rtsp_send_ok(int socket, rtsp_conn_t *conn, int cseq) {
- return rtsp_send_response(socket, conn, 200, "OK", cseq, NULL, NULL, 0);
- }
- int rtsp_send_http_response(int socket, rtsp_conn_t *conn, int status_code,
- const char *status_text, const char *content_type,
- const char *body, size_t body_len) {
- char header[512];
- int header_len = snprintf(header, sizeof(header),
- "HTTP/1.1 %d %s\r\n"
- "Content-Type: %s\r\n"
- "Content-Length: %zu\r\n"
- "Server: AirTunes/377.40.00\r\n"
- "CSeq: 1\r\n"
- "\r\n",
- status_code, status_text, content_type, body_len);
- // Build complete response
- size_t total_len = (size_t)header_len + body_len;
- uint8_t *response = malloc(total_len);
- if (!response) {
- ESP_LOGE(TAG, "Failed to allocate response buffer");
- return -1;
- }
- memcpy(response, header, (size_t)header_len);
- if (body && body_len > 0) {
- memcpy(response + header_len, body, body_len);
- }
- // Send encrypted or plain depending on mode
- int result;
- if (conn && conn->encrypted_mode) {
- result = rtsp_crypto_write_frame(socket, conn, response, total_len);
- } else {
- result = (send_all(socket, response, total_len) < 0) ? -1 : 0;
- if (result < 0) {
- ESP_LOGE(TAG, "Failed to send HTTP response");
- }
- }
- free(response);
- return result;
- }
|