Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                      _   _ ____  _
      3  *  Project         ___| | | |  _ \| |
      4  *                 / __| | | | |_) | |
      5  *                | (__| |_| |  _ <| |___
      6  *                 \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel (at) haxx.se>, et al.
      9  * Copyright (C) 2010, Howard Chu, <hyc (at) highlandsun.com>
     10  *
     11  * This software is licensed as described in the file COPYING, which
     12  * you should have received as part of this distribution. The terms
     13  * are also available at https://curl.haxx.se/docs/copyright.html.
     14  *
     15  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     16  * copies of the Software, and permit persons to whom the Software is
     17  * furnished to do so, under the terms of the COPYING file.
     18  *
     19  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     20  * KIND, either express or implied.
     21  *
     22  ***************************************************************************/
     23 
     24 #include "curl_setup.h"
     25 
     26 #ifdef USE_LIBRTMP
     27 
     28 #include "curl_rtmp.h"
     29 #include "urldata.h"
     30 #include "nonblock.h" /* for curlx_nonblock */
     31 #include "progress.h" /* for Curl_pgrsSetUploadSize */
     32 #include "transfer.h"
     33 #include "warnless.h"
     34 #include <curl/curl.h>
     35 #include <librtmp/rtmp.h>
     36 #include "curl_memory.h"
     37 /* The last #include file should be: */
     38 #include "memdebug.h"
     39 
     40 #if defined(WIN32) && !defined(USE_LWIPSOCK)
     41 #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
     42 #define SET_RCVTIMEO(tv,s)   int tv = s*1000
     43 #elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD)
     44 #define SET_RCVTIMEO(tv,s)   int tv = s*1000
     45 #else
     46 #define SET_RCVTIMEO(tv,s)   struct timeval tv = {s,0}
     47 #endif
     48 
     49 #define DEF_BUFTIME    (2*60*60*1000)    /* 2 hours */
     50 
     51 static CURLcode rtmp_setup_connection(struct connectdata *conn);
     52 static CURLcode rtmp_do(struct connectdata *conn, bool *done);
     53 static CURLcode rtmp_done(struct connectdata *conn, CURLcode, bool premature);
     54 static CURLcode rtmp_connect(struct connectdata *conn, bool *done);
     55 static CURLcode rtmp_disconnect(struct connectdata *conn, bool dead);
     56 
     57 static Curl_recv rtmp_recv;
     58 static Curl_send rtmp_send;
     59 
     60 /*
     61  * RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu
     62  */
     63 
     64 const struct Curl_handler Curl_handler_rtmp = {
     65   "RTMP",                               /* scheme */
     66   rtmp_setup_connection,                /* setup_connection */
     67   rtmp_do,                              /* do_it */
     68   rtmp_done,                            /* done */
     69   ZERO_NULL,                            /* do_more */
     70   rtmp_connect,                         /* connect_it */
     71   ZERO_NULL,                            /* connecting */
     72   ZERO_NULL,                            /* doing */
     73   ZERO_NULL,                            /* proto_getsock */
     74   ZERO_NULL,                            /* doing_getsock */
     75   ZERO_NULL,                            /* domore_getsock */
     76   ZERO_NULL,                            /* perform_getsock */
     77   rtmp_disconnect,                      /* disconnect */
     78   ZERO_NULL,                            /* readwrite */
     79   ZERO_NULL,                            /* connection_check */
     80   PORT_RTMP,                            /* defport */
     81   CURLPROTO_RTMP,                       /* protocol */
     82   PROTOPT_NONE                          /* flags*/
     83 };
     84 
     85 const struct Curl_handler Curl_handler_rtmpt = {
     86   "RTMPT",                              /* scheme */
     87   rtmp_setup_connection,                /* setup_connection */
     88   rtmp_do,                              /* do_it */
     89   rtmp_done,                            /* done */
     90   ZERO_NULL,                            /* do_more */
     91   rtmp_connect,                         /* connect_it */
     92   ZERO_NULL,                            /* connecting */
     93   ZERO_NULL,                            /* doing */
     94   ZERO_NULL,                            /* proto_getsock */
     95   ZERO_NULL,                            /* doing_getsock */
     96   ZERO_NULL,                            /* domore_getsock */
     97   ZERO_NULL,                            /* perform_getsock */
     98   rtmp_disconnect,                      /* disconnect */
     99   ZERO_NULL,                            /* readwrite */
    100   ZERO_NULL,                            /* connection_check */
    101   PORT_RTMPT,                           /* defport */
    102   CURLPROTO_RTMPT,                      /* protocol */
    103   PROTOPT_NONE                          /* flags*/
    104 };
    105 
    106 const struct Curl_handler Curl_handler_rtmpe = {
    107   "RTMPE",                              /* scheme */
    108   rtmp_setup_connection,                /* setup_connection */
    109   rtmp_do,                              /* do_it */
    110   rtmp_done,                            /* done */
    111   ZERO_NULL,                            /* do_more */
    112   rtmp_connect,                         /* connect_it */
    113   ZERO_NULL,                            /* connecting */
    114   ZERO_NULL,                            /* doing */
    115   ZERO_NULL,                            /* proto_getsock */
    116   ZERO_NULL,                            /* doing_getsock */
    117   ZERO_NULL,                            /* domore_getsock */
    118   ZERO_NULL,                            /* perform_getsock */
    119   rtmp_disconnect,                      /* disconnect */
    120   ZERO_NULL,                            /* readwrite */
    121   ZERO_NULL,                            /* connection_check */
    122   PORT_RTMP,                            /* defport */
    123   CURLPROTO_RTMPE,                      /* protocol */
    124   PROTOPT_NONE                          /* flags*/
    125 };
    126 
    127 const struct Curl_handler Curl_handler_rtmpte = {
    128   "RTMPTE",                             /* scheme */
    129   rtmp_setup_connection,                /* setup_connection */
    130   rtmp_do,                              /* do_it */
    131   rtmp_done,                            /* done */
    132   ZERO_NULL,                            /* do_more */
    133   rtmp_connect,                         /* connect_it */
    134   ZERO_NULL,                            /* connecting */
    135   ZERO_NULL,                            /* doing */
    136   ZERO_NULL,                            /* proto_getsock */
    137   ZERO_NULL,                            /* doing_getsock */
    138   ZERO_NULL,                            /* domore_getsock */
    139   ZERO_NULL,                            /* perform_getsock */
    140   rtmp_disconnect,                      /* disconnect */
    141   ZERO_NULL,                            /* readwrite */
    142   ZERO_NULL,                            /* connection_check */
    143   PORT_RTMPT,                           /* defport */
    144   CURLPROTO_RTMPTE,                     /* protocol */
    145   PROTOPT_NONE                          /* flags*/
    146 };
    147 
    148 const struct Curl_handler Curl_handler_rtmps = {
    149   "RTMPS",                              /* scheme */
    150   rtmp_setup_connection,                /* setup_connection */
    151   rtmp_do,                              /* do_it */
    152   rtmp_done,                            /* done */
    153   ZERO_NULL,                            /* do_more */
    154   rtmp_connect,                         /* connect_it */
    155   ZERO_NULL,                            /* connecting */
    156   ZERO_NULL,                            /* doing */
    157   ZERO_NULL,                            /* proto_getsock */
    158   ZERO_NULL,                            /* doing_getsock */
    159   ZERO_NULL,                            /* domore_getsock */
    160   ZERO_NULL,                            /* perform_getsock */
    161   rtmp_disconnect,                      /* disconnect */
    162   ZERO_NULL,                            /* readwrite */
    163   ZERO_NULL,                            /* connection_check */
    164   PORT_RTMPS,                           /* defport */
    165   CURLPROTO_RTMPS,                      /* protocol */
    166   PROTOPT_NONE                          /* flags*/
    167 };
    168 
    169 const struct Curl_handler Curl_handler_rtmpts = {
    170   "RTMPTS",                             /* scheme */
    171   rtmp_setup_connection,                /* setup_connection */
    172   rtmp_do,                              /* do_it */
    173   rtmp_done,                            /* done */
    174   ZERO_NULL,                            /* do_more */
    175   rtmp_connect,                         /* connect_it */
    176   ZERO_NULL,                            /* connecting */
    177   ZERO_NULL,                            /* doing */
    178   ZERO_NULL,                            /* proto_getsock */
    179   ZERO_NULL,                            /* doing_getsock */
    180   ZERO_NULL,                            /* domore_getsock */
    181   ZERO_NULL,                            /* perform_getsock */
    182   rtmp_disconnect,                      /* disconnect */
    183   ZERO_NULL,                            /* readwrite */
    184   ZERO_NULL,                            /* connection_check */
    185   PORT_RTMPS,                           /* defport */
    186   CURLPROTO_RTMPTS,                     /* protocol */
    187   PROTOPT_NONE                          /* flags*/
    188 };
    189 
    190 static CURLcode rtmp_setup_connection(struct connectdata *conn)
    191 {
    192   RTMP *r = RTMP_Alloc();
    193   if(!r)
    194     return CURLE_OUT_OF_MEMORY;
    195 
    196   RTMP_Init(r);
    197   RTMP_SetBufferMS(r, DEF_BUFTIME);
    198   if(!RTMP_SetupURL(r, conn->data->change.url)) {
    199     RTMP_Free(r);
    200     return CURLE_URL_MALFORMAT;
    201   }
    202   conn->proto.generic = r;
    203   return CURLE_OK;
    204 }
    205 
    206 static CURLcode rtmp_connect(struct connectdata *conn, bool *done)
    207 {
    208   RTMP *r = conn->proto.generic;
    209   SET_RCVTIMEO(tv, 10);
    210 
    211   r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET];
    212 
    213   /* We have to know if it's a write before we send the
    214    * connect request packet
    215    */
    216   if(conn->data->set.upload)
    217     r->Link.protocol |= RTMP_FEATURE_WRITE;
    218 
    219   /* For plain streams, use the buffer toggle trick to keep data flowing */
    220   if(!(r->Link.lFlags & RTMP_LF_LIVE) &&
    221      !(r->Link.protocol & RTMP_FEATURE_HTTP))
    222     r->Link.lFlags |= RTMP_LF_BUFX;
    223 
    224   (void)curlx_nonblock(r->m_sb.sb_socket, FALSE);
    225   setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO,
    226              (char *)&tv, sizeof(tv));
    227 
    228   if(!RTMP_Connect1(r, NULL))
    229     return CURLE_FAILED_INIT;
    230 
    231   /* Clients must send a periodic BytesReceived report to the server */
    232   r->m_bSendCounter = true;
    233 
    234   *done = TRUE;
    235   conn->recv[FIRSTSOCKET] = rtmp_recv;
    236   conn->send[FIRSTSOCKET] = rtmp_send;
    237   return CURLE_OK;
    238 }
    239 
    240 static CURLcode rtmp_do(struct connectdata *conn, bool *done)
    241 {
    242   struct Curl_easy *data = conn->data;
    243   RTMP *r = conn->proto.generic;
    244 
    245   if(!RTMP_ConnectStream(r, 0))
    246     return CURLE_FAILED_INIT;
    247 
    248   if(conn->data->set.upload) {
    249     Curl_pgrsSetUploadSize(data, data->state.infilesize);
    250     Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
    251   }
    252   else
    253     Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
    254   *done = TRUE;
    255   return CURLE_OK;
    256 }
    257 
    258 static CURLcode rtmp_done(struct connectdata *conn, CURLcode status,
    259                           bool premature)
    260 {
    261   (void)conn; /* unused */
    262   (void)status; /* unused */
    263   (void)premature; /* unused */
    264 
    265   return CURLE_OK;
    266 }
    267 
    268 static CURLcode rtmp_disconnect(struct connectdata *conn,
    269                                 bool dead_connection)
    270 {
    271   RTMP *r = conn->proto.generic;
    272   (void)dead_connection;
    273   if(r) {
    274     conn->proto.generic = NULL;
    275     RTMP_Close(r);
    276     RTMP_Free(r);
    277   }
    278   return CURLE_OK;
    279 }
    280 
    281 static ssize_t rtmp_recv(struct connectdata *conn, int sockindex, char *buf,
    282                          size_t len, CURLcode *err)
    283 {
    284   RTMP *r = conn->proto.generic;
    285   ssize_t nread;
    286 
    287   (void)sockindex; /* unused */
    288 
    289   nread = RTMP_Read(r, buf, curlx_uztosi(len));
    290   if(nread < 0) {
    291     if(r->m_read.status == RTMP_READ_COMPLETE ||
    292         r->m_read.status == RTMP_READ_EOF) {
    293       conn->data->req.size = conn->data->req.bytecount;
    294       nread = 0;
    295     }
    296     else
    297       *err = CURLE_RECV_ERROR;
    298   }
    299   return nread;
    300 }
    301 
    302 static ssize_t rtmp_send(struct connectdata *conn, int sockindex,
    303                          const void *buf, size_t len, CURLcode *err)
    304 {
    305   RTMP *r = conn->proto.generic;
    306   ssize_t num;
    307 
    308   (void)sockindex; /* unused */
    309 
    310   num = RTMP_Write(r, (char *)buf, curlx_uztosi(len));
    311   if(num < 0)
    312     *err = CURLE_SEND_ERROR;
    313 
    314   return num;
    315 }
    316 #endif  /* USE_LIBRTMP */
    317