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