Annotation of embedaddon/curl/lib/rtsp.c, revision 1.1.1.1
1.1 misho 1: /***************************************************************************
2: * _ _ ____ _
3: * Project ___| | | | _ \| |
4: * / __| | | | |_) | |
5: * | (__| |_| | _ <| |___
6: * \___|\___/|_| \_\_____|
7: *
8: * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
9: *
10: * This software is licensed as described in the file COPYING, which
11: * you should have received as part of this distribution. The terms
12: * are also available at https://curl.haxx.se/docs/copyright.html.
13: *
14: * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15: * copies of the Software, and permit persons to whom the Software is
16: * furnished to do so, under the terms of the COPYING file.
17: *
18: * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19: * KIND, either express or implied.
20: *
21: ***************************************************************************/
22:
23: #include "curl_setup.h"
24:
25: #ifndef CURL_DISABLE_RTSP
26:
27: #include "urldata.h"
28: #include <curl/curl.h>
29: #include "transfer.h"
30: #include "sendf.h"
31: #include "multiif.h"
32: #include "http.h"
33: #include "url.h"
34: #include "progress.h"
35: #include "rtsp.h"
36: #include "strcase.h"
37: #include "select.h"
38: #include "connect.h"
39: #include "strdup.h"
40: /* The last 3 #include files should be in this order */
41: #include "curl_printf.h"
42: #include "curl_memory.h"
43: #include "memdebug.h"
44:
45: #define RTP_PKT_CHANNEL(p) ((int)((unsigned char)((p)[1])))
46:
47: #define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \
48: ((int)((unsigned char)((p)[3]))))
49:
50: /* protocol-specific functions set up to be called by the main engine */
51: static CURLcode rtsp_do(struct connectdata *conn, bool *done);
52: static CURLcode rtsp_done(struct connectdata *conn, CURLcode, bool premature);
53: static CURLcode rtsp_connect(struct connectdata *conn, bool *done);
54: static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead);
55: static int rtsp_getsock_do(struct connectdata *conn, curl_socket_t *socks);
56:
57: /*
58: * Parse and write out any available RTP data.
59: *
60: * nread: amount of data left after k->str. will be modified if RTP
61: * data is parsed and k->str is moved up
62: * readmore: whether or not the RTP parser needs more data right away
63: */
64: static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
65: struct connectdata *conn,
66: ssize_t *nread,
67: bool *readmore);
68:
69: static CURLcode rtsp_setup_connection(struct connectdata *conn);
70: static unsigned int rtsp_conncheck(struct connectdata *check,
71: unsigned int checks_to_perform);
72:
73: /* this returns the socket to wait for in the DO and DOING state for the multi
74: interface and then we're always _sending_ a request and thus we wait for
75: the single socket to become writable only */
76: static int rtsp_getsock_do(struct connectdata *conn,
77: curl_socket_t *socks)
78: {
79: /* write mode */
80: socks[0] = conn->sock[FIRSTSOCKET];
81: return GETSOCK_WRITESOCK(0);
82: }
83:
84: static
85: CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len);
86:
87:
88: /*
89: * RTSP handler interface.
90: */
91: const struct Curl_handler Curl_handler_rtsp = {
92: "RTSP", /* scheme */
93: rtsp_setup_connection, /* setup_connection */
94: rtsp_do, /* do_it */
95: rtsp_done, /* done */
96: ZERO_NULL, /* do_more */
97: rtsp_connect, /* connect_it */
98: ZERO_NULL, /* connecting */
99: ZERO_NULL, /* doing */
100: ZERO_NULL, /* proto_getsock */
101: rtsp_getsock_do, /* doing_getsock */
102: ZERO_NULL, /* domore_getsock */
103: ZERO_NULL, /* perform_getsock */
104: rtsp_disconnect, /* disconnect */
105: rtsp_rtp_readwrite, /* readwrite */
106: rtsp_conncheck, /* connection_check */
107: PORT_RTSP, /* defport */
108: CURLPROTO_RTSP, /* protocol */
109: PROTOPT_NONE /* flags */
110: };
111:
112:
113: static CURLcode rtsp_setup_connection(struct connectdata *conn)
114: {
115: struct RTSP *rtsp;
116:
117: conn->data->req.protop = rtsp = calloc(1, sizeof(struct RTSP));
118: if(!rtsp)
119: return CURLE_OUT_OF_MEMORY;
120:
121: return CURLE_OK;
122: }
123:
124:
125: /*
126: * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not
127: * want to block the application forever while receiving a stream. Therefore,
128: * we cannot assume that an RTSP socket is dead just because it is readable.
129: *
130: * Instead, if it is readable, run Curl_connalive() to peek at the socket
131: * and distinguish between closed and data.
132: */
133: static bool rtsp_connisdead(struct connectdata *check)
134: {
135: int sval;
136: bool ret_val = TRUE;
137:
138: sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0);
139: if(sval == 0) {
140: /* timeout */
141: ret_val = FALSE;
142: }
143: else if(sval & CURL_CSELECT_ERR) {
144: /* socket is in an error state */
145: ret_val = TRUE;
146: }
147: else if(sval & CURL_CSELECT_IN) {
148: /* readable with no error. could still be closed */
149: ret_val = !Curl_connalive(check);
150: }
151:
152: return ret_val;
153: }
154:
155: /*
156: * Function to check on various aspects of a connection.
157: */
158: static unsigned int rtsp_conncheck(struct connectdata *check,
159: unsigned int checks_to_perform)
160: {
161: unsigned int ret_val = CONNRESULT_NONE;
162:
163: if(checks_to_perform & CONNCHECK_ISDEAD) {
164: if(rtsp_connisdead(check))
165: ret_val |= CONNRESULT_DEAD;
166: }
167:
168: return ret_val;
169: }
170:
171:
172: static CURLcode rtsp_connect(struct connectdata *conn, bool *done)
173: {
174: CURLcode httpStatus;
175: struct Curl_easy *data = conn->data;
176:
177: httpStatus = Curl_http_connect(conn, done);
178:
179: /* Initialize the CSeq if not already done */
180: if(data->state.rtsp_next_client_CSeq == 0)
181: data->state.rtsp_next_client_CSeq = 1;
182: if(data->state.rtsp_next_server_CSeq == 0)
183: data->state.rtsp_next_server_CSeq = 1;
184:
185: conn->proto.rtspc.rtp_channel = -1;
186:
187: return httpStatus;
188: }
189:
190: static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead)
191: {
192: (void) dead;
193: Curl_safefree(conn->proto.rtspc.rtp_buf);
194: return CURLE_OK;
195: }
196:
197:
198: static CURLcode rtsp_done(struct connectdata *conn,
199: CURLcode status, bool premature)
200: {
201: struct Curl_easy *data = conn->data;
202: struct RTSP *rtsp = data->req.protop;
203: CURLcode httpStatus;
204:
205: /* Bypass HTTP empty-reply checks on receive */
206: if(data->set.rtspreq == RTSPREQ_RECEIVE)
207: premature = TRUE;
208:
209: httpStatus = Curl_http_done(conn, status, premature);
210:
211: if(rtsp) {
212: /* Check the sequence numbers */
213: long CSeq_sent = rtsp->CSeq_sent;
214: long CSeq_recv = rtsp->CSeq_recv;
215: if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) {
216: failf(data,
217: "The CSeq of this request %ld did not match the response %ld",
218: CSeq_sent, CSeq_recv);
219: return CURLE_RTSP_CSEQ_ERROR;
220: }
221: if(data->set.rtspreq == RTSPREQ_RECEIVE &&
222: (conn->proto.rtspc.rtp_channel == -1)) {
223: infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv);
224: }
225: }
226:
227: return httpStatus;
228: }
229:
230: static CURLcode rtsp_do(struct connectdata *conn, bool *done)
231: {
232: struct Curl_easy *data = conn->data;
233: CURLcode result = CURLE_OK;
234: Curl_RtspReq rtspreq = data->set.rtspreq;
235: struct RTSP *rtsp = data->req.protop;
236: Curl_send_buffer *req_buffer;
237: curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */
238: curl_off_t putsize = 0; /* for ANNOUNCE and SET_PARAMETER */
239:
240: const char *p_request = NULL;
241: const char *p_session_id = NULL;
242: const char *p_accept = NULL;
243: const char *p_accept_encoding = NULL;
244: const char *p_range = NULL;
245: const char *p_referrer = NULL;
246: const char *p_stream_uri = NULL;
247: const char *p_transport = NULL;
248: const char *p_uagent = NULL;
249: const char *p_proxyuserpwd = NULL;
250: const char *p_userpwd = NULL;
251:
252: *done = TRUE;
253:
254: rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
255: rtsp->CSeq_recv = 0;
256:
257: /* Setup the 'p_request' pointer to the proper p_request string
258: * Since all RTSP requests are included here, there is no need to
259: * support custom requests like HTTP.
260: **/
261: data->set.opt_no_body = TRUE; /* most requests don't contain a body */
262: switch(rtspreq) {
263: default:
264: failf(data, "Got invalid RTSP request");
265: return CURLE_BAD_FUNCTION_ARGUMENT;
266: case RTSPREQ_OPTIONS:
267: p_request = "OPTIONS";
268: break;
269: case RTSPREQ_DESCRIBE:
270: p_request = "DESCRIBE";
271: data->set.opt_no_body = FALSE;
272: break;
273: case RTSPREQ_ANNOUNCE:
274: p_request = "ANNOUNCE";
275: break;
276: case RTSPREQ_SETUP:
277: p_request = "SETUP";
278: break;
279: case RTSPREQ_PLAY:
280: p_request = "PLAY";
281: break;
282: case RTSPREQ_PAUSE:
283: p_request = "PAUSE";
284: break;
285: case RTSPREQ_TEARDOWN:
286: p_request = "TEARDOWN";
287: break;
288: case RTSPREQ_GET_PARAMETER:
289: /* GET_PARAMETER's no_body status is determined later */
290: p_request = "GET_PARAMETER";
291: data->set.opt_no_body = FALSE;
292: break;
293: case RTSPREQ_SET_PARAMETER:
294: p_request = "SET_PARAMETER";
295: break;
296: case RTSPREQ_RECORD:
297: p_request = "RECORD";
298: break;
299: case RTSPREQ_RECEIVE:
300: p_request = "";
301: /* Treat interleaved RTP as body*/
302: data->set.opt_no_body = FALSE;
303: break;
304: case RTSPREQ_LAST:
305: failf(data, "Got invalid RTSP request: RTSPREQ_LAST");
306: return CURLE_BAD_FUNCTION_ARGUMENT;
307: }
308:
309: if(rtspreq == RTSPREQ_RECEIVE) {
310: Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
311:
312: return result;
313: }
314:
315: p_session_id = data->set.str[STRING_RTSP_SESSION_ID];
316: if(!p_session_id &&
317: (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) {
318: failf(data, "Refusing to issue an RTSP request [%s] without a session ID.",
319: p_request);
320: return CURLE_BAD_FUNCTION_ARGUMENT;
321: }
322:
323: /* Stream URI. Default to server '*' if not specified */
324: if(data->set.str[STRING_RTSP_STREAM_URI]) {
325: p_stream_uri = data->set.str[STRING_RTSP_STREAM_URI];
326: }
327: else {
328: p_stream_uri = "*";
329: }
330:
331: /* Transport Header for SETUP requests */
332: p_transport = Curl_checkheaders(conn, "Transport");
333: if(rtspreq == RTSPREQ_SETUP && !p_transport) {
334: /* New Transport: setting? */
335: if(data->set.str[STRING_RTSP_TRANSPORT]) {
336: Curl_safefree(conn->allocptr.rtsp_transport);
337:
338: conn->allocptr.rtsp_transport =
339: aprintf("Transport: %s\r\n",
340: data->set.str[STRING_RTSP_TRANSPORT]);
341: if(!conn->allocptr.rtsp_transport)
342: return CURLE_OUT_OF_MEMORY;
343: }
344: else {
345: failf(data,
346: "Refusing to issue an RTSP SETUP without a Transport: header.");
347: return CURLE_BAD_FUNCTION_ARGUMENT;
348: }
349:
350: p_transport = conn->allocptr.rtsp_transport;
351: }
352:
353: /* Accept Headers for DESCRIBE requests */
354: if(rtspreq == RTSPREQ_DESCRIBE) {
355: /* Accept Header */
356: p_accept = Curl_checkheaders(conn, "Accept")?
357: NULL:"Accept: application/sdp\r\n";
358:
359: /* Accept-Encoding header */
360: if(!Curl_checkheaders(conn, "Accept-Encoding") &&
361: data->set.str[STRING_ENCODING]) {
362: Curl_safefree(conn->allocptr.accept_encoding);
363: conn->allocptr.accept_encoding =
364: aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
365:
366: if(!conn->allocptr.accept_encoding)
367: return CURLE_OUT_OF_MEMORY;
368:
369: p_accept_encoding = conn->allocptr.accept_encoding;
370: }
371: }
372:
373: /* The User-Agent string might have been allocated in url.c already, because
374: it might have been used in the proxy connect, but if we have got a header
375: with the user-agent string specified, we erase the previously made string
376: here. */
377: if(Curl_checkheaders(conn, "User-Agent") && conn->allocptr.uagent) {
378: Curl_safefree(conn->allocptr.uagent);
379: conn->allocptr.uagent = NULL;
380: }
381: else if(!Curl_checkheaders(conn, "User-Agent") &&
382: data->set.str[STRING_USERAGENT]) {
383: p_uagent = conn->allocptr.uagent;
384: }
385:
386: /* setup the authentication headers */
387: result = Curl_http_output_auth(conn, p_request, p_stream_uri, FALSE);
388: if(result)
389: return result;
390:
391: p_proxyuserpwd = conn->allocptr.proxyuserpwd;
392: p_userpwd = conn->allocptr.userpwd;
393:
394: /* Referrer */
395: Curl_safefree(conn->allocptr.ref);
396: if(data->change.referer && !Curl_checkheaders(conn, "Referer"))
397: conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
398: else
399: conn->allocptr.ref = NULL;
400:
401: p_referrer = conn->allocptr.ref;
402:
403: /*
404: * Range Header
405: * Only applies to PLAY, PAUSE, RECORD
406: *
407: * Go ahead and use the Range stuff supplied for HTTP
408: */
409: if(data->state.use_range &&
410: (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) {
411:
412: /* Check to see if there is a range set in the custom headers */
413: if(!Curl_checkheaders(conn, "Range") && data->state.range) {
414: Curl_safefree(conn->allocptr.rangeline);
415: conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
416: p_range = conn->allocptr.rangeline;
417: }
418: }
419:
420: /*
421: * Sanity check the custom headers
422: */
423: if(Curl_checkheaders(conn, "CSeq")) {
424: failf(data, "CSeq cannot be set as a custom header.");
425: return CURLE_RTSP_CSEQ_ERROR;
426: }
427: if(Curl_checkheaders(conn, "Session")) {
428: failf(data, "Session ID cannot be set as a custom header.");
429: return CURLE_BAD_FUNCTION_ARGUMENT;
430: }
431:
432: /* Initialize a dynamic send buffer */
433: req_buffer = Curl_add_buffer_init();
434:
435: if(!req_buffer)
436: return CURLE_OUT_OF_MEMORY;
437:
438: result =
439: Curl_add_bufferf(&req_buffer,
440: "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
441: "CSeq: %ld\r\n", /* CSeq */
442: p_request, p_stream_uri, rtsp->CSeq_sent);
443: if(result)
444: return result;
445:
446: /*
447: * Rather than do a normal alloc line, keep the session_id unformatted
448: * to make comparison easier
449: */
450: if(p_session_id) {
451: result = Curl_add_bufferf(&req_buffer, "Session: %s\r\n", p_session_id);
452: if(result)
453: return result;
454: }
455:
456: /*
457: * Shared HTTP-like options
458: */
459: result = Curl_add_bufferf(&req_buffer,
460: "%s" /* transport */
461: "%s" /* accept */
462: "%s" /* accept-encoding */
463: "%s" /* range */
464: "%s" /* referrer */
465: "%s" /* user-agent */
466: "%s" /* proxyuserpwd */
467: "%s" /* userpwd */
468: ,
469: p_transport ? p_transport : "",
470: p_accept ? p_accept : "",
471: p_accept_encoding ? p_accept_encoding : "",
472: p_range ? p_range : "",
473: p_referrer ? p_referrer : "",
474: p_uagent ? p_uagent : "",
475: p_proxyuserpwd ? p_proxyuserpwd : "",
476: p_userpwd ? p_userpwd : "");
477:
478: /*
479: * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
480: * with basic and digest, it will be freed anyway by the next request
481: */
482: Curl_safefree(conn->allocptr.userpwd);
483: conn->allocptr.userpwd = NULL;
484:
485: if(result)
486: return result;
487:
488: if((rtspreq == RTSPREQ_SETUP) || (rtspreq == RTSPREQ_DESCRIBE)) {
489: result = Curl_add_timecondition(conn, req_buffer);
490: if(result)
491: return result;
492: }
493:
494: result = Curl_add_custom_headers(conn, FALSE, req_buffer);
495: if(result)
496: return result;
497:
498: if(rtspreq == RTSPREQ_ANNOUNCE ||
499: rtspreq == RTSPREQ_SET_PARAMETER ||
500: rtspreq == RTSPREQ_GET_PARAMETER) {
501:
502: if(data->set.upload) {
503: putsize = data->state.infilesize;
504: data->set.httpreq = HTTPREQ_PUT;
505:
506: }
507: else {
508: postsize = (data->state.infilesize != -1)?
509: data->state.infilesize:
510: (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);
511: data->set.httpreq = HTTPREQ_POST;
512: }
513:
514: if(putsize > 0 || postsize > 0) {
515: /* As stated in the http comments, it is probably not wise to
516: * actually set a custom Content-Length in the headers */
517: if(!Curl_checkheaders(conn, "Content-Length")) {
518: result =
519: Curl_add_bufferf(&req_buffer,
520: "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
521: (data->set.upload ? putsize : postsize));
522: if(result)
523: return result;
524: }
525:
526: if(rtspreq == RTSPREQ_SET_PARAMETER ||
527: rtspreq == RTSPREQ_GET_PARAMETER) {
528: if(!Curl_checkheaders(conn, "Content-Type")) {
529: result = Curl_add_bufferf(&req_buffer,
530: "Content-Type: text/parameters\r\n");
531: if(result)
532: return result;
533: }
534: }
535:
536: if(rtspreq == RTSPREQ_ANNOUNCE) {
537: if(!Curl_checkheaders(conn, "Content-Type")) {
538: result = Curl_add_bufferf(&req_buffer,
539: "Content-Type: application/sdp\r\n");
540: if(result)
541: return result;
542: }
543: }
544:
545: data->state.expect100header = FALSE; /* RTSP posts are simple/small */
546: }
547: else if(rtspreq == RTSPREQ_GET_PARAMETER) {
548: /* Check for an empty GET_PARAMETER (heartbeat) request */
549: data->set.httpreq = HTTPREQ_HEAD;
550: data->set.opt_no_body = TRUE;
551: }
552: }
553:
554: /* RTSP never allows chunked transfer */
555: data->req.forbidchunk = TRUE;
556: /* Finish the request buffer */
557: result = Curl_add_buffer(&req_buffer, "\r\n", 2);
558: if(result)
559: return result;
560:
561: if(postsize > 0) {
562: result = Curl_add_buffer(&req_buffer, data->set.postfields,
563: (size_t)postsize);
564: if(result)
565: return result;
566: }
567:
568: /* issue the request */
569: result = Curl_add_buffer_send(&req_buffer, conn,
570: &data->info.request_size, 0, FIRSTSOCKET);
571: if(result) {
572: failf(data, "Failed sending RTSP request");
573: return result;
574: }
575:
576: Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, putsize?FIRSTSOCKET:-1);
577:
578: /* Increment the CSeq on success */
579: data->state.rtsp_next_client_CSeq++;
580:
581: if(data->req.writebytecount) {
582: /* if a request-body has been sent off, we make sure this progress is
583: noted properly */
584: Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
585: if(Curl_pgrsUpdate(conn))
586: result = CURLE_ABORTED_BY_CALLBACK;
587: }
588:
589: return result;
590: }
591:
592:
593: static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
594: struct connectdata *conn,
595: ssize_t *nread,
596: bool *readmore) {
597: struct SingleRequest *k = &data->req;
598: struct rtsp_conn *rtspc = &(conn->proto.rtspc);
599:
600: char *rtp; /* moving pointer to rtp data */
601: ssize_t rtp_dataleft; /* how much data left to parse in this round */
602: char *scratch;
603: CURLcode result;
604:
605: if(rtspc->rtp_buf) {
606: /* There was some leftover data the last time. Merge buffers */
607: char *newptr = Curl_saferealloc(rtspc->rtp_buf,
608: rtspc->rtp_bufsize + *nread);
609: if(!newptr) {
610: rtspc->rtp_buf = NULL;
611: rtspc->rtp_bufsize = 0;
612: return CURLE_OUT_OF_MEMORY;
613: }
614: rtspc->rtp_buf = newptr;
615: memcpy(rtspc->rtp_buf + rtspc->rtp_bufsize, k->str, *nread);
616: rtspc->rtp_bufsize += *nread;
617: rtp = rtspc->rtp_buf;
618: rtp_dataleft = rtspc->rtp_bufsize;
619: }
620: else {
621: /* Just parse the request buffer directly */
622: rtp = k->str;
623: rtp_dataleft = *nread;
624: }
625:
626: while((rtp_dataleft > 0) &&
627: (rtp[0] == '$')) {
628: if(rtp_dataleft > 4) {
629: int rtp_length;
630:
631: /* Parse the header */
632: /* The channel identifier immediately follows and is 1 byte */
633: rtspc->rtp_channel = RTP_PKT_CHANNEL(rtp);
634:
635: /* The length is two bytes */
636: rtp_length = RTP_PKT_LENGTH(rtp);
637:
638: if(rtp_dataleft < rtp_length + 4) {
639: /* Need more - incomplete payload*/
640: *readmore = TRUE;
641: break;
642: }
643: /* We have the full RTP interleaved packet
644: * Write out the header including the leading '$' */
645: DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n",
646: rtspc->rtp_channel, rtp_length));
647: result = rtp_client_write(conn, &rtp[0], rtp_length + 4);
648: if(result) {
649: failf(data, "Got an error writing an RTP packet");
650: *readmore = FALSE;
651: Curl_safefree(rtspc->rtp_buf);
652: rtspc->rtp_buf = NULL;
653: rtspc->rtp_bufsize = 0;
654: return result;
655: }
656:
657: /* Move forward in the buffer */
658: rtp_dataleft -= rtp_length + 4;
659: rtp += rtp_length + 4;
660:
661: if(data->set.rtspreq == RTSPREQ_RECEIVE) {
662: /* If we are in a passive receive, give control back
663: * to the app as often as we can.
664: */
665: k->keepon &= ~KEEP_RECV;
666: }
667: }
668: else {
669: /* Need more - incomplete header */
670: *readmore = TRUE;
671: break;
672: }
673: }
674:
675: if(rtp_dataleft != 0 && rtp[0] == '$') {
676: DEBUGF(infof(data, "RTP Rewinding %zd %s\n", rtp_dataleft,
677: *readmore ? "(READMORE)" : ""));
678:
679: /* Store the incomplete RTP packet for a "rewind" */
680: scratch = malloc(rtp_dataleft);
681: if(!scratch) {
682: Curl_safefree(rtspc->rtp_buf);
683: rtspc->rtp_buf = NULL;
684: rtspc->rtp_bufsize = 0;
685: return CURLE_OUT_OF_MEMORY;
686: }
687: memcpy(scratch, rtp, rtp_dataleft);
688: Curl_safefree(rtspc->rtp_buf);
689: rtspc->rtp_buf = scratch;
690: rtspc->rtp_bufsize = rtp_dataleft;
691:
692: /* As far as the transfer is concerned, this data is consumed */
693: *nread = 0;
694: return CURLE_OK;
695: }
696: /* Fix up k->str to point just after the last RTP packet */
697: k->str += *nread - rtp_dataleft;
698:
699: /* either all of the data has been read or...
700: * rtp now points at the next byte to parse
701: */
702: if(rtp_dataleft > 0)
703: DEBUGASSERT(k->str[0] == rtp[0]);
704:
705: DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */
706:
707: *nread = rtp_dataleft;
708:
709: /* If we get here, we have finished with the leftover/merge buffer */
710: Curl_safefree(rtspc->rtp_buf);
711: rtspc->rtp_buf = NULL;
712: rtspc->rtp_bufsize = 0;
713:
714: return CURLE_OK;
715: }
716:
717: static
718: CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len)
719: {
720: struct Curl_easy *data = conn->data;
721: size_t wrote;
722: curl_write_callback writeit;
723: void *user_ptr;
724:
725: if(len == 0) {
726: failf(data, "Cannot write a 0 size RTP packet.");
727: return CURLE_WRITE_ERROR;
728: }
729:
730: /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that
731: function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP
732: data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA
733: pointer to write out the RTP data. */
734: if(data->set.fwrite_rtp) {
735: writeit = data->set.fwrite_rtp;
736: user_ptr = data->set.rtp_out;
737: }
738: else {
739: writeit = data->set.fwrite_func;
740: user_ptr = data->set.out;
741: }
742:
743: Curl_set_in_callback(data, true);
744: wrote = writeit(ptr, 1, len, user_ptr);
745: Curl_set_in_callback(data, false);
746:
747: if(CURL_WRITEFUNC_PAUSE == wrote) {
748: failf(data, "Cannot pause RTP");
749: return CURLE_WRITE_ERROR;
750: }
751:
752: if(wrote != len) {
753: failf(data, "Failed writing RTP data");
754: return CURLE_WRITE_ERROR;
755: }
756:
757: return CURLE_OK;
758: }
759:
760: CURLcode Curl_rtsp_parseheader(struct connectdata *conn,
761: char *header)
762: {
763: struct Curl_easy *data = conn->data;
764: long CSeq = 0;
765:
766: if(checkprefix("CSeq:", header)) {
767: /* Store the received CSeq. Match is verified in rtsp_done */
768: int nc = sscanf(&header[4], ": %ld", &CSeq);
769: if(nc == 1) {
770: struct RTSP *rtsp = data->req.protop;
771: rtsp->CSeq_recv = CSeq; /* mark the request */
772: data->state.rtsp_CSeq_recv = CSeq; /* update the handle */
773: }
774: else {
775: failf(data, "Unable to read the CSeq header: [%s]", header);
776: return CURLE_RTSP_CSEQ_ERROR;
777: }
778: }
779: else if(checkprefix("Session:", header)) {
780: char *start;
781:
782: /* Find the first non-space letter */
783: start = header + 8;
784: while(*start && ISSPACE(*start))
785: start++;
786:
787: if(!*start) {
788: failf(data, "Got a blank Session ID");
789: }
790: else if(data->set.str[STRING_RTSP_SESSION_ID]) {
791: /* If the Session ID is set, then compare */
792: if(strncmp(start, data->set.str[STRING_RTSP_SESSION_ID],
793: strlen(data->set.str[STRING_RTSP_SESSION_ID])) != 0) {
794: failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]",
795: start, data->set.str[STRING_RTSP_SESSION_ID]);
796: return CURLE_RTSP_SESSION_ERROR;
797: }
798: }
799: else {
800: /* If the Session ID is not set, and we find it in a response, then set
801: * it.
802: *
803: * Allow any non whitespace content, up to the field separator or end of
804: * line. RFC 2326 isn't 100% clear on the session ID and for example
805: * gstreamer does url-encoded session ID's not covered by the standard.
806: */
807: char *end = start;
808: while(*end && *end != ';' && !ISSPACE(*end))
809: end++;
810:
811: /* Copy the id substring into a new buffer */
812: data->set.str[STRING_RTSP_SESSION_ID] = malloc(end - start + 1);
813: if(data->set.str[STRING_RTSP_SESSION_ID] == NULL)
814: return CURLE_OUT_OF_MEMORY;
815: memcpy(data->set.str[STRING_RTSP_SESSION_ID], start, end - start);
816: (data->set.str[STRING_RTSP_SESSION_ID])[end - start] = '\0';
817: }
818: }
819: return CURLE_OK;
820: }
821:
822: #endif /* CURL_DISABLE_RTSP */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>