• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                      _   _ ____  _
3  *  Project         ___| | | |  _ \| |
4  *                 / __| | | | |_) | |
5  *                | (__| |_| |  _ <| |___
6  *                 \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  * Copyright (C) Howard Chu, <hyc@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.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  * SPDX-License-Identifier: curl
23  *
24  ***************************************************************************/
25 
26 #include "curl_setup.h"
27 
28 #ifdef USE_LIBRTMP
29 
30 #include "curl_rtmp.h"
31 #include "urldata.h"
32 #include "nonblock.h" /* for curlx_nonblock */
33 #include "progress.h" /* for Curl_pgrsSetUploadSize */
34 #include "transfer.h"
35 #include "warnless.h"
36 #include <curl/curl.h>
37 #include <librtmp/rtmp.h>
38 
39 /* The last 3 #include files should be in this order */
40 #include "curl_printf.h"
41 #include "curl_memory.h"
42 #include "memdebug.h"
43 
44 #if defined(_WIN32) && !defined(USE_LWIPSOCK)
45 #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
46 #define SET_RCVTIMEO(tv,s)   int tv = s*1000
47 #elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD)
48 #define SET_RCVTIMEO(tv,s)   int tv = s*1000
49 #else
50 #define SET_RCVTIMEO(tv,s)   struct timeval tv = {s,0}
51 #endif
52 
53 #define DEF_BUFTIME    (2*60*60*1000)    /* 2 hours */
54 
55 static CURLcode rtmp_setup_connection(struct Curl_easy *data,
56                                       struct connectdata *conn);
57 static CURLcode rtmp_do(struct Curl_easy *data, bool *done);
58 static CURLcode rtmp_done(struct Curl_easy *data, CURLcode, bool premature);
59 static CURLcode rtmp_connect(struct Curl_easy *data, bool *done);
60 static CURLcode rtmp_disconnect(struct Curl_easy *data,
61                                 struct connectdata *conn, bool dead);
62 
63 static Curl_recv rtmp_recv;
64 static Curl_send rtmp_send;
65 
66 /*
67  * RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu
68  */
69 
70 const struct Curl_handler Curl_handler_rtmp = {
71   "rtmp",                               /* scheme */
72   rtmp_setup_connection,                /* setup_connection */
73   rtmp_do,                              /* do_it */
74   rtmp_done,                            /* done */
75   ZERO_NULL,                            /* do_more */
76   rtmp_connect,                         /* connect_it */
77   ZERO_NULL,                            /* connecting */
78   ZERO_NULL,                            /* doing */
79   ZERO_NULL,                            /* proto_getsock */
80   ZERO_NULL,                            /* doing_getsock */
81   ZERO_NULL,                            /* domore_getsock */
82   ZERO_NULL,                            /* perform_getsock */
83   rtmp_disconnect,                      /* disconnect */
84   ZERO_NULL,                            /* write_resp */
85   ZERO_NULL,                            /* write_resp_hd */
86   ZERO_NULL,                            /* connection_check */
87   ZERO_NULL,                            /* attach connection */
88   ZERO_NULL,                            /* follow */
89   PORT_RTMP,                            /* defport */
90   CURLPROTO_RTMP,                       /* protocol */
91   CURLPROTO_RTMP,                       /* family */
92   PROTOPT_NONE                          /* flags */
93 };
94 
95 const struct Curl_handler Curl_handler_rtmpt = {
96   "rtmpt",                              /* scheme */
97   rtmp_setup_connection,                /* setup_connection */
98   rtmp_do,                              /* do_it */
99   rtmp_done,                            /* done */
100   ZERO_NULL,                            /* do_more */
101   rtmp_connect,                         /* connect_it */
102   ZERO_NULL,                            /* connecting */
103   ZERO_NULL,                            /* doing */
104   ZERO_NULL,                            /* proto_getsock */
105   ZERO_NULL,                            /* doing_getsock */
106   ZERO_NULL,                            /* domore_getsock */
107   ZERO_NULL,                            /* perform_getsock */
108   rtmp_disconnect,                      /* disconnect */
109   ZERO_NULL,                            /* write_resp */
110   ZERO_NULL,                            /* write_resp_hd */
111   ZERO_NULL,                            /* connection_check */
112   ZERO_NULL,                            /* attach connection */
113   ZERO_NULL,                            /* follow */
114   PORT_RTMPT,                           /* defport */
115   CURLPROTO_RTMPT,                      /* protocol */
116   CURLPROTO_RTMPT,                      /* family */
117   PROTOPT_NONE                          /* flags */
118 };
119 
120 const struct Curl_handler Curl_handler_rtmpe = {
121   "rtmpe",                              /* scheme */
122   rtmp_setup_connection,                /* setup_connection */
123   rtmp_do,                              /* do_it */
124   rtmp_done,                            /* done */
125   ZERO_NULL,                            /* do_more */
126   rtmp_connect,                         /* connect_it */
127   ZERO_NULL,                            /* connecting */
128   ZERO_NULL,                            /* doing */
129   ZERO_NULL,                            /* proto_getsock */
130   ZERO_NULL,                            /* doing_getsock */
131   ZERO_NULL,                            /* domore_getsock */
132   ZERO_NULL,                            /* perform_getsock */
133   rtmp_disconnect,                      /* disconnect */
134   ZERO_NULL,                            /* write_resp */
135   ZERO_NULL,                            /* write_resp_hd */
136   ZERO_NULL,                            /* connection_check */
137   ZERO_NULL,                            /* attach connection */
138   ZERO_NULL,                            /* follow */
139   PORT_RTMP,                            /* defport */
140   CURLPROTO_RTMPE,                      /* protocol */
141   CURLPROTO_RTMPE,                      /* family */
142   PROTOPT_NONE                          /* flags */
143 };
144 
145 const struct Curl_handler Curl_handler_rtmpte = {
146   "rtmpte",                             /* scheme */
147   rtmp_setup_connection,                /* setup_connection */
148   rtmp_do,                              /* do_it */
149   rtmp_done,                            /* done */
150   ZERO_NULL,                            /* do_more */
151   rtmp_connect,                         /* connect_it */
152   ZERO_NULL,                            /* connecting */
153   ZERO_NULL,                            /* doing */
154   ZERO_NULL,                            /* proto_getsock */
155   ZERO_NULL,                            /* doing_getsock */
156   ZERO_NULL,                            /* domore_getsock */
157   ZERO_NULL,                            /* perform_getsock */
158   rtmp_disconnect,                      /* disconnect */
159   ZERO_NULL,                            /* write_resp */
160   ZERO_NULL,                            /* write_resp_hd */
161   ZERO_NULL,                            /* connection_check */
162   ZERO_NULL,                            /* attach connection */
163   ZERO_NULL,                            /* follow */
164   PORT_RTMPT,                           /* defport */
165   CURLPROTO_RTMPTE,                     /* protocol */
166   CURLPROTO_RTMPTE,                     /* family */
167   PROTOPT_NONE                          /* flags */
168 };
169 
170 const struct Curl_handler Curl_handler_rtmps = {
171   "rtmps",                              /* scheme */
172   rtmp_setup_connection,                /* setup_connection */
173   rtmp_do,                              /* do_it */
174   rtmp_done,                            /* done */
175   ZERO_NULL,                            /* do_more */
176   rtmp_connect,                         /* connect_it */
177   ZERO_NULL,                            /* connecting */
178   ZERO_NULL,                            /* doing */
179   ZERO_NULL,                            /* proto_getsock */
180   ZERO_NULL,                            /* doing_getsock */
181   ZERO_NULL,                            /* domore_getsock */
182   ZERO_NULL,                            /* perform_getsock */
183   rtmp_disconnect,                      /* disconnect */
184   ZERO_NULL,                            /* write_resp */
185   ZERO_NULL,                            /* write_resp_hd */
186   ZERO_NULL,                            /* connection_check */
187   ZERO_NULL,                            /* attach connection */
188   ZERO_NULL,                            /* follow */
189   PORT_RTMPS,                           /* defport */
190   CURLPROTO_RTMPS,                      /* protocol */
191   CURLPROTO_RTMP,                       /* family */
192   PROTOPT_NONE                          /* flags */
193 };
194 
195 const struct Curl_handler Curl_handler_rtmpts = {
196   "rtmpts",                             /* scheme */
197   rtmp_setup_connection,                /* setup_connection */
198   rtmp_do,                              /* do_it */
199   rtmp_done,                            /* done */
200   ZERO_NULL,                            /* do_more */
201   rtmp_connect,                         /* connect_it */
202   ZERO_NULL,                            /* connecting */
203   ZERO_NULL,                            /* doing */
204   ZERO_NULL,                            /* proto_getsock */
205   ZERO_NULL,                            /* doing_getsock */
206   ZERO_NULL,                            /* domore_getsock */
207   ZERO_NULL,                            /* perform_getsock */
208   rtmp_disconnect,                      /* disconnect */
209   ZERO_NULL,                            /* write_resp */
210   ZERO_NULL,                            /* write_resp_hd */
211   ZERO_NULL,                            /* connection_check */
212   ZERO_NULL,                            /* attach connection */
213   ZERO_NULL,                            /* follow */
214   PORT_RTMPS,                           /* defport */
215   CURLPROTO_RTMPTS,                     /* protocol */
216   CURLPROTO_RTMPT,                      /* family */
217   PROTOPT_NONE                          /* flags */
218 };
219 
rtmp_setup_connection(struct Curl_easy * data,struct connectdata * conn)220 static CURLcode rtmp_setup_connection(struct Curl_easy *data,
221                                       struct connectdata *conn)
222 {
223   RTMP *r = RTMP_Alloc();
224   if(!r)
225     return CURLE_OUT_OF_MEMORY;
226 
227   RTMP_Init(r);
228   RTMP_SetBufferMS(r, DEF_BUFTIME);
229   if(!RTMP_SetupURL(r, data->state.url)) {
230     RTMP_Free(r);
231     return CURLE_URL_MALFORMAT;
232   }
233   conn->proto.rtmp = r;
234   return CURLE_OK;
235 }
236 
rtmp_connect(struct Curl_easy * data,bool * done)237 static CURLcode rtmp_connect(struct Curl_easy *data, bool *done)
238 {
239   struct connectdata *conn = data->conn;
240   RTMP *r = conn->proto.rtmp;
241   SET_RCVTIMEO(tv, 10);
242 
243   r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET];
244 
245   /* We have to know if it is a write before we send the
246    * connect request packet
247    */
248   if(data->state.upload)
249     r->Link.protocol |= RTMP_FEATURE_WRITE;
250 
251   /* For plain streams, use the buffer toggle trick to keep data flowing */
252   if(!(r->Link.lFlags & RTMP_LF_LIVE) &&
253      !(r->Link.protocol & RTMP_FEATURE_HTTP))
254     r->Link.lFlags |= RTMP_LF_BUFX;
255 
256   (void)curlx_nonblock(r->m_sb.sb_socket, FALSE);
257   setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO,
258              (char *)&tv, sizeof(tv));
259 
260   if(!RTMP_Connect1(r, NULL))
261     return CURLE_FAILED_INIT;
262 
263   /* Clients must send a periodic BytesReceived report to the server */
264   r->m_bSendCounter = TRUE;
265 
266   *done = TRUE;
267   conn->recv[FIRSTSOCKET] = rtmp_recv;
268   conn->send[FIRSTSOCKET] = rtmp_send;
269   return CURLE_OK;
270 }
271 
rtmp_do(struct Curl_easy * data,bool * done)272 static CURLcode rtmp_do(struct Curl_easy *data, bool *done)
273 {
274   struct connectdata *conn = data->conn;
275   RTMP *r = conn->proto.rtmp;
276 
277   if(!RTMP_ConnectStream(r, 0))
278     return CURLE_FAILED_INIT;
279 
280   if(data->state.upload) {
281     Curl_pgrsSetUploadSize(data, data->state.infilesize);
282     Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
283   }
284   else
285     Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
286   *done = TRUE;
287   return CURLE_OK;
288 }
289 
rtmp_done(struct Curl_easy * data,CURLcode status,bool premature)290 static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status,
291                           bool premature)
292 {
293   (void)data; /* unused */
294   (void)status; /* unused */
295   (void)premature; /* unused */
296 
297   return CURLE_OK;
298 }
299 
rtmp_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)300 static CURLcode rtmp_disconnect(struct Curl_easy *data,
301                                 struct connectdata *conn,
302                                 bool dead_connection)
303 {
304   RTMP *r = conn->proto.rtmp;
305   (void)data;
306   (void)dead_connection;
307   if(r) {
308     conn->proto.rtmp = NULL;
309     RTMP_Close(r);
310     RTMP_Free(r);
311   }
312   return CURLE_OK;
313 }
314 
rtmp_recv(struct Curl_easy * data,int sockindex,char * buf,size_t len,CURLcode * err)315 static ssize_t rtmp_recv(struct Curl_easy *data, int sockindex, char *buf,
316                          size_t len, CURLcode *err)
317 {
318   struct connectdata *conn = data->conn;
319   RTMP *r = conn->proto.rtmp;
320   ssize_t nread;
321 
322   (void)sockindex; /* unused */
323 
324   nread = RTMP_Read(r, buf, curlx_uztosi(len));
325   if(nread < 0) {
326     if(r->m_read.status == RTMP_READ_COMPLETE ||
327        r->m_read.status == RTMP_READ_EOF) {
328       data->req.size = data->req.bytecount;
329       nread = 0;
330     }
331     else
332       *err = CURLE_RECV_ERROR;
333   }
334   return nread;
335 }
336 
rtmp_send(struct Curl_easy * data,int sockindex,const void * buf,size_t len,bool eos,CURLcode * err)337 static ssize_t rtmp_send(struct Curl_easy *data, int sockindex,
338                          const void *buf, size_t len, bool eos, CURLcode *err)
339 {
340   struct connectdata *conn = data->conn;
341   RTMP *r = conn->proto.rtmp;
342   ssize_t num;
343 
344   (void)sockindex; /* unused */
345   (void)eos; /* unused */
346 
347   num = RTMP_Write(r, (char *)buf, curlx_uztosi(len));
348   if(num < 0)
349     *err = CURLE_SEND_ERROR;
350 
351   return num;
352 }
353 
Curl_rtmp_version(char * version,size_t len)354 void Curl_rtmp_version(char *version, size_t len)
355 {
356   char suff[2];
357   if(RTMP_LIB_VERSION & 0xff) {
358     suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1;
359     suff[1] = '\0';
360   }
361   else
362     suff[0] = '\0';
363 
364   msnprintf(version, len, "librtmp/%d.%d%s",
365             RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
366             suff);
367 }
368 
369 #endif  /* USE_LIBRTMP */
370