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