diff options
Diffstat (limited to 'lib/rtsp.c')
-rw-r--r-- | lib/rtsp.c | 201 |
1 files changed, 128 insertions, 73 deletions
@@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, 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 @@ -20,7 +20,7 @@ * ***************************************************************************/ -#include "setup.h" +#include "curl_setup.h" #ifndef CURL_DISABLE_RTSP @@ -28,19 +28,18 @@ #include <curl/curl.h> #include "transfer.h" #include "sendf.h" -#include "easyif.h" /* for Curl_convert_... prototypes */ #include "multiif.h" #include "http.h" #include "url.h" #include "progress.h" #include "rtsp.h" #include "rawstr.h" -#include "curl_memory.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> +#include "select.h" +#include "connect.h" +#include "curl_printf.h" -/* The last #include file should be: */ +/* The last #include files should be: */ +#include "curl_memory.h" #include "memdebug.h" /* @@ -58,10 +57,31 @@ #define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \ ((int)((unsigned char)((p)[3])))) +/* protocol-specific functions set up to be called by the main engine */ +static CURLcode rtsp_do(struct connectdata *conn, bool *done); +static CURLcode rtsp_done(struct connectdata *conn, CURLcode, bool premature); +static CURLcode rtsp_connect(struct connectdata *conn, bool *done); +static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead); + static int rtsp_getsock_do(struct connectdata *conn, curl_socket_t *socks, int numsocks); +/* + * Parse and write out any available RTP data. + * + * nread: amount of data left after k->str. will be modified if RTP + * 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, + struct connectdata *conn, + ssize_t *nread, + bool *readmore); + +static CURLcode rtsp_setup_connection(struct connectdata *conn); + + /* 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 the single socket to become writable only */ @@ -84,22 +104,71 @@ CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len); */ const struct Curl_handler Curl_handler_rtsp = { "RTSP", /* scheme */ - ZERO_NULL, /* setup_connection */ - Curl_rtsp, /* do_it */ - Curl_rtsp_done, /* done */ + rtsp_setup_connection, /* setup_connection */ + rtsp_do, /* do_it */ + rtsp_done, /* done */ ZERO_NULL, /* do_more */ - Curl_rtsp_connect, /* connect_it */ + rtsp_connect, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ ZERO_NULL, /* proto_getsock */ rtsp_getsock_do, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ - Curl_rtsp_disconnect, /* disconnect */ + rtsp_disconnect, /* disconnect */ + rtsp_rtp_readwrite, /* readwrite */ PORT_RTSP, /* defport */ - PROT_RTSP, /* protocol */ + CURLPROTO_RTSP, /* protocol */ + PROTOPT_NONE /* flags */ }; -CURLcode Curl_rtsp_connect(struct connectdata *conn, bool *done) + +static CURLcode rtsp_setup_connection(struct connectdata *conn) +{ + struct RTSP *rtsp; + + conn->data->req.protop = rtsp = calloc(1, sizeof(struct RTSP)); + if(!rtsp) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + + +/* + * The server may send us RTP data at any point, and RTSPREQ_RECEIVE does not + * 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 + * and distinguish between closed and data. + */ +bool Curl_rtsp_connisdead(struct connectdata *check) +{ + int sval; + bool ret_val = TRUE; + + sval = Curl_socket_ready(check->sock[FIRSTSOCKET], CURL_SOCKET_BAD, 0); + if(sval == 0) { + /* timeout */ + ret_val = FALSE; + } + else if(sval & CURL_CSELECT_ERR) { + /* 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; + } + + return ret_val; +} + +static CURLcode rtsp_connect(struct connectdata *conn, bool *done) { CURLcode httpStatus; struct SessionHandle *data = conn->data; @@ -117,17 +186,19 @@ CURLcode Curl_rtsp_connect(struct connectdata *conn, bool *done) return httpStatus; } -CURLcode Curl_rtsp_disconnect(struct connectdata *conn) { +static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead) +{ + (void) dead; Curl_safefree(conn->proto.rtspc.rtp_buf); return CURLE_OK; } -CURLcode Curl_rtsp_done(struct connectdata *conn, - CURLcode status, bool premature) +static CURLcode rtsp_done(struct connectdata *conn, + CURLcode status, bool premature) { struct SessionHandle *data = conn->data; - struct RTSP *rtsp = data->state.proto.rtsp; + struct RTSP *rtsp = data->req.protop; CURLcode httpStatus; long CSeq_sent; long CSeq_recv; @@ -143,7 +214,8 @@ CURLcode Curl_rtsp_done(struct connectdata *conn, CSeq_sent = rtsp->CSeq_sent; CSeq_recv = rtsp->CSeq_recv; if((data->set.rtspreq != RTSPREQ_RECEIVE) && (CSeq_sent != CSeq_recv)) { - failf(data, "The CSeq of this request %ld did not match the response %ld", + failf(data, + "The CSeq of this request %ld did not match the response %ld", CSeq_sent, CSeq_recv); return CURLE_RTSP_CSEQ_ERROR; } @@ -157,12 +229,12 @@ CURLcode Curl_rtsp_done(struct connectdata *conn, return httpStatus; } -CURLcode Curl_rtsp(struct connectdata *conn, bool *done) +static CURLcode rtsp_do(struct connectdata *conn, bool *done) { struct SessionHandle *data = conn->data; CURLcode result=CURLE_OK; Curl_RtspReq rtspreq = data->set.rtspreq; - struct RTSP *rtsp; + struct RTSP *rtsp = data->req.protop; struct HTTP *http; Curl_send_buffer *req_buffer; curl_off_t postsize = 0; /* for ANNOUNCE and SET_PARAMETER */ @@ -180,20 +252,6 @@ CURLcode Curl_rtsp(struct connectdata *conn, bool *done) *done = TRUE; - Curl_reset_reqproto(conn); - - if(!data->state.proto.rtsp) { - /* Only allocate this struct if we don't already have it! */ - - rtsp = calloc(1, sizeof(struct RTSP)); - if(!rtsp) - return CURLE_OUT_OF_MEMORY; - data->state.proto.rtsp = rtsp; - } - else { - rtsp = data->state.proto.rtsp; - } - http = &(rtsp->http_wrapper); /* Assert that no one has changed the RTSP struct in an evil way */ DEBUGASSERT((void *)http == (void *)rtsp); @@ -205,11 +263,10 @@ CURLcode Curl_rtsp(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"; @@ -236,6 +293,7 @@ CURLcode Curl_rtsp(struct connectdata *conn, bool *done) case RTSPREQ_GET_PARAMETER: /* GET_PARAMETER's no_body status is determined later */ p_request = "GET_PARAMETER"; + data->set.opt_no_body = FALSE; break; case RTSPREQ_SET_PARAMETER: p_request = "SET_PARAMETER"; @@ -264,7 +322,7 @@ CURLcode Curl_rtsp(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; } @@ -280,7 +338,7 @@ CURLcode Curl_rtsp(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]) { @@ -304,11 +362,11 @@ CURLcode Curl_rtsp(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 = @@ -325,18 +383,18 @@ CURLcode Curl_rtsp(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; } /* 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; @@ -353,7 +411,7 @@ CURLcode Curl_rtsp(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; @@ -363,11 +421,11 @@ CURLcode Curl_rtsp(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; } @@ -423,7 +481,7 @@ CURLcode Curl_rtsp(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; @@ -432,13 +490,13 @@ CURLcode Curl_rtsp(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; } @@ -446,9 +504,9 @@ CURLcode Curl_rtsp(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; @@ -456,7 +514,7 @@ CURLcode Curl_rtsp(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) @@ -465,7 +523,7 @@ CURLcode Curl_rtsp(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) @@ -473,8 +531,9 @@ CURLcode Curl_rtsp(struct connectdata *conn, bool *done) } } - data->state.expect100header = FALSE; /* RTSP posts are simple/small */ - } else if(rtspreq == RTSPREQ_GET_PARAMETER) { + data->state.expect100header = FALSE; /* RTSP posts are simple/small */ + } + else if(rtspreq == RTSPREQ_GET_PARAMETER) { /* Check for an empty GET_PARAMETER (heartbeat) request */ data->set.httpreq = HTTPREQ_HEAD; data->set.opt_no_body = TRUE; @@ -521,10 +580,11 @@ CURLcode Curl_rtsp(struct connectdata *conn, bool *done) return result; } -CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data, - struct connectdata *conn, - ssize_t *nread, - bool *readmore) { + +static CURLcode rtsp_rtp_readwrite(struct SessionHandle *data, + struct connectdata *conn, + ssize_t *nread, + bool *readmore) { struct SingleRequest *k = &data->req; struct rtsp_conn *rtspc = &(conn->proto.rtspc); @@ -606,7 +666,7 @@ CURLcode Curl_rtsp_rtp_readwrite(struct SessionHandle *data, } if(rtp_dataleft != 0 && rtp[0] == '$') { - DEBUGF(infof(data, "RTP Rewinding %zu %s\n", rtp_dataleft, + DEBUGF(infof(data, "RTP Rewinding %zd %s\n", rtp_dataleft, *readmore ? "(READMORE)" : "")); /* Store the incomplete RTP packet for a "rewind" */ @@ -685,15 +745,10 @@ CURLcode Curl_rtsp_parseheader(struct connectdata *conn, if(checkprefix("CSeq:", header)) { /* Store the received CSeq. Match is verified in rtsp_done */ - int nc; - char *temp = strdup(header); - if(!temp) - return CURLE_OUT_OF_MEMORY; - Curl_strntoupper(temp, temp, sizeof(temp)); - nc = sscanf(temp, "CSEQ: %ld", &CSeq); - free(temp); + int nc = sscanf(&header[4], ": %ld", &CSeq); if(nc == 1) { - data->state.proto.rtsp->CSeq_recv = CSeq; /* mark the request */ + struct RTSP *rtsp = data->req.protop; + rtsp->CSeq_recv = CSeq; /* mark the request */ data->state.rtsp_CSeq_recv = CSeq; /* update the handle */ } else { @@ -705,7 +760,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++; |