From 3159ee338b2a01b867e67daf48565a0639cc2dc8 Mon Sep 17 00:00:00 2001
From: phil <me@filou.se>
Date: Wed, 21 Apr 2021 10:52:49 +0200
Subject: [PATCH] first working draft rtsp sdp host

---
 src/include/aes67/rtsp.h            |  47 +++++++-
 src/include/aes67/utils/rtsp-srv.h  |   4 +-
 src/utils/rav-publish/rav-publish.c |  49 ++++++--
 src/utils/rtsp-srv.c                | 176 +++++++++++++++++++++++++---
 4 files changed, 248 insertions(+), 28 deletions(-)

diff --git a/src/include/aes67/rtsp.h b/src/include/aes67/rtsp.h
index 7c5149b..c3a1749 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 f06f060..96dd235 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 30f5659..bfd3ff4 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 554c540..33c68d8 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)
-- 
GitLab