diff --git a/src/include/aes67/rtsp.h b/src/include/aes67/rtsp.h index 7c5149b95441623c251995a1c05fdc3c4001d668..c3a174954d258573d63c1ae3e20d2d703c0d2e71 100644 --- a/src/include/aes67/rtsp.h +++ b/src/include/aes67/rtsp.h @@ -5,6 +5,7 @@ * References: * Real Time Streaming Protocol (RTSP) https://tools.ietf.org/html/rfc2326 * Real-Time Streaming Protocol Version 2.0 https://tools.ietf.org/html/rfc7826 + * https://www.iana.org/assignments/rtsp-parameters/rtsp-parameters.xhtml */ /** @@ -37,7 +38,51 @@ extern "C" { #define AES67_RTSP_DEFAULT_PORT 554 #define AES67_RTSP_SCHEME "rtsp" -#define AES67_RTSP_STATUS_OK 200 +#define AES67_RTSP_STATUS_CONTINUE 100 +#define AES67_RTSP_STATUS_OK 200 +#define AES67_RTSP_STATUS_CREATED 201 +#define AES67_RTSP_STATUS_LOW_ON_STORAGE_SPACE 250 +#define AES67_RTSP_STATUS_MULTIPLE_CHOICES 300 +#define AES67_RTSP_STATUS_MOVED_PERMANENTLY 301 +#define AES67_RTSP_STATUS_MOVED_TEMPORARILY 302 +#define AES67_RTSP_STATUS_SEE_OTHER 303 +#define AES67_RTSP_STATUS_NOT_MODIFIED 304 +#define AES67_RTSP_STATUS_USE_PROXY 305 +#define AES67_RTSP_STATUS_BAD_REQUEST 400 +#define AES67_RTSP_STATUS_UNAUTHORIZED 401 +#define AES67_RTSP_STATUS_PAYMENT_REQUIRED 402 +#define AES67_RTSP_STATUS_FORBIDDEN 403 +#define AES67_RTSP_STATUS_NOT_FOUND 404 +#define AES67_RTSP_STATUS_METHOD_NOT_ALLOWED 405 +#define AES67_RTSP_STATUS_NOT_ACCEPTABLE 406 +#define AES67_RTSP_STATUS_PROXY_AUTH_REQUIRED 407 +#define AES67_RTSP_STATUS_REQUEST_TIMEOUT 408 +#define AES67_RTSP_STATUS_GONE 410 +#define AES67_RTSP_STATUS_LENGTH_REQUIRED 411 +#define AES67_RTSP_STATUS_PRECONDITION_FAILED 412 +#define AES67_RTSP_STATUS_ENTITY_TOO_LARGE 413 +#define AES67_RTSP_STATUS_URI_TOO_LARGE 414 +#define AES67_RTSP_STATUS_UNSUPPORTED_MEDIA 415 +#define AES67_RTSP_STATUS_PARAM_NOT_UNDERSTOOD 451 +#define AES67_RTSP_STATUS_CONFERENCE_NOT_FOUND 452 +#define AES67_RTSP_STATUS_NOT_ENOUGH_BANDWIDTH 453 +#define AES67_RTSP_STATUS_SESSION_NOT_FOUND 454 +#define AES67_RTSP_STATUS_METHOD_NOT_VALID_IN_STATE 455 +#define AES67_RTSP_STATUS_HDR_FIELD_NOT_VALID 456 +#define AES67_RTSP_STATUS_INVALID RANGE 457 +#define AES67_RTSP_STATUS_PARAM_IS_READONLY 458 +#define AES67_RTSP_STATUS_AGGR_OP_NOT_ALLOWED 459 +#define AES67_RTSP_STATUS_ONLY_AGGR_OP_ALLOWED 460 +#define AES67_RTSP_STATUS_UNSUPPORTED_TRANSPORT 461 +#define AES67_RTSP_STATUS_DEST_UNREACHABLE 462 +#define AES67_RTSP_STATUS_KEY_MGMT_FAILURE 463 +#define AES67_RTSP_STATUS_INTERNAL_ERROR 500 +#define AES67_RTSP_STATUS_NOT_IMPLEMENTED 501 +#define AES67_RTSP_STATUS_BAD_GATEWAY 502 +#define AES67_RTSP_STATUS_SERVICE_UNAVAILABLE 503 +#define AES67_RTSP_STATUS_GATEWAY_TIMEOUT 504 +#define AES67_RTSP_STATUS_VERSION_NOT_SUPPORTED 505 +#define AES67_RTSP_STATUS_OPTION_NOT_SUPPORTED 551 //struct aes67_rtsp { // u32_t cseq; diff --git a/src/include/aes67/utils/rtsp-srv.h b/src/include/aes67/utils/rtsp-srv.h index f06f060bf2741a785d6e2265acf656a325c0b775..96dd235230562f241ff212c2c26888b47ed3d330 100644 --- a/src/include/aes67/utils/rtsp-srv.h +++ b/src/include/aes67/utils/rtsp-srv.h @@ -130,7 +130,7 @@ struct aes67_rtsp_srv { } req; // request struct { - u16_t status_code; +// u16_t status_code; u16_t sent; u16_t len; u8_t data[AES67_RTSP_SRV_TXBUFSIZE]; @@ -147,7 +147,7 @@ void aes67_rtsp_srv_blocking(struct aes67_rtsp_srv * srv, bool blocking); void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv); -void aes67_rtsp_srv_sdp_getter(struct aes67_rtsp_srv * srv, void * sdpref, u8_t * buf, u16_t * len, u16_t maxlen); +u16_t aes67_rtsp_srv_sdp_getter(struct aes67_rtsp_srv * srv, void * sdpref, u8_t * buf, u16_t maxlen); void aes67_rtsp_srv_http_handler(struct aes67_rtsp_srv * srv, const enum aes67_rtsp_srv_method method, const char * uri, const u8_t urilen, u8_t * buf, u16_t * len, u16_t maxlen, void * response_data); struct aes67_rtsp_srv_resource * aes67_rtsp_srv_sdp_add(struct aes67_rtsp_srv * srv, const char * uri, const u8_t urilen, void * sdpref); diff --git a/src/utils/rav-publish/rav-publish.c b/src/utils/rav-publish/rav-publish.c index 30f56595769c429e51ede96e9af90a7e39d50456..bfd3ff4d5bb86ca7f50ba06346aa54180c282a89 100644 --- a/src/utils/rav-publish/rav-publish.c +++ b/src/utils/rav-publish/rav-publish.c @@ -55,13 +55,17 @@ static struct { struct aes67_net_addr addr; u16_t port; u32_t ttl; + bool http; + char * http_root; } opts = { .verbose = false, .rtsp = true, .host = NULL, .addr.ipver = aes67_net_ipver_undefined, .port = 0, - .ttl = 100 + .ttl = 100, + .http = false, + .http_root = NULL }; static volatile bool keep_running; @@ -85,6 +89,7 @@ static void help(FILE * fd) "\t --host <host>\t Host of target device (by default will assume self; if given will try to use originator IPv4/6 from SDP file).\n" "\t --ip <ip>\t (Override) IPv4/6 address of target device (create an record for host).\n" "\t --no-rtsp\t Do not start a RTSP server.\n" + "\t --http <root>\t Start a http server with given root dir (implies RTSP server)\n" , argv0); } @@ -222,7 +227,7 @@ static void mdns_process() static int rtsp_setup() { - aes67_rtsp_srv_init(&rtsp_srv, false, NULL); + aes67_rtsp_srv_init(&rtsp_srv, opts.http, NULL); aes67_rtsp_srv_blocking(&rtsp_srv, false); @@ -238,7 +243,7 @@ static int rtsp_setup() uri[urilen] = '\0'; - printf("final uri [%d]: [%s]\n", urilen, uri); +// printf("final uri [%d]: [%s]\n", urilen, uri); aes67_rtsp_srv_sdp_add(&rtsp_srv, uri, urilen, sdpres); @@ -267,26 +272,27 @@ static void rtsp_process() aes67_rtsp_srv_process(&rtsp_srv); } -void aes67_rtsp_srv_sdp_getter(struct aes67_rtsp_srv * srv, void * sdpref, u8_t * buf, u16_t * len, u16_t maxlen) +u16_t aes67_rtsp_srv_sdp_getter(struct aes67_rtsp_srv * srv, void * sdpref, u8_t * buf, u16_t maxlen) { assert(srv); assert(sdpref); assert(buf); - assert(len); assert(maxlen); sdpres_t * sdpres = sdpref; fprintf(stderr, "serving rtsp describe for uri %s\n", sdpres->name); - if (maxlen < sdpres->len){ + u16_t len = snprintf((char*)buf, maxlen, "Content-Length: %u\r\n\r\n", sdpres->len); + + if (sdpres->len + len > maxlen){ fprintf(stderr, "sdp file too big for compiled in buffer size"); - *len = 0; - return; + return 0; } - memcpy(buf, sdpres->data, sdpres->len); - *len += sdpres->len; + memcpy(buf + len, sdpres->data, sdpres->len); + + return len + sdpres->len; } static int load_sdpres(char * fname, size_t maxlen) @@ -395,6 +401,7 @@ int main(int argc, char * argv[]) {"ip", required_argument, 0, 2 }, {"no-rtsp", no_argument, 0, 3 }, {"ttl", required_argument, 0, 4}, + {"http", required_argument, 0, 5}, {0, 0, 0, 0 } }; @@ -442,6 +449,23 @@ int main(int argc, char * argv[]) } break; + case 5: + if (access(optarg, F_OK) != 0){ + fprintf(stderr, "ERROR http root does not exists? %s\n", optarg); + return EXIT_FAILURE; + } + opts.http = true; + opts.http_root = optarg; + + +// struct stat st; + +// if (stat(fname, &st)){ +// fprintf(stderr, "ERROR failed to get filesize %s\n", fname); +// return EXIT_FAILURE; +// } + break; + case '?': case 'h': help(stdout); @@ -464,6 +488,11 @@ int main(int argc, char * argv[]) return EXIT_FAILURE; } + if (opts.http && !opts.rtsp){ + fprintf(stderr, "RTSP server can not be disabled when HTTP server is to run\n"); + return EXIT_FAILURE; + } + for (int i = optind; i < argc; i++) { if (load_sdpres(argv[i], 1024)) { fprintf(stderr, "sdp load error\n"); diff --git a/src/utils/rtsp-srv.c b/src/utils/rtsp-srv.c index 554c540de4f85e82dabe973fed1ebb3e2fdf3047..33c68d86944ee4bffd1f30949fad1f0c426bb495 100644 --- a/src/utils/rtsp-srv.c +++ b/src/utils/rtsp-srv.c @@ -39,7 +39,7 @@ static int sock_set_blocking(int sockfd, bool blocking){ return EXIT_SUCCESS; } -static struct aes67_rtsp_srv_resource * rtsp_resource_by_uri(struct aes67_rtsp_srv * srv, const char * uri, u8_t urilen) +static struct aes67_rtsp_srv_resource * rtsp_resource_by_uri(struct aes67_rtsp_srv * srv, const u8_t * uri, u8_t urilen) { assert(srv); assert(uri); @@ -96,7 +96,7 @@ int aes67_rtsp_srv_start(struct aes67_rtsp_srv * srv, const enum aes67_net_ipver aes67_rtsp_srv_stop(srv); - srv->listen_sockfd = socket (AF_UNIX, SOCK_STREAM, 0); + srv->listen_sockfd = socket (AF_INET, SOCK_STREAM, 0); if (srv->listen_sockfd < 0){ perror ("socket()"); @@ -144,17 +144,18 @@ void aes67_rtsp_srv_stop(struct aes67_rtsp_srv * srv) { assert(srv); + if (srv->listen_sockfd != -1){ + sock_set_blocking(srv->listen_sockfd, true); + close(srv->listen_sockfd); + srv->listen_sockfd = -1; + } + if (srv->client_sockfd != -1){ sock_set_blocking(srv->client_sockfd, true); close(srv->client_sockfd); srv->client_sockfd = -1; } - if (srv->listen_sockfd != -1){ - sock_set_blocking(srv->listen_sockfd, true); - close(srv->listen_sockfd); - srv->listen_sockfd = -1; - } } @@ -162,6 +163,11 @@ void aes67_rtsp_srv_blocking(struct aes67_rtsp_srv * srv, bool blocking) { assert(srv); + if (srv->blocking == blocking){ + return; + } + srv->blocking = blocking; + if (srv->listen_sockfd != -1){ sock_set_blocking(srv->listen_sockfd, blocking); } @@ -189,7 +195,7 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv) srv->req.header_len = 0; srv->req.content_length = 0; srv->req.CR = 0; - srv->res.status_code = 0; +// srv->res.status_code = 0; sock_set_blocking(srv->client_sockfd, srv->blocking); @@ -291,6 +297,7 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv) srv->req.version.minor = s[7] - '0'; s = srv->req.data; + fprintf(stderr, "[%s]\n", s); if (srv->req.proto == aes67_rtsp_srv_proto_rtsp && s[0] == 'D' && @@ -357,13 +364,15 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv) close(srv->client_sockfd); srv->client_sockfd = -1; srv->state = aes67_rtsp_srv_state_listening; + fprintf(stderr, "method not recognized\n"); return; } // "RTSP/1.0" - "METHOD .." - srv->req.urilen = &srv->req.data[srv->req.data_len - CR - sizeof("HTTP/1.0")] - srv->req.uri; + srv->req.urilen = &srv->req.data[srv->req.data_len - CR - sizeof(" HTTP/1.0")] - srv->req.uri; - printf("uri[%d] = %s", srv->req.urilen, srv->req.uri); + srv->req.uri[srv->req.urilen] = '\0'; + printf("uri[%d] = [%s]\n", srv->req.urilen, srv->req.uri); // if rtsp, discard scheme and host if (srv->req.proto == aes67_rtsp_srv_proto_rtsp){ @@ -395,6 +404,7 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv) srv->req.urilen -= delim - srv->req.uri; srv->req.uri = delim; } + printf("uri[%d] = [%s]\n", srv->req.urilen, srv->req.uri); // set line start srv->req.line = &srv->req.data[srv->req.data_len]; @@ -504,7 +514,7 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv) } // header // read body (if total data is less than header and expected content length..) - u16_t missing = srv->req.content_length - (srv->req.data_len - srv->req.content_length); + u16_t missing = srv->req.content_length - (srv->req.data_len - srv->req.header_len); if (missing > 0) { // boundary check @@ -512,7 +522,7 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv) close(srv->client_sockfd); srv->client_sockfd = -1; srv->state = aes67_rtsp_srv_state_listening; - fprintf(stderr, "would overflow\n"); + fprintf(stderr, "would overflow (has %d, hdr %d, content %d, missing %d)\n", srv->req.data_len, srv->req.header_len, srv->req.content_length, missing); return; } @@ -539,21 +549,157 @@ void aes67_rtsp_srv_process(struct aes67_rtsp_srv * srv) if (srv->state == aes67_rtsp_srv_state_processing){ if (srv->req.proto == aes67_rtsp_srv_proto_rtsp){ -// struct aes67_rtsp_srv_resource * res = rtsp_resource_by_uri(srv, ) + fprintf(stderr, "is rtsp\n"); + + u16_t status_code = AES67_RTSP_STATUS_INTERNAL_ERROR; + struct aes67_rtsp_srv_resource * res = NULL; + + switch (srv->req.method){ + + case aes67_rtsp_srv_method_options: + fprintf(stderr, "method = options\n"); + status_code = AES67_RTSP_STATUS_OK; + break; + + case aes67_rtsp_srv_method_describe: + res = rtsp_resource_by_uri(srv, srv->req.uri, srv->req.urilen); + if (res == NULL){ + status_code = AES67_RTSP_STATUS_NOT_FOUND; + } else { + status_code = AES67_RTSP_STATUS_OK; + } + break; + + default: + // well, in principle this is what would be the answer (not necessarily RFC conform) + // but this case should be caught earlier on and execution should never reach here + status_code = AES67_RTSP_STATUS_NOT_IMPLEMENTED; + } + + + u8_t * d = srv->res.data; + u16_t l = 0; + + d[0] = 'R'; + d[1] = 'T'; + d[2] = 'S'; + d[3] = 'P'; + d[4] = '/'; + d[5] = '0' + srv->req.version.major; + d[6] = '.'; + d[7] = '0' + srv->req.version.minor; + d[8] = ' '; + l = 9; + + l += aes67_itoa(status_code, d + l , 10); + + d[l++] = ' '; + + switch(status_code){ + case AES67_RTSP_STATUS_OK: + d[l++] = 'O'; + d[l++] = 'K'; + break; + case AES67_RTSP_STATUS_NOT_FOUND: + d[l++] = 'N'; + d[l++] = 'O'; + d[l++] = 'T'; + d[l++] = ' '; + d[l++] = 'F'; + d[l++] = 'O'; + d[l++] = 'U'; + d[l++] = 'N'; + d[l++] = 'D'; + break; + case AES67_RTSP_STATUS_NOT_IMPLEMENTED: + d[l++] = 'N'; + d[l++] = 'O'; + d[l++] = 'T'; + d[l++] = ' '; + d[l++] = 'I'; + d[l++] = 'M'; + d[l++] = 'P'; + d[l++] = 'L'; + d[l++] = 'E'; + d[l++] = 'M'; + d[l++] = 'T'; + d[l++] = 'E'; + d[l++] = 'D'; + break; + default: + d[l++] = '?'; + } + + d[l++] = '\r'; + d[l++] = '\n'; + + d[l++] = 'C'; + d[l++] = 'S'; + d[l++] = 'e'; + d[l++] = 'q'; + d[l++] = ':'; + d[l++] = ' '; + l += aes67_itoa(srv->req.cseq, d + l, 10); + d[l++] = '\r'; + d[l++] = '\n'; + + switch (srv->req.method){ + + case aes67_rtsp_srv_method_options: + aes67_memcpy(d + l, "Public: DESCRIBE\r\n\r\n", sizeof("Public: DESCRIBE\r\n\r\n")-1); + l += sizeof("Public: DESCRIBE\r\n\r\n")-1; + break; + + case aes67_rtsp_srv_method_describe: + if (res){ + l += aes67_rtsp_srv_sdp_getter(srv, res->sdpref, d + l, AES67_RTSP_SRV_TXBUFSIZE - l); + } else { + // add end of header / empty line + d[l++] = '\r'; + d[l++] = '\n'; + } + break; + + default: + // add end of header / empty line + d[l++] = '\r'; + d[l++] = '\n'; + break; + } + + srv->res.len = l; + srv->res.sent = 0; + + srv->state = aes67_rtsp_srv_state_sending; + } // proto == aes67_rtsp_srv_proto_rtsp else if (srv->req.proto == aes67_rtsp_srv_proto_http){ - + fprintf(stderr, "is http\n"); } // proto == aes67_rtsp_srv_proto_http } // state == aes67_rtsp_srv_state_processing if (srv->state == aes67_rtsp_srv_state_sending){ + if (srv->res.sent < srv->res.len){ + fprintf(stderr, "Sending %d bytes..\n", srv->res.len - srv->res.sent); + write(srv->client_sockfd, srv->res.data + srv->res.sent, srv->res.len - srv->res.sent); + + srv->res.sent = srv->res.len; + + srv->state = aes67_rtsp_srv_state_listening; + + close(srv->client_sockfd); + srv->client_sockfd = -1; + } + } //state == aes67_rtsp_srv_state_sending } -WEAK_FUN void aes67_rtsp_srv_sdp_getter(struct aes67_rtsp_srv * srv, void * sdpref, u8_t * buf, u16_t * len, u16_t maxlen) +WEAK_FUN u16_t aes67_rtsp_srv_sdp_getter(struct aes67_rtsp_srv * srv, void * sdpref, u8_t * buf, u16_t maxlen) { assert(false); + + return AES67_RTSP_STATUS_INTERNAL_ERROR; } WEAK_FUN void aes67_rtsp_srv_http_handler(struct aes67_rtsp_srv * srv, const enum aes67_rtsp_srv_method method, const char * uri, const u8_t urilen, u8_t * buf, u16_t * len, u16_t maxlen, void * response_data)