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