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