1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. 9 * Copyright (C) 2010, 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.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 48static CURLcode rtmp_setup_connection(struct connectdata *conn); 49static CURLcode rtmp_do(struct connectdata *conn, bool *done); 50static CURLcode rtmp_done(struct connectdata *conn, CURLcode, bool premature); 51static CURLcode rtmp_connect(struct connectdata *conn, bool *done); 52static CURLcode rtmp_disconnect(struct connectdata *conn, bool dead); 53 54static Curl_recv rtmp_recv; 55static Curl_send rtmp_send; 56 57/* 58 * RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu 59 */ 60 61const 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 81const 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 101const 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 121const 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 141const 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 161const 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 181static 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 197static 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 231static 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 248static 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 258static 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 271static 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 292static 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