113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * Copyright (C) Howard Chu, <hyc@highlandsun.com> 1013498266Sopenharmony_ci * 1113498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1213498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1313498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1413498266Sopenharmony_ci * 1513498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1613498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1713498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1813498266Sopenharmony_ci * 1913498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 2013498266Sopenharmony_ci * KIND, either express or implied. 2113498266Sopenharmony_ci * 2213498266Sopenharmony_ci * SPDX-License-Identifier: curl 2313498266Sopenharmony_ci * 2413498266Sopenharmony_ci ***************************************************************************/ 2513498266Sopenharmony_ci 2613498266Sopenharmony_ci#include "curl_setup.h" 2713498266Sopenharmony_ci 2813498266Sopenharmony_ci#ifdef USE_LIBRTMP 2913498266Sopenharmony_ci 3013498266Sopenharmony_ci#include "curl_rtmp.h" 3113498266Sopenharmony_ci#include "urldata.h" 3213498266Sopenharmony_ci#include "nonblock.h" /* for curlx_nonblock */ 3313498266Sopenharmony_ci#include "progress.h" /* for Curl_pgrsSetUploadSize */ 3413498266Sopenharmony_ci#include "transfer.h" 3513498266Sopenharmony_ci#include "warnless.h" 3613498266Sopenharmony_ci#include <curl/curl.h> 3713498266Sopenharmony_ci#include <librtmp/rtmp.h> 3813498266Sopenharmony_ci#include "curl_memory.h" 3913498266Sopenharmony_ci/* The last #include file should be: */ 4013498266Sopenharmony_ci#include "memdebug.h" 4113498266Sopenharmony_ci 4213498266Sopenharmony_ci#if defined(_WIN32) && !defined(USE_LWIPSOCK) 4313498266Sopenharmony_ci#define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e) 4413498266Sopenharmony_ci#define SET_RCVTIMEO(tv,s) int tv = s*1000 4513498266Sopenharmony_ci#elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD) 4613498266Sopenharmony_ci#define SET_RCVTIMEO(tv,s) int tv = s*1000 4713498266Sopenharmony_ci#else 4813498266Sopenharmony_ci#define SET_RCVTIMEO(tv,s) struct timeval tv = {s,0} 4913498266Sopenharmony_ci#endif 5013498266Sopenharmony_ci 5113498266Sopenharmony_ci#define DEF_BUFTIME (2*60*60*1000) /* 2 hours */ 5213498266Sopenharmony_ci 5313498266Sopenharmony_cistatic CURLcode rtmp_setup_connection(struct Curl_easy *data, 5413498266Sopenharmony_ci struct connectdata *conn); 5513498266Sopenharmony_cistatic CURLcode rtmp_do(struct Curl_easy *data, bool *done); 5613498266Sopenharmony_cistatic CURLcode rtmp_done(struct Curl_easy *data, CURLcode, bool premature); 5713498266Sopenharmony_cistatic CURLcode rtmp_connect(struct Curl_easy *data, bool *done); 5813498266Sopenharmony_cistatic CURLcode rtmp_disconnect(struct Curl_easy *data, 5913498266Sopenharmony_ci struct connectdata *conn, bool dead); 6013498266Sopenharmony_ci 6113498266Sopenharmony_cistatic Curl_recv rtmp_recv; 6213498266Sopenharmony_cistatic Curl_send rtmp_send; 6313498266Sopenharmony_ci 6413498266Sopenharmony_ci/* 6513498266Sopenharmony_ci * RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu 6613498266Sopenharmony_ci */ 6713498266Sopenharmony_ci 6813498266Sopenharmony_ciconst struct Curl_handler Curl_handler_rtmp = { 6913498266Sopenharmony_ci "RTMP", /* scheme */ 7013498266Sopenharmony_ci rtmp_setup_connection, /* setup_connection */ 7113498266Sopenharmony_ci rtmp_do, /* do_it */ 7213498266Sopenharmony_ci rtmp_done, /* done */ 7313498266Sopenharmony_ci ZERO_NULL, /* do_more */ 7413498266Sopenharmony_ci rtmp_connect, /* connect_it */ 7513498266Sopenharmony_ci ZERO_NULL, /* connecting */ 7613498266Sopenharmony_ci ZERO_NULL, /* doing */ 7713498266Sopenharmony_ci ZERO_NULL, /* proto_getsock */ 7813498266Sopenharmony_ci ZERO_NULL, /* doing_getsock */ 7913498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 8013498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 8113498266Sopenharmony_ci rtmp_disconnect, /* disconnect */ 8213498266Sopenharmony_ci ZERO_NULL, /* write_resp */ 8313498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 8413498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 8513498266Sopenharmony_ci PORT_RTMP, /* defport */ 8613498266Sopenharmony_ci CURLPROTO_RTMP, /* protocol */ 8713498266Sopenharmony_ci CURLPROTO_RTMP, /* family */ 8813498266Sopenharmony_ci PROTOPT_NONE /* flags */ 8913498266Sopenharmony_ci}; 9013498266Sopenharmony_ci 9113498266Sopenharmony_ciconst struct Curl_handler Curl_handler_rtmpt = { 9213498266Sopenharmony_ci "RTMPT", /* scheme */ 9313498266Sopenharmony_ci rtmp_setup_connection, /* setup_connection */ 9413498266Sopenharmony_ci rtmp_do, /* do_it */ 9513498266Sopenharmony_ci rtmp_done, /* done */ 9613498266Sopenharmony_ci ZERO_NULL, /* do_more */ 9713498266Sopenharmony_ci rtmp_connect, /* connect_it */ 9813498266Sopenharmony_ci ZERO_NULL, /* connecting */ 9913498266Sopenharmony_ci ZERO_NULL, /* doing */ 10013498266Sopenharmony_ci ZERO_NULL, /* proto_getsock */ 10113498266Sopenharmony_ci ZERO_NULL, /* doing_getsock */ 10213498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 10313498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 10413498266Sopenharmony_ci rtmp_disconnect, /* disconnect */ 10513498266Sopenharmony_ci ZERO_NULL, /* write_resp */ 10613498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 10713498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 10813498266Sopenharmony_ci PORT_RTMPT, /* defport */ 10913498266Sopenharmony_ci CURLPROTO_RTMPT, /* protocol */ 11013498266Sopenharmony_ci CURLPROTO_RTMPT, /* family */ 11113498266Sopenharmony_ci PROTOPT_NONE /* flags */ 11213498266Sopenharmony_ci}; 11313498266Sopenharmony_ci 11413498266Sopenharmony_ciconst struct Curl_handler Curl_handler_rtmpe = { 11513498266Sopenharmony_ci "RTMPE", /* scheme */ 11613498266Sopenharmony_ci rtmp_setup_connection, /* setup_connection */ 11713498266Sopenharmony_ci rtmp_do, /* do_it */ 11813498266Sopenharmony_ci rtmp_done, /* done */ 11913498266Sopenharmony_ci ZERO_NULL, /* do_more */ 12013498266Sopenharmony_ci rtmp_connect, /* connect_it */ 12113498266Sopenharmony_ci ZERO_NULL, /* connecting */ 12213498266Sopenharmony_ci ZERO_NULL, /* doing */ 12313498266Sopenharmony_ci ZERO_NULL, /* proto_getsock */ 12413498266Sopenharmony_ci ZERO_NULL, /* doing_getsock */ 12513498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 12613498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 12713498266Sopenharmony_ci rtmp_disconnect, /* disconnect */ 12813498266Sopenharmony_ci ZERO_NULL, /* write_resp */ 12913498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 13013498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 13113498266Sopenharmony_ci PORT_RTMP, /* defport */ 13213498266Sopenharmony_ci CURLPROTO_RTMPE, /* protocol */ 13313498266Sopenharmony_ci CURLPROTO_RTMPE, /* family */ 13413498266Sopenharmony_ci PROTOPT_NONE /* flags */ 13513498266Sopenharmony_ci}; 13613498266Sopenharmony_ci 13713498266Sopenharmony_ciconst struct Curl_handler Curl_handler_rtmpte = { 13813498266Sopenharmony_ci "RTMPTE", /* scheme */ 13913498266Sopenharmony_ci rtmp_setup_connection, /* setup_connection */ 14013498266Sopenharmony_ci rtmp_do, /* do_it */ 14113498266Sopenharmony_ci rtmp_done, /* done */ 14213498266Sopenharmony_ci ZERO_NULL, /* do_more */ 14313498266Sopenharmony_ci rtmp_connect, /* connect_it */ 14413498266Sopenharmony_ci ZERO_NULL, /* connecting */ 14513498266Sopenharmony_ci ZERO_NULL, /* doing */ 14613498266Sopenharmony_ci ZERO_NULL, /* proto_getsock */ 14713498266Sopenharmony_ci ZERO_NULL, /* doing_getsock */ 14813498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 14913498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 15013498266Sopenharmony_ci rtmp_disconnect, /* disconnect */ 15113498266Sopenharmony_ci ZERO_NULL, /* write_resp */ 15213498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 15313498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 15413498266Sopenharmony_ci PORT_RTMPT, /* defport */ 15513498266Sopenharmony_ci CURLPROTO_RTMPTE, /* protocol */ 15613498266Sopenharmony_ci CURLPROTO_RTMPTE, /* family */ 15713498266Sopenharmony_ci PROTOPT_NONE /* flags */ 15813498266Sopenharmony_ci}; 15913498266Sopenharmony_ci 16013498266Sopenharmony_ciconst struct Curl_handler Curl_handler_rtmps = { 16113498266Sopenharmony_ci "RTMPS", /* scheme */ 16213498266Sopenharmony_ci rtmp_setup_connection, /* setup_connection */ 16313498266Sopenharmony_ci rtmp_do, /* do_it */ 16413498266Sopenharmony_ci rtmp_done, /* done */ 16513498266Sopenharmony_ci ZERO_NULL, /* do_more */ 16613498266Sopenharmony_ci rtmp_connect, /* connect_it */ 16713498266Sopenharmony_ci ZERO_NULL, /* connecting */ 16813498266Sopenharmony_ci ZERO_NULL, /* doing */ 16913498266Sopenharmony_ci ZERO_NULL, /* proto_getsock */ 17013498266Sopenharmony_ci ZERO_NULL, /* doing_getsock */ 17113498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 17213498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 17313498266Sopenharmony_ci rtmp_disconnect, /* disconnect */ 17413498266Sopenharmony_ci ZERO_NULL, /* write_resp */ 17513498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 17613498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 17713498266Sopenharmony_ci PORT_RTMPS, /* defport */ 17813498266Sopenharmony_ci CURLPROTO_RTMPS, /* protocol */ 17913498266Sopenharmony_ci CURLPROTO_RTMP, /* family */ 18013498266Sopenharmony_ci PROTOPT_NONE /* flags */ 18113498266Sopenharmony_ci}; 18213498266Sopenharmony_ci 18313498266Sopenharmony_ciconst struct Curl_handler Curl_handler_rtmpts = { 18413498266Sopenharmony_ci "RTMPTS", /* scheme */ 18513498266Sopenharmony_ci rtmp_setup_connection, /* setup_connection */ 18613498266Sopenharmony_ci rtmp_do, /* do_it */ 18713498266Sopenharmony_ci rtmp_done, /* done */ 18813498266Sopenharmony_ci ZERO_NULL, /* do_more */ 18913498266Sopenharmony_ci rtmp_connect, /* connect_it */ 19013498266Sopenharmony_ci ZERO_NULL, /* connecting */ 19113498266Sopenharmony_ci ZERO_NULL, /* doing */ 19213498266Sopenharmony_ci ZERO_NULL, /* proto_getsock */ 19313498266Sopenharmony_ci ZERO_NULL, /* doing_getsock */ 19413498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 19513498266Sopenharmony_ci ZERO_NULL, /* perform_getsock */ 19613498266Sopenharmony_ci rtmp_disconnect, /* disconnect */ 19713498266Sopenharmony_ci ZERO_NULL, /* write_resp */ 19813498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 19913498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 20013498266Sopenharmony_ci PORT_RTMPS, /* defport */ 20113498266Sopenharmony_ci CURLPROTO_RTMPTS, /* protocol */ 20213498266Sopenharmony_ci CURLPROTO_RTMPT, /* family */ 20313498266Sopenharmony_ci PROTOPT_NONE /* flags */ 20413498266Sopenharmony_ci}; 20513498266Sopenharmony_ci 20613498266Sopenharmony_cistatic CURLcode rtmp_setup_connection(struct Curl_easy *data, 20713498266Sopenharmony_ci struct connectdata *conn) 20813498266Sopenharmony_ci{ 20913498266Sopenharmony_ci RTMP *r = RTMP_Alloc(); 21013498266Sopenharmony_ci if(!r) 21113498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 21213498266Sopenharmony_ci 21313498266Sopenharmony_ci RTMP_Init(r); 21413498266Sopenharmony_ci RTMP_SetBufferMS(r, DEF_BUFTIME); 21513498266Sopenharmony_ci if(!RTMP_SetupURL(r, data->state.url)) { 21613498266Sopenharmony_ci RTMP_Free(r); 21713498266Sopenharmony_ci return CURLE_URL_MALFORMAT; 21813498266Sopenharmony_ci } 21913498266Sopenharmony_ci conn->proto.rtmp = r; 22013498266Sopenharmony_ci return CURLE_OK; 22113498266Sopenharmony_ci} 22213498266Sopenharmony_ci 22313498266Sopenharmony_cistatic CURLcode rtmp_connect(struct Curl_easy *data, bool *done) 22413498266Sopenharmony_ci{ 22513498266Sopenharmony_ci struct connectdata *conn = data->conn; 22613498266Sopenharmony_ci RTMP *r = conn->proto.rtmp; 22713498266Sopenharmony_ci SET_RCVTIMEO(tv, 10); 22813498266Sopenharmony_ci 22913498266Sopenharmony_ci r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET]; 23013498266Sopenharmony_ci 23113498266Sopenharmony_ci /* We have to know if it's a write before we send the 23213498266Sopenharmony_ci * connect request packet 23313498266Sopenharmony_ci */ 23413498266Sopenharmony_ci if(data->state.upload) 23513498266Sopenharmony_ci r->Link.protocol |= RTMP_FEATURE_WRITE; 23613498266Sopenharmony_ci 23713498266Sopenharmony_ci /* For plain streams, use the buffer toggle trick to keep data flowing */ 23813498266Sopenharmony_ci if(!(r->Link.lFlags & RTMP_LF_LIVE) && 23913498266Sopenharmony_ci !(r->Link.protocol & RTMP_FEATURE_HTTP)) 24013498266Sopenharmony_ci r->Link.lFlags |= RTMP_LF_BUFX; 24113498266Sopenharmony_ci 24213498266Sopenharmony_ci (void)curlx_nonblock(r->m_sb.sb_socket, FALSE); 24313498266Sopenharmony_ci setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, 24413498266Sopenharmony_ci (char *)&tv, sizeof(tv)); 24513498266Sopenharmony_ci 24613498266Sopenharmony_ci if(!RTMP_Connect1(r, NULL)) 24713498266Sopenharmony_ci return CURLE_FAILED_INIT; 24813498266Sopenharmony_ci 24913498266Sopenharmony_ci /* Clients must send a periodic BytesReceived report to the server */ 25013498266Sopenharmony_ci r->m_bSendCounter = true; 25113498266Sopenharmony_ci 25213498266Sopenharmony_ci *done = TRUE; 25313498266Sopenharmony_ci conn->recv[FIRSTSOCKET] = rtmp_recv; 25413498266Sopenharmony_ci conn->send[FIRSTSOCKET] = rtmp_send; 25513498266Sopenharmony_ci return CURLE_OK; 25613498266Sopenharmony_ci} 25713498266Sopenharmony_ci 25813498266Sopenharmony_cistatic CURLcode rtmp_do(struct Curl_easy *data, bool *done) 25913498266Sopenharmony_ci{ 26013498266Sopenharmony_ci struct connectdata *conn = data->conn; 26113498266Sopenharmony_ci RTMP *r = conn->proto.rtmp; 26213498266Sopenharmony_ci 26313498266Sopenharmony_ci if(!RTMP_ConnectStream(r, 0)) 26413498266Sopenharmony_ci return CURLE_FAILED_INIT; 26513498266Sopenharmony_ci 26613498266Sopenharmony_ci if(data->state.upload) { 26713498266Sopenharmony_ci Curl_pgrsSetUploadSize(data, data->state.infilesize); 26813498266Sopenharmony_ci Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); 26913498266Sopenharmony_ci } 27013498266Sopenharmony_ci else 27113498266Sopenharmony_ci Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1); 27213498266Sopenharmony_ci *done = TRUE; 27313498266Sopenharmony_ci return CURLE_OK; 27413498266Sopenharmony_ci} 27513498266Sopenharmony_ci 27613498266Sopenharmony_cistatic CURLcode rtmp_done(struct Curl_easy *data, CURLcode status, 27713498266Sopenharmony_ci bool premature) 27813498266Sopenharmony_ci{ 27913498266Sopenharmony_ci (void)data; /* unused */ 28013498266Sopenharmony_ci (void)status; /* unused */ 28113498266Sopenharmony_ci (void)premature; /* unused */ 28213498266Sopenharmony_ci 28313498266Sopenharmony_ci return CURLE_OK; 28413498266Sopenharmony_ci} 28513498266Sopenharmony_ci 28613498266Sopenharmony_cistatic CURLcode rtmp_disconnect(struct Curl_easy *data, 28713498266Sopenharmony_ci struct connectdata *conn, 28813498266Sopenharmony_ci bool dead_connection) 28913498266Sopenharmony_ci{ 29013498266Sopenharmony_ci RTMP *r = conn->proto.rtmp; 29113498266Sopenharmony_ci (void)data; 29213498266Sopenharmony_ci (void)dead_connection; 29313498266Sopenharmony_ci if(r) { 29413498266Sopenharmony_ci conn->proto.rtmp = NULL; 29513498266Sopenharmony_ci RTMP_Close(r); 29613498266Sopenharmony_ci RTMP_Free(r); 29713498266Sopenharmony_ci } 29813498266Sopenharmony_ci return CURLE_OK; 29913498266Sopenharmony_ci} 30013498266Sopenharmony_ci 30113498266Sopenharmony_cistatic ssize_t rtmp_recv(struct Curl_easy *data, int sockindex, char *buf, 30213498266Sopenharmony_ci size_t len, CURLcode *err) 30313498266Sopenharmony_ci{ 30413498266Sopenharmony_ci struct connectdata *conn = data->conn; 30513498266Sopenharmony_ci RTMP *r = conn->proto.rtmp; 30613498266Sopenharmony_ci ssize_t nread; 30713498266Sopenharmony_ci 30813498266Sopenharmony_ci (void)sockindex; /* unused */ 30913498266Sopenharmony_ci 31013498266Sopenharmony_ci nread = RTMP_Read(r, buf, curlx_uztosi(len)); 31113498266Sopenharmony_ci if(nread < 0) { 31213498266Sopenharmony_ci if(r->m_read.status == RTMP_READ_COMPLETE || 31313498266Sopenharmony_ci r->m_read.status == RTMP_READ_EOF) { 31413498266Sopenharmony_ci data->req.size = data->req.bytecount; 31513498266Sopenharmony_ci nread = 0; 31613498266Sopenharmony_ci } 31713498266Sopenharmony_ci else 31813498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 31913498266Sopenharmony_ci } 32013498266Sopenharmony_ci return nread; 32113498266Sopenharmony_ci} 32213498266Sopenharmony_ci 32313498266Sopenharmony_cistatic ssize_t rtmp_send(struct Curl_easy *data, int sockindex, 32413498266Sopenharmony_ci const void *buf, size_t len, CURLcode *err) 32513498266Sopenharmony_ci{ 32613498266Sopenharmony_ci struct connectdata *conn = data->conn; 32713498266Sopenharmony_ci RTMP *r = conn->proto.rtmp; 32813498266Sopenharmony_ci ssize_t num; 32913498266Sopenharmony_ci 33013498266Sopenharmony_ci (void)sockindex; /* unused */ 33113498266Sopenharmony_ci 33213498266Sopenharmony_ci num = RTMP_Write(r, (char *)buf, curlx_uztosi(len)); 33313498266Sopenharmony_ci if(num < 0) 33413498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 33513498266Sopenharmony_ci 33613498266Sopenharmony_ci return num; 33713498266Sopenharmony_ci} 33813498266Sopenharmony_ci#endif /* USE_LIBRTMP */ 339