diff --git a/librtmp/rtmp.c b/librtmp/rtmp.c index 5ef3ae9..adcff1f 100644 --- a/librtmp/rtmp.c +++ b/librtmp/rtmp.c @@ -96,6 +96,7 @@ static int SendDeleteStream(RTMP *r, double dStreamId); static int SendFCSubscribe(RTMP *r, AVal *subscribepath); static int SendPlay(RTMP *r); static int SendBytesReceived(RTMP *r); +static int SendUsherToken(RTMP *r, AVal *usherToken); #if 0 /* unused */ static int SendBGHasStream(RTMP *r, double dId, AVal *playpath); @@ -335,6 +336,7 @@ RTMP_SetupStream(RTMP *r, uint32_t swfSize, AVal *flashVer, AVal *subscribepath, + AVal *usherToken, int dStart, int dStop, int bLiveStream, long int timeout) { @@ -355,6 +357,8 @@ RTMP_SetupStream(RTMP *r, RTMP_Log(RTMP_LOGDEBUG, "auth : %s", auth->av_val); if (subscribepath && subscribepath->av_val) RTMP_Log(RTMP_LOGDEBUG, "subscribepath : %s", subscribepath->av_val); + if (usherToken && usherToken->av_val) + RTMP_Log(RTMP_LOGDEBUG, "NetStream.Authenticate.UsherToken : %s", usherToken->av_val); if (flashVer && flashVer->av_val) RTMP_Log(RTMP_LOGDEBUG, "flashVer : %s", flashVer->av_val); if (dStart > 0) @@ -420,6 +424,8 @@ RTMP_SetupStream(RTMP *r, r->Link.flashVer = RTMP_DefaultFlashVer; if (subscribepath && subscribepath->av_len) r->Link.subscribepath = *subscribepath; + if (usherToken && usherToken->av_len) + r->Link.usherToken = *usherToken; r->Link.seekTime = dStart; r->Link.stopTime = dStop; if (bLiveStream) @@ -477,6 +483,8 @@ static struct urlopt { "Stream is live, no seeking possible" }, { AVC("subscribe"), OFF(Link.subscribepath), OPT_STR, 0, "Stream to subscribe to" }, + { AVC("jtv"), OFF(Link.usherToken), OPT_STR, 0, + "Justin.tv authentication token" }, { AVC("token"), OFF(Link.token), OPT_STR, 0, "Key for SecureToken response" }, { AVC("swfVfy"), OFF(Link.lFlags), OPT_BOOL, RTMP_LF_SWFV, @@ -1641,6 +1649,39 @@ SendFCSubscribe(RTMP *r, AVal *subscribepath) return RTMP_SendPacket(r, &packet, TRUE); } +//Justin.tv specific authentication +static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken"); //SAVC() isn't suitable for that + +static int +SendUsherToken(RTMP *r, AVal *usherToken) +{ + RTMPPacket packet; + char pbuf[1024], *pend = pbuf + sizeof(pbuf); + char *enc; + packet.m_nChannel = 0x03; /* control channel (invoke) */ + packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; + packet.m_nTimeStamp = 0; + packet.m_nInfoField2 = 0; + packet.m_hasAbsTimestamp = 0; + packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; + + RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %s", usherToken->av_val); + enc = packet.m_body; + enc = AMF_EncodeString(enc, pend, &av_NetStream_Authenticate_UsherToken); + enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); + *enc++ = AMF_NULL; + enc = AMF_EncodeString(enc, pend, usherToken); + + if (!enc) + return FALSE; + + packet.m_nBodySize = enc - packet.m_body; + + return RTMP_SendPacket(r, &packet, FALSE); +} +/******************************************/ + SAVC(releaseStream); static int @@ -2364,6 +2405,9 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) if (!(r->Link.protocol & RTMP_FEATURE_WRITE)) { + /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */ + if (r->Link.usherToken.av_len) + SendUsherToken(r, &r->Link.usherToken); /* Send the FCSubscribe if live stream or if subscribepath is set */ if (r->Link.subscribepath.av_len) SendFCSubscribe(r, &r->Link.subscribepath); diff --git a/librtmp/rtmp.h b/librtmp/rtmp.h index 1ece207..6b2ae5b 100644 --- a/librtmp/rtmp.h +++ b/librtmp/rtmp.h @@ -155,6 +155,7 @@ extern "C" AVal auth; AVal flashVer; AVal subscribepath; + AVal usherToken; AVal token; AMFObject extras; int edepth; @@ -297,6 +298,7 @@ extern "C" uint32_t swfSize, AVal *flashVer, AVal *subscribepath, + AVal *usherToken, int dStart, int dStop, int bLiveStream, long int timeout); diff --git a/rtmpdump.c b/rtmpdump.c index c1cd95b..ec1de85 100644 --- a/rtmpdump.c +++ b/rtmpdump.c @@ -692,6 +692,8 @@ void usage(char *prog) RTMP_LogPrintf ("--token|-T key Key for SecureToken response\n"); RTMP_LogPrintf + ("--jtv|-j JSON Authentication token for Justin.tv legacy servers\n"); + RTMP_LogPrintf ("--hashes|-# Display progress with hashes, not with the byte counter\n"); RTMP_LogPrintf ("--buffer|-b Buffer time in milliseconds (default: %lu)\n", @@ -738,6 +740,7 @@ main(int argc, char **argv) AVal hostname = { 0, 0 }; AVal playpath = { 0, 0 }; AVal subscribepath = { 0, 0 }; + AVal usherToken = { 0, 0 }; //Justin.tv auth token int port = -1; int protocol = RTMP_PROTOCOL_UNDEFINED; int retries = 0; @@ -839,12 +842,13 @@ main(int argc, char **argv) {"debug", 0, NULL, 'z'}, {"quiet", 0, NULL, 'q'}, {"verbose", 0, NULL, 'V'}, + {"jtv", 1, NULL, 'j'}, {0, 0, 0, 0} }; while ((opt = getopt_long(argc, argv, - "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#", + "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:", longopts, NULL)) != -1) { switch (opt) @@ -1051,6 +1055,9 @@ main(int argc, char **argv) case 'S': STR2AVAL(sockshost, optarg); break; + case 'j': + STR2AVAL(usherToken, optarg); + break; default: RTMP_LogPrintf("unknown option: %c\n", opt); usage(argv[0]); @@ -1167,7 +1174,7 @@ main(int argc, char **argv) RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath, &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, - &flashVer, &subscribepath, dSeek, dStopOffset, bLiveStream, timeout); + &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout); /* Try to keep the stream moving if it pauses on us */ if (!bLiveStream && !(protocol & RTMP_FEATURE_HTTP)) diff --git a/rtmpgw.c b/rtmpgw.c index 10a99e8..ce7319a 100644 --- a/rtmpgw.c +++ b/rtmpgw.c @@ -95,6 +95,7 @@ typedef struct AVal flashVer; AVal token; AVal subscribepath; + AVal usherToken; //Justin.tv auth token AVal sockshost; AMFObject extras; int edepth; @@ -552,7 +553,7 @@ void processTCPrequest(STREAMING_SERVER * server, // server socket and state (ou RTMP_Init(&rtmp); RTMP_SetBufferMS(&rtmp, req.bufferTime); RTMP_SetupStream(&rtmp, req.protocol, &req.hostname, req.rtmpport, &req.sockshost, - &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, dSeek, req.dStopOffset, + &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, &req.usherToken, dSeek, req.dStopOffset, req.bLiveStream, req.timeout); /* backward compatibility, we always sent this as true before */ if (req.auth.av_len) @@ -953,6 +954,9 @@ ParseOption(char opt, char *arg, RTMP_REQUEST * req) case 'z': RTMP_debuglevel = RTMP_LOGALL; break; + case 'j': + STR2AVAL(req->usherToken, arg); + break; default: RTMP_LogPrintf("unknown option: %c, arg: %s\n", opt, arg); return FALSE; @@ -1023,6 +1027,7 @@ main(int argc, char **argv) {"debug", 0, NULL, 'z'}, {"quiet", 0, NULL, 'q'}, {"verbose", 0, NULL, 'V'}, + {"jtv", 1, NULL, 'j'}, {0, 0, 0, 0} }; @@ -1035,7 +1040,7 @@ main(int argc, char **argv) while ((opt = getopt_long(argc, argv, - "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:", longopts, + "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:j:", longopts, NULL)) != -1) { switch (opt) @@ -1095,6 +1100,8 @@ main(int argc, char **argv) ("--stop|-B num Stop at num seconds into stream\n"); RTMP_LogPrintf ("--token|-T key Key for SecureToken response\n"); + RTMP_LogPrintf + ("--jtv|-j JSON Authentication token for Justin.tv legacy servers\n"); RTMP_LogPrintf ("--buffer|-b Buffer time in milliseconds (default: %lu)\n\n", defaultRTMPRequest.bufferTime); diff --git a/rtmpsrv.c b/rtmpsrv.c index f1b6c66..cf52bfa 100644 --- a/rtmpsrv.c +++ b/rtmpsrv.c @@ -116,6 +116,7 @@ typedef struct AVal swfHash; AVal flashVer; AVal subscribepath; + AVal usherToken; uint32_t swfSize; uint32_t dStartOffset;