Coverage Report

Created: 2026-01-23 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/ietf/rtsp_common.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2022
6
 *          All rights reserved
7
 *
8
 *  This file is part of GPAC / IETF RTP/RTSP/SDP sub-project
9
 *
10
 *  GPAC is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU Lesser General Public License as published by
12
 *  the Free Software Foundation; either version 2, or (at your option)
13
 *  any later version.
14
 *
15
 *  GPAC is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU Lesser General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU Lesser General Public
21
 *  License along with this library; see the file COPYING.  If not, write to
22
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
 *
24
 */
25
26
#include <gpac/internal/ietf_dev.h>
27
28
#ifndef GPAC_DISABLE_STREAMING
29
30
#include <gpac/token.h>
31
32
33
GF_Err gf_rtsp_read_reply(GF_RTSPSession *sess)
34
0
{
35
0
  GF_Err e;
36
0
  u32 res, body_size = 0;
37
0
  u32 BodyStart = 0;
38
39
  //fetch more data on the socket if needed
40
0
  while (1) {
41
    //Locate header / body
42
0
    if (!BodyStart) gf_rtsp_get_body_info(sess, &BodyStart, &body_size, GF_FALSE);
43
44
0
    if (BodyStart) {
45
      //enough data
46
0
      res = sess->CurrentSize - sess->CurrentPos;
47
0
      if (!body_size || (res >= body_size + BodyStart)) {
48
        //done
49
0
        break;
50
0
      }
51
0
    }
52
    //this is the tricky part: if we do NOT have a body start -> we refill
53
0
    e = gf_rtsp_refill_buffer(sess);
54
0
    if (e) {
55
      //we have a buffer, nothing to follow and this is an http tunnel, this means no '=' at the end of the base64 string
56
      //assume we got the entire message
57
0
      if ((e==GF_IP_NETWORK_EMPTY) && (sess->CurrentPos<sess->CurrentSize) && (sess->tunnel_mode==RTSP_HTTP_SERVER)) {
58
0
        return GF_OK;
59
0
      }
60
0
      return e;
61
0
    }
62
0
  }
63
0
  return GF_OK;
64
0
}
65
66
void gf_rtsp_get_body_info(GF_RTSPSession *sess, u32 *body_start, u32 *body_size, Bool skip_tunnel)
67
0
{
68
0
  s32 start;
69
0
  Bool is_post=GF_FALSE;
70
0
  char *buffer;
71
0
  char *cl_str;
72
73
0
  *body_start = *body_size = 0;
74
75
0
  buffer = sess->tcp_buffer + sess->CurrentPos;
76
0
  if (!skip_tunnel && (sess->tunnel_mode==RTSP_HTTP_SERVER)) {
77
0
    start = gf_token_find(buffer, 0, sess->CurrentSize - sess->CurrentPos, "=");
78
0
    if (start<=0) return;
79
0
    *body_start = start + 1;
80
0
    if (buffer[start+1]=='=')
81
0
      *body_start += 1;
82
0
    return;
83
0
  } else {
84
0
    start = gf_token_find(buffer, 0, sess->CurrentSize - sess->CurrentPos, "\r\n\r\n");
85
0
    if (start<=0) return;
86
    //if found add the 2 "\r\n" and parse it
87
0
    *body_start = start + 4;
88
0
  }
89
0
  if (!strncmp(buffer, "POST ", 5)) {
90
0
    is_post = GF_TRUE;
91
0
  }
92
93
  //get the content length
94
0
  cl_str = strstr(buffer, "Content-Length: ");
95
0
  if (!cl_str) cl_str = strstr(buffer, "Content-length: ");
96
97
0
  if (cl_str) {
98
0
    char *sep;
99
0
    cl_str += 16;
100
0
    sep = strchr(cl_str, '\r');
101
0
    if (sep) {
102
0
      sep[0] = 0;
103
0
      *body_size = atoi(cl_str);
104
0
      sep[0] = '\r';
105
0
    }
106
0
  }
107
0
  if (is_post) *body_size = 0;
108
0
}
109
110
111
GF_Err gf_rstp_do_read_sock(GF_RTSPSession *sess, GF_Socket *sock, u8 *data, u32 data_size, u32 *out_read)
112
0
{
113
0
  GF_Err e;
114
0
#ifdef GPAC_HAS_SSL
115
0
  SSL *ssl_sock = (sock == sess->http) ? sess->ssl_http : sess->ssl;
116
0
  if (ssl_sock) {
117
    //receive on null buffer (select only, check if data available)
118
0
    e = gf_sk_receive(sock, NULL, 0, NULL);
119
    //empty and no pending bytes in SSL, network empty
120
0
    if ((e==GF_IP_NETWORK_EMPTY) && !SSL_pending(sess->ssl))
121
0
      return GF_IP_NETWORK_EMPTY;
122
123
0
    int size = SSL_read(ssl_sock, data, data_size);
124
0
    if (size < 0) {
125
0
      *out_read = 0;
126
0
      e = gf_sk_probe(sock);
127
0
      if (!e) {
128
0
        int err = SSL_get_error(ssl_sock, size);
129
0
        if (err==SSL_ERROR_SSL) {
130
0
          e = GF_IO_ERR;
131
0
        } else if ((err==SSL_ERROR_WANT_READ) || (err==SSL_ERROR_WANT_WRITE)) {
132
0
          e = GF_IP_NETWORK_EMPTY;
133
0
        } else {
134
0
          e = GF_IP_NETWORK_FAILURE;
135
0
        }
136
0
      }
137
0
    } else if (!size) {
138
0
      e = GF_IP_NETWORK_EMPTY;
139
0
    } else {
140
0
      e = GF_OK;
141
0
      data[size] = 0;
142
0
      *out_read = size;
143
0
    }
144
0
  } else
145
0
#endif
146
0
  {
147
0
    e = gf_sk_receive(sock, data, data_size, out_read);
148
0
  }
149
0
  return e;
150
0
}
151
152
153
154
GF_Err gf_rtsp_refill_buffer(GF_RTSPSession *sess)
155
0
{
156
0
  GF_Err e;
157
0
  u32 res;
158
0
  char *ptr;
159
160
0
  if (!sess) return GF_BAD_PARAM;
161
0
  if (!sess->connection) return GF_IP_NETWORK_EMPTY;
162
163
0
  res = sess->CurrentSize - sess->CurrentPos;
164
0
  if (!res) return gf_rtsp_fill_buffer(sess);
165
166
0
  ptr = (char *)gf_malloc(sizeof(char) * res);
167
0
  memcpy(ptr, sess->tcp_buffer + sess->CurrentPos, res);
168
0
  memcpy(sess->tcp_buffer, ptr, res);
169
0
  gf_free(ptr);
170
171
0
  sess->CurrentPos = 0;
172
0
  sess->CurrentSize = res;
173
174
  //now read from current pos
175
0
  e = gf_rstp_do_read_sock(sess, sess->connection, sess->tcp_buffer + sess->CurrentSize, sess->SockBufferSize - sess->CurrentSize, &res);
176
177
0
  if (!e) {
178
0
    sess->CurrentSize += res;
179
0
  }
180
0
  return e;
181
0
}
182
183
GF_Err gf_rtsp_fill_buffer(GF_RTSPSession *sess)
184
0
{
185
0
  GF_Err e = GF_OK;
186
187
0
  if (!sess->connection) return GF_IP_NETWORK_EMPTY;
188
189
0
  if (sess->CurrentSize == sess->CurrentPos) {
190
0
    if (sess->tunnel_mode==RTSP_HTTP_SERVER) {
191
0
      e = gf_rstp_do_read_sock(sess, sess->http, sess->tcp_buffer, sess->SockBufferSize, &sess->CurrentSize);
192
0
    } else {
193
0
      e = gf_rstp_do_read_sock(sess, sess->connection, sess->tcp_buffer, sess->SockBufferSize, &sess->CurrentSize);
194
0
    }
195
0
    sess->CurrentPos = 0;
196
0
    sess->tcp_buffer[sess->CurrentSize] = 0;
197
0
    if (e) sess->CurrentSize = 0;
198
0
  } else if (!sess->CurrentSize) e = GF_IP_NETWORK_EMPTY;
199
0
  return e;
200
0
}
201
202
203
GF_RTSPTransport *gf_rtsp_transport_parse(u8 *buffer)
204
0
{
205
0
  Bool IsFirst;
206
0
  char buf[100], param_name[100], param_val[100];
207
0
  s32 pos, nPos;
208
0
  u32 v1, v2;
209
0
  GF_RTSPTransport *tmp;
210
0
  if (!buffer) return NULL;
211
  //only support for RTP/AVP for now
212
0
  if (strnicmp(buffer, "RTP/AVP", 7) && strnicmp(buffer, "RTP/SAVP", 8)) return NULL;
213
214
0
  GF_SAFEALLOC(tmp, GF_RTSPTransport);
215
0
  if (!tmp) return NULL;
216
217
0
  IsFirst = GF_TRUE;
218
0
  pos = 0;
219
0
  while (1) {
220
0
    pos = gf_token_get(buffer, pos, " ;", buf, 100);
221
0
    if (pos <= 0) break;
222
0
    if (strstr(buf, "=")) {
223
0
      nPos = gf_token_get(buf, 0, "=", param_name, 100);
224
0
      /*nPos = */gf_token_get(buf, nPos, "=", param_val, 100);
225
0
    } else {
226
0
      strcpy(param_name, buf);
227
0
    }
228
229
    //very first param is the profile
230
0
    if (IsFirst) {
231
0
      tmp->Profile = gf_strdup(param_name);
232
0
      IsFirst = GF_FALSE;
233
0
      continue;
234
0
    }
235
236
0
    if (!stricmp(param_name, "destination")) {
237
0
      if (tmp->destination) gf_free(tmp->destination);
238
0
      tmp->destination = gf_strdup(param_val);
239
0
    }
240
0
    else if (!stricmp(param_name, "source")) {
241
0
      if (tmp->source) gf_free(tmp->source);
242
0
      tmp->source = gf_strdup(param_val);
243
0
    }
244
0
    else if (!stricmp(param_name, "unicast")) tmp->IsUnicast = GF_TRUE;
245
0
    else if (!stricmp(param_name, "RECORD")) tmp->IsRecord = GF_TRUE;
246
0
    else if (!stricmp(param_name, "append")) tmp->Append = GF_TRUE;
247
0
    else if (!stricmp(param_name, "interleaved")) {
248
0
      u32 rID, rcID;
249
0
      tmp->IsInterleaved = GF_TRUE;
250
0
      if (sscanf(param_val, "%u-%u", &rID, &rcID) == 1) {
251
0
        sscanf(param_val, "%u", &rID);
252
0
        tmp->rtcpID = tmp->rtpID = (u8) rID;
253
0
      } else {
254
0
        tmp->rtpID = (u8) rID;
255
0
        tmp->rtcpID = (u8) rcID;
256
0
      }
257
0
    }
258
0
    else if (!stricmp(param_name, "layers")) sscanf(param_val, "%u", &tmp->MulticastLayers);
259
0
    else if (!stricmp(param_name, "ttl")) sscanf(param_val, "%c ", &tmp->TTL);
260
0
    else if (!stricmp(param_name, "port")) {
261
0
      sscanf(param_val, "%u-%u", &v1, &v2);
262
0
      tmp->port_first = (u16) v1;
263
0
      tmp->port_last = (u16) v2;
264
0
    }
265
    /*do not use %hud here, broken on Win32 (sscanf returns 1)*/
266
0
    else if (!stricmp(param_name, "server_port")) {
267
0
      sscanf(param_val, "%d-%d", &v1, &v2);
268
0
      tmp->port_first = (u16) v1;
269
0
      tmp->port_last = (u16) v2;
270
0
    }
271
    /*do not use %hud here, broken on Win32 (sscanf returns 1)*/
272
0
    else if (!stricmp(param_name, "client_port")) {
273
0
      sscanf(param_val, "%d-%d", &v1, &v2);
274
0
      tmp->client_port_first = (u16) v1;
275
0
      tmp->client_port_last = (u16) v2;
276
0
    }
277
0
    else if (!stricmp(param_name, "ssrc")) sscanf(param_val, "%X", &tmp->SSRC);
278
0
  }
279
0
  return tmp;
280
0
}
281
282
283
284
GF_Err gf_rtsp_parse_header(u8 *buffer, u32 BufferSize, u32 BodyStart, GF_RTSPCommand *com, GF_RTSPResponse *rsp)
285
0
{
286
0
  char LineBuffer[1024];
287
0
  char HeaderBuf[100], ValBuf[1024], temp[400];
288
0
  s32 Pos, LinePos;
289
0
  u32 HeaderLine;
290
291
  //then parse the full header
292
0
  LinePos = 0;
293
0
  strcpy(HeaderBuf, "");
294
0
  while (1) {
295
0
    LinePos = gf_token_get_line(buffer, LinePos, BufferSize, LineBuffer, 1024);
296
0
    if (LinePos <= 0) return GF_REMOTE_SERVICE_ERROR;
297
298
    //extract field header and value. Warning: some params (transport, ..) may be on several lines
299
0
    Pos = gf_token_get(LineBuffer, 0, ":\r\n", temp, 400);
300
301
    //end of header
302
0
    if (Pos <= 0) {
303
0
      HeaderLine = 2;
304
0
    }
305
    //this is a header
306
0
    else if (LineBuffer[0] != ' ') {
307
0
      HeaderLine = 1;
308
0
    } else {
309
0
      Pos = gf_token_get(LineBuffer, 0, ", \r\n", temp, 400);
310
      //end of header - process any pending one
311
0
      if (Pos <= 0) {
312
0
        HeaderLine = 2;
313
0
      } else {
314
        //n-line value - append
315
0
        strcat(ValBuf, "\r\n");
316
0
        strcat(ValBuf, temp);
317
0
        continue;
318
0
      }
319
0
    }
320
    //process current value
321
0
    if (HeaderLine && strlen(HeaderBuf)) {
322
0
      if (rsp) {
323
0
        gf_rtsp_set_response_value(rsp, HeaderBuf, ValBuf);
324
0
      }
325
0
      else {
326
0
        gf_rtsp_set_command_value(com, HeaderBuf, ValBuf);
327
0
      }
328
0
    }
329
    //done with the header
330
0
    if ( (HeaderLine == 2) || ((u32) LinePos >= BodyStart) ) return GF_OK;
331
332
    //process current line
333
0
    strcpy(HeaderBuf, temp);
334
335
    //skip ':'
336
0
    Pos += 1;
337
    //a server should normally reply with a space, but check it
338
0
    if (LineBuffer[Pos] == ' ') Pos += 1;
339
    /*!! empty value !! - DSS may send these for CSeq if something goes wrong*/
340
0
    if (!strcmp(LineBuffer+Pos, "\r\n")) {
341
0
      HeaderBuf[0] = 0;
342
0
      continue;
343
0
    }
344
0
    Pos = gf_token_get(LineBuffer, Pos, "\r\n", ValBuf, 400);
345
0
    if (Pos <= 0) break;
346
347
0
  }
348
  //if we get here we haven't reached the BodyStart
349
0
  return GF_REMOTE_SERVICE_ERROR;
350
0
}
351
352
353
GF_EXPORT
354
const char *gf_rtsp_nc_to_string(u32 ErrCode)
355
0
{
356
0
  switch (ErrCode) {
357
0
  case NC_RTSP_Continue:
358
0
    return "Continue";
359
0
  case NC_RTSP_OK:
360
0
    return "OK";
361
0
  case NC_RTSP_Created:
362
0
    return "Created";
363
0
  case NC_RTSP_Low_on_Storage_Space:
364
0
    return "Low on Storage Space";
365
0
  case NC_RTSP_Multiple_Choice:
366
0
    return "Multiple Choice";
367
0
  case NC_RTSP_Moved_Permanently:
368
0
    return "Moved Permanently";
369
0
  case NC_RTSP_Moved_Temporarily:
370
0
    return "Moved Temporarily";
371
0
  case NC_RTSP_See_Other:
372
0
    return "See Other";
373
0
  case NC_RTSP_Use_Proxy:
374
0
    return "Use Proxy";
375
0
  case NC_RTSP_Bad_Request:
376
0
    return "Bad Request";
377
0
  case NC_RTSP_Unauthorized:
378
0
    return "Unauthorized";
379
0
  case NC_RTSP_Payment_Required:
380
0
    return "Payment Required";
381
0
  case NC_RTSP_Forbidden:
382
0
    return "Forbidden";
383
0
  case NC_RTSP_Not_Found:
384
0
    return "Not Found";
385
0
  case NC_RTSP_Method_Not_Allowed:
386
0
    return "Method Not Allowed";
387
0
  case NC_RTSP_Not_Acceptable:
388
0
    return "Not Acceptable";
389
0
  case NC_RTSP_Proxy_Authentication_Required:
390
0
    return "Proxy Authentication Required";
391
0
  case NC_RTSP_Request_Timeout:
392
0
    return "Request Timeout";
393
0
  case NC_RTSP_Gone:
394
0
    return "Gone";
395
0
  case NC_RTSP_Length_Required:
396
0
    return "Length Required";
397
0
  case NC_RTSP_Precondition_Failed:
398
0
    return "Precondition Failed";
399
0
  case NC_RTSP_Request_Entity_Too_Large:
400
0
    return "Request Entity Too Large";
401
0
  case NC_RTSP_Request_URI_Too_Long:
402
0
    return "Request URI Too Long";
403
0
  case NC_RTSP_Unsupported_Media_Type:
404
0
    return "Unsupported Media Type";
405
0
  case NC_RTSP_Invalid_parameter:
406
0
    return "Invalid parameter";
407
0
  case NC_RTSP_Illegal_Conference_Identifier:
408
0
    return "Illegal Conference Identifier";
409
0
  case NC_RTSP_Not_Enough_Bandwidth:
410
0
    return "Not Enough Bandwidth";
411
0
  case NC_RTSP_Session_Not_Found:
412
0
    return "Session Not Found";
413
0
  case NC_RTSP_Method_Not_Valid_In_This_State:
414
0
    return "Method Not Valid In This State";
415
0
  case NC_RTSP_Header_Field_Not_Valid:
416
0
    return "Header Field Not Valid";
417
0
  case NC_RTSP_Invalid_Range:
418
0
    return "Invalid Range";
419
0
  case NC_RTSP_Parameter_Is_ReadOnly:
420
0
    return "Parameter Is Read-Only";
421
0
  case NC_RTSP_Aggregate_Operation_Not_Allowed:
422
0
    return "Aggregate Operation Not Allowed";
423
0
  case NC_RTSP_Only_Aggregate_Operation_Allowed:
424
0
    return "Only Aggregate Operation Allowed";
425
0
  case NC_RTSP_Unsupported_Transport:
426
0
    return "Unsupported Transport";
427
0
  case NC_RTSP_Destination_Unreachable:
428
0
    return "Destination Unreachable";
429
0
  case NC_RTSP_Internal_Server_Error:
430
0
    return "Internal Server Error";
431
0
  case NC_RTSP_Bad_Gateway:
432
0
    return "Bad Gateway";
433
0
  case NC_RTSP_Service_Unavailable:
434
0
    return "Service Unavailable";
435
0
  case NC_RTSP_Gateway_Timeout:
436
0
    return "Gateway Timeout";
437
0
  case NC_RTSP_RTSP_Version_Not_Supported:
438
0
    return "RTSP Version Not Supported";
439
0
  case NC_RTSP_Option_not_support:
440
0
    return "Option not support";
441
442
0
  case NC_RTSP_Not_Implemented:
443
0
  default:
444
0
    return "Not Implemented";
445
0
  }
446
0
}
447
448
#endif /*GPAC_DISABLE_STREAMING*/