diff options
Diffstat (limited to 'libs/libcurl/src/rtsp.c')
-rw-r--r-- | libs/libcurl/src/rtsp.c | 251 |
1 files changed, 146 insertions, 105 deletions
diff --git a/libs/libcurl/src/rtsp.c b/libs/libcurl/src/rtsp.c index f7c6562a7b..925da2c1a9 100644 --- a/libs/libcurl/src/rtsp.c +++ b/libs/libcurl/src/rtsp.c @@ -5,11 +5,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -33,15 +33,13 @@ #include "url.h" #include "progress.h" #include "rtsp.h" -#include "rawstr.h" -#include "curl_memory.h" +#include "strcase.h" #include "select.h" #include "connect.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - -/* The last #include file should be: */ +#include "strdup.h" +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" #include "memdebug.h" /* @@ -76,13 +74,16 @@ static int rtsp_getsock_do(struct connectdata *conn, * data is parsed and k->str is moved up * readmore: whether or not the RTP parser needs more data right away */ -static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data, +static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, struct connectdata *conn, ssize_t *nread, bool *readmore); static CURLcode rtsp_setup_connection(struct connectdata *conn); +bool rtsp_connisdead(struct connectdata *check); +static unsigned int rtsp_conncheck(struct connectdata *check, + unsigned int checks_to_perform); /* this returns the socket to wait for in the DO and DOING state for the multi interface and then we're always _sending_ a request and thus we wait for @@ -119,6 +120,7 @@ const struct Curl_handler Curl_handler_rtsp = { ZERO_NULL, /* perform_getsock */ rtsp_disconnect, /* disconnect */ rtsp_rtp_readwrite, /* readwrite */ + rtsp_conncheck, /* connection_check */ PORT_RTSP, /* defport */ CURLPROTO_RTSP, /* protocol */ PROTOPT_NONE /* flags */ @@ -142,15 +144,15 @@ static CURLcode rtsp_setup_connection(struct connectdata *conn) * want to block the application forever while receiving a stream. Therefore, * we cannot assume that an RTSP socket is dead just because it is readable. * - * Instead, if it is readable, run Curl_getconnectinfo() to peek at the socket + * Instead, if it is readable, run Curl_connalive() to peek at the socket * and distinguish between closed and data. */ -bool Curl_rtsp_connisdead(struct connectdata *check) +bool rtsp_connisdead(struct connectdata *check) { int sval; bool ret_val = TRUE; - sval = Curl_socket_ready(check->sock[FIRSTSOCKET], CURL_SOCKET_BAD, 0); + sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0); if(sval == 0) { /* timeout */ ret_val = FALSE; @@ -159,21 +161,35 @@ bool Curl_rtsp_connisdead(struct connectdata *check) /* socket is in an error state */ ret_val = TRUE; } - else if((sval & CURL_CSELECT_IN) && check->data) { - /* readable with no error. could be closed or could be alive but we can - only check if we have a proper SessionHandle for the connection */ - curl_socket_t connectinfo = Curl_getconnectinfo(check->data, &check); - if(connectinfo != CURL_SOCKET_BAD) - ret_val = FALSE; + else if(sval & CURL_CSELECT_IN) { + /* readable with no error. could still be closed */ + ret_val = !Curl_connalive(check); + } + + return ret_val; +} + +/* + * Function to check on various aspects of a connection. + */ +static unsigned int rtsp_conncheck(struct connectdata *check, + unsigned int checks_to_perform) +{ + unsigned int ret_val = CONNRESULT_NONE; + + if(checks_to_perform & CONNCHECK_ISDEAD) { + if(rtsp_connisdead(check)) + ret_val |= CONNRESULT_DEAD; } return ret_val; } + static CURLcode rtsp_connect(struct connectdata *conn, bool *done) { CURLcode httpStatus; - struct SessionHandle *data = conn->data; + struct Curl_easy *data = conn->data; httpStatus = Curl_http_connect(conn, done); @@ -199,7 +215,7 @@ static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead) static CURLcode rtsp_done(struct connectdata *conn, CURLcode status, bool premature) { - struct SessionHandle *data = conn->data; + struct Curl_easy *data = conn->data; struct RTSP *rtsp = data->req.protop; CURLcode httpStatus; long CSeq_sent; @@ -221,7 +237,7 @@ static CURLcode rtsp_done(struct connectdata *conn, CSeq_sent, CSeq_recv); return CURLE_RTSP_CSEQ_ERROR; } - else if(data->set.rtspreq == RTSPREQ_RECEIVE && + if(data->set.rtspreq == RTSPREQ_RECEIVE && (conn->proto.rtspc.rtp_channel == -1)) { infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv); /* TODO CPC: Server -> Client logic here */ @@ -233,8 +249,8 @@ static CURLcode rtsp_done(struct connectdata *conn, static CURLcode rtsp_do(struct connectdata *conn, bool *done) { - struct SessionHandle *data = conn->data; - CURLcode result=CURLE_OK; + struct Curl_easy *data = conn->data; + CURLcode result = CURLE_OK; Curl_RtspReq rtspreq = data->set.rtspreq; struct RTSP *rtsp = data->req.protop; struct HTTP *http; @@ -251,6 +267,8 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) const char *p_stream_uri = NULL; const char *p_transport = NULL; const char *p_uagent = NULL; + const char *p_proxyuserpwd = NULL; + const char *p_userpwd = NULL; *done = TRUE; @@ -265,11 +283,10 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) * Since all RTSP requests are included here, there is no need to * support custom requests like HTTP. **/ - DEBUGASSERT((rtspreq > RTSPREQ_NONE && rtspreq < RTSPREQ_LAST)); data->set.opt_no_body = TRUE; /* most requests don't contain a body */ switch(rtspreq) { - case RTSPREQ_NONE: - failf(data, "Got invalid RTSP request: RTSPREQ_NONE"); + default: + failf(data, "Got invalid RTSP request"); return CURLE_BAD_FUNCTION_ARGUMENT; case RTSPREQ_OPTIONS: p_request = "OPTIONS"; @@ -325,11 +342,10 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(!p_session_id && (rtspreq & ~(RTSPREQ_OPTIONS | RTSPREQ_DESCRIBE | RTSPREQ_SETUP))) { failf(data, "Refusing to issue an RTSP request [%s] without a session ID.", - p_request ? p_request : ""); + p_request); return CURLE_BAD_FUNCTION_ARGUMENT; } - /* TODO: auth? */ /* TODO: proxy? */ /* Stream URI. Default to server '*' if not specified */ @@ -341,7 +357,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) } /* Transport Header for SETUP requests */ - p_transport = Curl_checkheaders(data, "Transport:"); + p_transport = Curl_checkheaders(conn, "Transport:"); if(rtspreq == RTSPREQ_SETUP && !p_transport) { /* New Transport: setting? */ if(data->set.str[STRING_RTSP_TRANSPORT]) { @@ -365,11 +381,11 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) /* Accept Headers for DESCRIBE requests */ if(rtspreq == RTSPREQ_DESCRIBE) { /* Accept Header */ - p_accept = Curl_checkheaders(data, "Accept:")? + p_accept = Curl_checkheaders(conn, "Accept:")? NULL:"Accept: application/sdp\r\n"; /* Accept-Encoding header */ - if(!Curl_checkheaders(data, "Accept-Encoding:") && + if(!Curl_checkheaders(conn, "Accept-Encoding:") && data->set.str[STRING_ENCODING]) { Curl_safefree(conn->allocptr.accept_encoding); conn->allocptr.accept_encoding = @@ -386,18 +402,26 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ - if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) { + if(Curl_checkheaders(conn, "User-Agent:") && conn->allocptr.uagent) { Curl_safefree(conn->allocptr.uagent); conn->allocptr.uagent = NULL; } - else if(!Curl_checkheaders(data, "User-Agent:") && + else if(!Curl_checkheaders(conn, "User-Agent:") && data->set.str[STRING_USERAGENT]) { p_uagent = conn->allocptr.uagent; } + /* setup the authentication headers */ + result = Curl_http_output_auth(conn, p_request, p_stream_uri, FALSE); + if(result) + return result; + + p_proxyuserpwd = conn->allocptr.proxyuserpwd; + p_userpwd = conn->allocptr.userpwd; + /* Referrer */ Curl_safefree(conn->allocptr.ref); - if(data->change.referer && !Curl_checkheaders(data, "Referer:")) + if(data->change.referer && !Curl_checkheaders(conn, "Referer:")) conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer); else conn->allocptr.ref = NULL; @@ -414,7 +438,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) { /* Check to see if there is a range set in the custom headers */ - if(!Curl_checkheaders(data, "Range:") && data->state.range) { + if(!Curl_checkheaders(conn, "Range:") && data->state.range) { Curl_safefree(conn->allocptr.rangeline); conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range); p_range = conn->allocptr.rangeline; @@ -424,11 +448,11 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) /* * Sanity check the custom headers */ - if(Curl_checkheaders(data, "CSeq:")) { + if(Curl_checkheaders(conn, "CSeq:")) { failf(data, "CSeq cannot be set as a custom header."); return CURLE_RTSP_CSEQ_ERROR; } - if(Curl_checkheaders(data, "Session:")) { + if(Curl_checkheaders(conn, "Session:")) { failf(data, "Session ID cannot be set as a custom header."); return CURLE_BAD_FUNCTION_ARGUMENT; } @@ -443,8 +467,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) Curl_add_bufferf(req_buffer, "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */ "CSeq: %ld\r\n", /* CSeq */ - (p_request ? p_request : ""), p_stream_uri, - rtsp->CSeq_sent); + p_request, p_stream_uri, rtsp->CSeq_sent); if(result) return result; @@ -468,13 +491,25 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) "%s" /* range */ "%s" /* referrer */ "%s" /* user-agent */ + "%s" /* proxyuserpwd */ + "%s" /* userpwd */ , p_transport ? p_transport : "", p_accept ? p_accept : "", p_accept_encoding ? p_accept_encoding : "", p_range ? p_range : "", p_referrer ? p_referrer : "", - p_uagent ? p_uagent : ""); + p_uagent ? p_uagent : "", + p_proxyuserpwd ? p_proxyuserpwd : "", + p_userpwd ? p_userpwd : ""); + + /* + * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM + * with basic and digest, it will be freed anyway by the next request + */ + Curl_safefree(conn->allocptr.userpwd); + conn->allocptr.userpwd = NULL; + if(result) return result; @@ -484,7 +519,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) return result; } - result = Curl_add_custom_headers(conn, req_buffer); + result = Curl_add_custom_headers(conn, FALSE, req_buffer); if(result) return result; @@ -493,13 +528,13 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) rtspreq == RTSPREQ_GET_PARAMETER) { if(data->set.upload) { - putsize = data->set.infilesize; + putsize = data->state.infilesize; data->set.httpreq = HTTPREQ_PUT; } else { - postsize = (data->set.postfieldsize != -1)? - data->set.postfieldsize: + postsize = (data->state.infilesize != -1)? + data->state.infilesize: (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0); data->set.httpreq = HTTPREQ_POST; } @@ -507,9 +542,9 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(putsize > 0 || postsize > 0) { /* As stated in the http comments, it is probably not wise to * actually set a custom Content-Length in the headers */ - if(!Curl_checkheaders(data, "Content-Length:")) { + if(!Curl_checkheaders(conn, "Content-Length:")) { result = Curl_add_bufferf(req_buffer, - "Content-Length: %" FORMAT_OFF_T"\r\n", + "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", (data->set.upload ? putsize : postsize)); if(result) return result; @@ -517,7 +552,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(rtspreq == RTSPREQ_SET_PARAMETER || rtspreq == RTSPREQ_GET_PARAMETER) { - if(!Curl_checkheaders(data, "Content-Type:")) { + if(!Curl_checkheaders(conn, "Content-Type:")) { result = Curl_add_bufferf(req_buffer, "Content-Type: text/parameters\r\n"); if(result) @@ -526,7 +561,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) } if(rtspreq == RTSPREQ_ANNOUNCE) { - if(!Curl_checkheaders(data, "Content-Type:")) { + if(!Curl_checkheaders(conn, "Content-Type:")) { result = Curl_add_bufferf(req_buffer, "Content-Type: application/sdp\r\n"); if(result) @@ -584,7 +619,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) } -static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data, +static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data, struct connectdata *conn, ssize_t *nread, bool *readmore) { @@ -598,9 +633,9 @@ static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data, if(rtspc->rtp_buf) { /* There was some leftover data the last time. Merge buffers */ - char *newptr = realloc(rtspc->rtp_buf, rtspc->rtp_bufsize + *nread); + char *newptr = Curl_saferealloc(rtspc->rtp_buf, + rtspc->rtp_bufsize + *nread); if(!newptr) { - Curl_safefree(rtspc->rtp_buf); rtspc->rtp_buf = NULL; rtspc->rtp_bufsize = 0; return CURLE_OUT_OF_MEMORY; @@ -634,31 +669,29 @@ static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data, *readmore = TRUE; break; } - else { - /* We have the full RTP interleaved packet - * Write out the header including the leading '$' */ - DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n", - rtspc->rtp_channel, rtp_length)); - result = rtp_client_write(conn, &rtp[0], rtp_length + 4); - if(result) { - failf(data, "Got an error writing an RTP packet"); - *readmore = FALSE; - Curl_safefree(rtspc->rtp_buf); - rtspc->rtp_buf = NULL; - rtspc->rtp_bufsize = 0; - return result; - } + /* We have the full RTP interleaved packet + * Write out the header including the leading '$' */ + DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n", + rtspc->rtp_channel, rtp_length)); + result = rtp_client_write(conn, &rtp[0], rtp_length + 4); + if(result) { + failf(data, "Got an error writing an RTP packet"); + *readmore = FALSE; + Curl_safefree(rtspc->rtp_buf); + rtspc->rtp_buf = NULL; + rtspc->rtp_bufsize = 0; + return result; + } - /* Move forward in the buffer */ - rtp_dataleft -= rtp_length + 4; - rtp += rtp_length + 4; + /* Move forward in the buffer */ + rtp_dataleft -= rtp_length + 4; + rtp += rtp_length + 4; - if(data->set.rtspreq == RTSPREQ_RECEIVE) { - /* If we are in a passive receive, give control back - * to the app as often as we can. - */ - k->keepon &= ~KEEP_RECV; - } + if(data->set.rtspreq == RTSPREQ_RECEIVE) { + /* If we are in a passive receive, give control back + * to the app as often as we can. + */ + k->keepon &= ~KEEP_RECV; } } else { @@ -689,20 +722,18 @@ static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data, *nread = 0; return CURLE_OK; } - else { - /* Fix up k->str to point just after the last RTP packet */ - k->str += *nread - rtp_dataleft; + /* Fix up k->str to point just after the last RTP packet */ + k->str += *nread - rtp_dataleft; - /* either all of the data has been read or... - * rtp now points at the next byte to parse - */ - if(rtp_dataleft > 0) - DEBUGASSERT(k->str[0] == rtp[0]); + /* either all of the data has been read or... + * rtp now points at the next byte to parse + */ + if(rtp_dataleft > 0) + DEBUGASSERT(k->str[0] == rtp[0]); - DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */ + DEBUGASSERT(rtp_dataleft <= *nread); /* sanity check */ - *nread = rtp_dataleft; - } + *nread = rtp_dataleft; /* If we get here, we have finished with the leftover/merge buffer */ Curl_safefree(rtspc->rtp_buf); @@ -715,25 +746,39 @@ static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data, static CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len) { - struct SessionHandle *data = conn->data; + struct Curl_easy *data = conn->data; size_t wrote; curl_write_callback writeit; + void *user_ptr; if(len == 0) { - failf (data, "Cannot write a 0 size RTP packet."); + failf(data, "Cannot write a 0 size RTP packet."); return CURLE_WRITE_ERROR; } - writeit = data->set.fwrite_rtp?data->set.fwrite_rtp:data->set.fwrite_func; - wrote = writeit(ptr, 1, len, data->set.rtp_out); + /* If the user has configured CURLOPT_INTERLEAVEFUNCTION then use that + function and any configured CURLOPT_INTERLEAVEDATA to write out the RTP + data. Otherwise, use the CURLOPT_WRITEFUNCTION with the CURLOPT_WRITEDATA + pointer to write out the RTP data. */ + if(data->set.fwrite_rtp) { + writeit = data->set.fwrite_rtp; + user_ptr = data->set.rtp_out; + } + else + { + writeit = data->set.fwrite_func; + user_ptr = data->set.out; + } + + wrote = writeit(ptr, 1, len, user_ptr); if(CURL_WRITEFUNC_PAUSE == wrote) { - failf (data, "Cannot pause RTP"); + failf(data, "Cannot pause RTP"); return CURLE_WRITE_ERROR; } if(wrote != len) { - failf (data, "Failed writing RTP data"); + failf(data, "Failed writing RTP data"); return CURLE_WRITE_ERROR; } @@ -743,7 +788,7 @@ CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len) CURLcode Curl_rtsp_parseheader(struct connectdata *conn, char *header) { - struct SessionHandle *data = conn->data; + struct Curl_easy *data = conn->data; long CSeq = 0; if(checkprefix("CSeq:", header)) { @@ -763,7 +808,7 @@ CURLcode Curl_rtsp_parseheader(struct connectdata *conn, char *start; /* Find the first non-space letter */ - start = header + 9; + start = header + 8; while(*start && ISSPACE(*start)) start++; @@ -780,19 +825,15 @@ CURLcode Curl_rtsp_parseheader(struct connectdata *conn, } } else { - /* If the Session ID is not set, and we find it in a response, then - set it */ - - /* The session ID can be an alphanumeric or a 'safe' character + /* If the Session ID is not set, and we find it in a response, then set + * it. * - * RFC 2326 15.1 Base Syntax: - * safe = "\$" | "-" | "_" | "." | "+" - * */ + * Allow any non whitespace content, up to the field separator or end of + * line. RFC 2326 isn't 100% clear on the session ID and for example + * gstreamer does url-encoded session ID's not covered by the standard. + */ char *end = start; - while(*end && - (ISALNUM(*end) || *end == '-' || *end == '_' || *end == '.' || - *end == '+' || - (*end == '\\' && *(end + 1) && *(end + 1) == '$' && (++end, 1)))) + while(*end && *end != ';' && !ISSPACE(*end)) end++; /* Copy the id substring into a new buffer */ |