Annotation of embedaddon/curl/lib/rtsp.c, revision 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>