Submitted by: Zeckma Date: 2026-06-19 Initial Package Version: 2.3 Origin: Diff from 2.3 tarball to 2.6 tag of https://git.ffmpeg.org/rtmpdump Upstream Status: Upstream Description: Brings 2.3 up to 2.6, since newer tags don't have tarballs released anymore, but upstream is active. Replicate this patch: ### git clone --branch vNEW_TAG https://git.ffmpeg.org/rtmpdump.git wget https://rtmpdump.mplayerhq.hu/download/rtmpdump-2.3.tgz tar -xf rtmpdump-2.3.tgz rm -rvf rtmpdump/.* diff -Naurp rtmpdump-2.3 rtmpdump >> rtmpdump-2.3-git.patch mv -v rtmpdump-2.3-{git,NEW_TAG}.patch ### diff '--color=auto' -Naurp rtmpdump-2.3/ChangeLog rtmpdump/ChangeLog --- rtmpdump-2.3/ChangeLog 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/ChangeLog 2026-06-19 14:15:18.299594947 -0600 @@ -1,9 +1,25 @@ RTMPDump Copyright 2008-2009 Andrej Stepanchuk; Distributed under the GPL v2 -Copyright 2009-2010 Howard Chu +Copyright 2009-2015 Howard Chu Copyright 2009 The Flvstreamer Team http://rtmpdump.mplayerhq.hu/ +1 March 2024, v2.6 +- miscellaneous cleanup of authentication and encryption support + +23 December 2015 +- miscellaneous protocol fixes +- withdraw v2.5 RTMPE type 10 support, code was too broken/non-portable + +31 March 2012, v2.5 +- add RTMPE type 10 handshake support for x86 systems + +20 July 2011 +- add NetStream.Authenticate.UsherToken for Justin.tv + +11 July 2011, v2.4 +- add RTMPE type 9 handshake support + 30 June 2010, v2.3 - fix RC4 cleanup for GnuTLS/gcrypt - declare RTMP_Write buf as const diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/amf.c rtmpdump/librtmp/amf.c --- rtmpdump-2.3/librtmp/amf.c 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/amf.c 2026-06-19 14:15:18.301606620 -0600 @@ -33,6 +33,7 @@ #include "bytes.h" static const AMFObjectProperty AMFProp_Invalid = { {0, 0}, AMF_INVALID }; +static const AMFObject AMFObj_Invalid = { 0, 0 }; static const AVal AV_empty = { 0, 0 }; /* Data is Big-Endian */ @@ -340,13 +341,19 @@ AMFProp_GetBoolean(AMFObjectProperty *pr void AMFProp_GetString(AMFObjectProperty *prop, AVal *str) { - *str = prop->p_vu.p_aval; + if (prop->p_type == AMF_STRING) + *str = prop->p_vu.p_aval; + else + *str = AV_empty; } void AMFProp_GetObject(AMFObjectProperty *prop, AMFObject *obj) { - *obj = prop->p_vu.p_object; + if (prop->p_type == AMF_OBJECT) + *obj = prop->p_vu.p_object; + else + *obj = AMFObj_Invalid; } int @@ -396,6 +403,14 @@ AMFProp_Encode(AMFObjectProperty *prop, pBuffer = AMF_Encode(&prop->p_vu.p_object, pBuffer, pBufEnd); break; + case AMF_ECMA_ARRAY: + pBuffer = AMF_EncodeEcmaArray(&prop->p_vu.p_object, pBuffer, pBufEnd); + break; + + case AMF_STRICT_ARRAY: + pBuffer = AMF_EncodeArray(&prop->p_vu.p_object, pBuffer, pBufEnd); + break; + default: RTMP_Log(RTMP_LOGERROR, "%s, invalid type. %d", __FUNCTION__, prop->p_type); pBuffer = NULL; @@ -463,6 +478,8 @@ AMF3ReadString(const char *data, AVal *s RTMP_Log(RTMP_LOGDEBUG, "%s, string reference, index: %d, not supported, ignoring!", __FUNCTION__, refIndex); + str->av_val = NULL; + str->av_len = 0; return len; } else @@ -502,9 +519,11 @@ AMF3Prop_Decode(AMFObjectProperty *prop, if (name.av_len <= 0) return nRes; + nSize -= nRes; + if (nSize <= 0) + return -1; prop->p_name = name; pBuffer += nRes; - nSize -= nRes; } /* decode */ @@ -586,10 +605,12 @@ AMF3Prop_Decode(AMFObjectProperty *prop, case AMF3_ARRAY: case AMF3_BYTE_ARRAY: default: - RTMP_Log(RTMP_LOGDEBUG, "%s - AMF3 unknown/unsupported datatype 0x%02x, @0x%08X", + RTMP_Log(RTMP_LOGDEBUG, "%s - AMF3 unknown/unsupported datatype 0x%02x, @%p", __FUNCTION__, (unsigned char)(*pBuffer), pBuffer); return -1; } + if (nSize < 0) + return -1; return nOriginalSize - nSize; } @@ -700,7 +721,6 @@ AMFProp_Decode(AMFObjectProperty *prop, if (nRes == -1) return -1; nSize -= nRes; - prop->p_type = AMF_OBJECT; break; } case AMF_OBJECT_END: @@ -718,7 +738,6 @@ AMFProp_Decode(AMFObjectProperty *prop, if (nRes == -1) return -1; nSize -= nRes; - prop->p_type = AMF_OBJECT; break; } case AMF_DATE: @@ -735,13 +754,15 @@ AMFProp_Decode(AMFObjectProperty *prop, break; } case AMF_LONG_STRING: + case AMF_XML_DOC: { unsigned int nStringSize = AMF_DecodeInt32(pBuffer); if (nSize < (long)nStringSize + 4) return -1; AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval); nSize -= (4 + nStringSize); - prop->p_type = AMF_STRING; + if (prop->p_type == AMF_LONG_STRING) + prop->p_type = AMF_STRING; break; } case AMF_RECORDSET: @@ -750,12 +771,6 @@ AMFProp_Decode(AMFObjectProperty *prop, return -1; break; } - case AMF_XML_DOC: - { - RTMP_Log(RTMP_LOGERROR, "AMF_XML_DOC not supported!"); - return -1; - break; - } case AMF_TYPED_OBJECT: { RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!"); @@ -772,7 +787,7 @@ AMFProp_Decode(AMFObjectProperty *prop, break; } default: - RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @0x%08X", __FUNCTION__, + RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__, prop->p_type, pBuffer - 1); return -1; } @@ -819,6 +834,18 @@ AMFProp_Dump(AMFObjectProperty *prop) AMF_Dump(&prop->p_vu.p_object); return; } + else if (prop->p_type == AMF_ECMA_ARRAY) + { + RTMP_Log(RTMP_LOGDEBUG, "Property: <%sECMA_ARRAY>", strRes); + AMF_Dump(&prop->p_vu.p_object); + return; + } + else if (prop->p_type == AMF_STRICT_ARRAY) + { + RTMP_Log(RTMP_LOGDEBUG, "Property: <%sSTRICT_ARRAY>", strRes); + AMF_Dump(&prop->p_vu.p_object); + return; + } switch (prop->p_type) { @@ -847,7 +874,8 @@ AMFProp_Dump(AMFObjectProperty *prop) void AMFProp_Reset(AMFObjectProperty *prop) { - if (prop->p_type == AMF_OBJECT) + if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY || + prop->p_type == AMF_STRICT_ARRAY) AMF_Reset(&prop->p_vu.p_object); else { @@ -892,6 +920,76 @@ AMF_Encode(AMFObject *obj, char *pBuffer return pBuffer; } +char * +AMF_EncodeEcmaArray(AMFObject *obj, char *pBuffer, char *pBufEnd) +{ + int i; + + if (pBuffer+4 >= pBufEnd) + return NULL; + + *pBuffer++ = AMF_ECMA_ARRAY; + + pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num); + + for (i = 0; i < obj->o_num; i++) + { + char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd); + if (res == NULL) + { + RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d", + i); + break; + } + else + { + pBuffer = res; + } + } + + if (pBuffer + 3 >= pBufEnd) + return NULL; /* no room for the end marker */ + + pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END); + + return pBuffer; +} + +char * +AMF_EncodeArray(AMFObject *obj, char *pBuffer, char *pBufEnd) +{ + int i; + + if (pBuffer+4 >= pBufEnd) + return NULL; + + *pBuffer++ = AMF_STRICT_ARRAY; + + pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num); + + for (i = 0; i < obj->o_num; i++) + { + char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd); + if (res == NULL) + { + RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d", + i); + break; + } + else + { + pBuffer = res; + } + } + + //if (pBuffer + 3 >= pBufEnd) + // return NULL; /* no room for the end marker */ + + //pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END); + + return pBuffer; +} + int AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize, int nArrayLen, int bDecodeName) @@ -907,9 +1005,17 @@ AMF_DecodeArray(AMFObject *obj, const ch int nRes; nArrayLen--; + if (nSize <= 0) + { + bError = TRUE; + break; + } nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName); if (nRes == -1) - bError = TRUE; + { + bError = TRUE; + break; + } else { nSize -= nRes; @@ -968,12 +1074,12 @@ AMF3_Decode(AMFObject *obj, const char * else { int32_t classExtRef = (classRef >> 1); - int i; + int i, cdnum; cd.cd_externalizable = (classExtRef & 0x1) == 1; cd.cd_dynamic = ((classExtRef >> 1) & 0x1) == 1; - cd.cd_num = classExtRef >> 2; + cdnum = classExtRef >> 2; /* class name */ @@ -988,9 +1094,16 @@ AMF3_Decode(AMFObject *obj, const char * cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic, cd.cd_num); - for (i = 0; i < cd.cd_num; i++) + for (i = 0; i < cdnum; i++) { AVal memberName; + if (nSize <=0) + { +invalid: + RTMP_Log(RTMP_LOGDEBUG, "%s, invalid class encoding!", + __FUNCTION__); + return nOriginalSize; + } len = AMF3ReadString(pBuffer, &memberName); RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val); AMF3CD_AddProp(&cd, &memberName); @@ -1026,6 +1139,8 @@ AMF3_Decode(AMFObject *obj, const char * int nRes, i; for (i = 0; i < cd.cd_num; i++) /* non-dynamic */ { + if (nSize <=0) + goto invalid; nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE); if (nRes == -1) RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!", @@ -1043,6 +1158,8 @@ AMF3_Decode(AMFObject *obj, const char * do { + if (nSize <=0) + goto invalid; nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, TRUE); AMF_AddProp(obj, &prop); @@ -1090,10 +1207,18 @@ AMF_Decode(AMFObject *obj, const char *p nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName); if (nRes == -1) - bError = TRUE; + { + bError = TRUE; + break; + } else { nSize -= nRes; + if (nSize < 0) + { + bError = TRUE; + break; + } pBuffer += nRes; AMF_AddProp(obj, &prop); } @@ -1111,7 +1236,7 @@ AMF_AddProp(AMFObject *obj, const AMFObj if (!(obj->o_num & 0x0f)) obj->o_props = realloc(obj->o_props, (obj->o_num + 16) * sizeof(AMFObjectProperty)); - obj->o_props[obj->o_num++] = *prop; + memcpy(&obj->o_props[obj->o_num++], prop, sizeof(AMFObjectProperty)); } int @@ -1125,7 +1250,7 @@ AMF_GetProp(AMFObject *obj, const AVal * { if (nIndex >= 0) { - if (nIndex <= obj->o_num) + if (nIndex < obj->o_num) return &obj->o_props[nIndex]; } else diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/amf.h rtmpdump/librtmp/amf.h --- rtmpdump-2.3/librtmp/amf.h 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/amf.h 2026-06-19 14:15:18.301606620 -0600 @@ -104,6 +104,9 @@ extern "C" double AMF_DecodeNumber(const char *data); char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd); + char *AMF_EncodeEcmaArray(AMFObject *obj, char *pBuffer, char *pBufEnd); + char *AMF_EncodeArray(AMFObject *obj, char *pBuffer, char *pBufEnd); + int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize, int bDecodeName); int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize, diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/dh.h rtmpdump/librtmp/dh.h --- rtmpdump-2.3/librtmp/dh.h 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/dh.h 2026-06-19 14:15:18.301606620 -0600 @@ -30,18 +30,22 @@ #ifdef USE_POLARSSL #include typedef mpi * MP_t; -#define MP_new(m) m = malloc(sizeof(mpi)); mpi_init(m, NULL) +#define MP_new(m) m = malloc(sizeof(mpi)); mpi_init(m) #define MP_set_w(mpi, w) mpi_lset(mpi, w) #define MP_cmp(u, v) mpi_cmp_mpi(u, v) #define MP_set(u, v) mpi_copy(u, v) #define MP_sub_w(mpi, w) mpi_sub_int(mpi, mpi, w) #define MP_cmp_1(mpi) mpi_cmp_int(mpi, 1) #define MP_modexp(r, y, q, p) mpi_exp_mod(r, y, q, p, NULL) -#define MP_free(mpi) mpi_free(mpi, NULL); free(mpi) +#define MP_free(mpi) mpi_free(mpi); free(mpi) #define MP_gethex(u, hex, res) MP_new(u); res = mpi_read_string(u, 16, hex) == 0 #define MP_bytes(u) mpi_size(u) #define MP_setbin(u,buf,len) mpi_write_binary(u,buf,len) #define MP_getbin(u,buf,len) MP_new(u); mpi_read_binary(u,buf,len) +#define MP_setpg(dh, p, g) dh->p = p; dh->g = g +#define MP_setlength(dh, l) dh->length = l +#define MP_getp(dh) dh->p +#define MP_getpubkey(dh) dh->pub_key typedef struct MDH { MP_t p; @@ -53,7 +57,7 @@ typedef struct MDH { } MDH; #define MDH_new() calloc(1,sizeof(MDH)) -#define MDH_free(vp) {MDH *dh = vp; dhm_free(&dh->ctx); MP_free(dh->p); MP_free(dh->g); MP_free(dh->pub_key); MP_free(dh->priv_key); free(dh);} +#define MDH_free(vp) {MDH *_dh = vp; dhm_free(&_dh->ctx); MP_free(_dh->p); MP_free(_dh->g); MP_free(_dh->pub_key); MP_free(_dh->priv_key); free(_dh);} static int MDH_generate_key(MDH *dh) { @@ -61,7 +65,7 @@ static int MDH_generate_key(MDH *dh) MP_set(&dh->ctx.P, dh->p); MP_set(&dh->ctx.G, dh->g); dh->ctx.len = 128; - dhm_make_public(&dh->ctx, 1024, out, 1, havege_rand, &RTMP_TLS_ctx->hs); + dhm_make_public(&dh->ctx, 1024, out, 1, havege_random, &RTMP_TLS_ctx->hs); MP_new(dh->pub_key); MP_new(dh->priv_key); MP_set(dh->pub_key, &dh->ctx.GX); @@ -71,27 +75,32 @@ static int MDH_generate_key(MDH *dh) static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh) { - int n = len; MP_set(&dh->ctx.GY, pub); - dhm_calc_secret(&dh->ctx, secret, &n); + dhm_calc_secret(&dh->ctx, secret, &len); return 0; } #elif defined(USE_GNUTLS) -#include -typedef gcry_mpi_t MP_t; -#define MP_new(m) m = gcry_mpi_new(1) -#define MP_set_w(mpi, w) gcry_mpi_set_ui(mpi, w) -#define MP_cmp(u, v) gcry_mpi_cmp(u, v) -#define MP_set(u, v) gcry_mpi_set(u, v) -#define MP_sub_w(mpi, w) gcry_mpi_sub_ui(mpi, mpi, w) -#define MP_cmp_1(mpi) gcry_mpi_cmp_ui(mpi, 1) -#define MP_modexp(r, y, q, p) gcry_mpi_powm(r, y, q, p) -#define MP_free(mpi) gcry_mpi_release(mpi) -#define MP_gethex(u, hex, res) res = (gcry_mpi_scan(&u, GCRYMPI_FMT_HEX, hex, 0, 0) == 0) -#define MP_bytes(u) (gcry_mpi_get_nbits(u) + 7) / 8 -#define MP_setbin(u,buf,len) gcry_mpi_print(GCRYMPI_FMT_USG,buf,len,NULL,u) -#define MP_getbin(u,buf,len) gcry_mpi_scan(&u,GCRYMPI_FMT_USG,buf,len,NULL) +#include +#include +#include +typedef mpz_ptr MP_t; +#define MP_new(m) m = malloc(sizeof(*m)); mpz_init2(m, 1) +#define MP_set_w(mpi, w) mpz_set_ui(mpi, w) +#define MP_cmp(u, v) mpz_cmp(u, v) +#define MP_set(u, v) mpz_set(u, v) +#define MP_sub_w(mpi, w) mpz_sub_ui(mpi, mpi, w) +#define MP_cmp_1(mpi) mpz_cmp_ui(mpi, 1) +#define MP_modexp(r, y, q, p) mpz_powm(r, y, q, p) +#define MP_free(mpi) mpz_clear(mpi); free(mpi) +#define MP_gethex(u, hex, res) u = malloc(sizeof(*u)); mpz_init2(u, 1); res = (mpz_set_str(u, hex, 16) == 0) +#define MP_bytes(u) (mpz_sizeinbase(u, 2) + 7) / 8 +#define MP_setbin(u,buf,len) nettle_mpz_get_str_256(len,buf,u) +#define MP_getbin(u,buf,len) u = malloc(sizeof(*u)); mpz_init2(u, 1); nettle_mpz_set_str_256_u(u,len,buf) +#define MP_setpg(dh, p, g) dh->p = p; dh->g = g +#define MP_setlength(dh, l) dh->length = l +#define MP_getp(dh) dh->p +#define MP_getpubkey(dh) dh->pub_key typedef struct MDH { MP_t p; @@ -104,21 +113,62 @@ typedef struct MDH { #define MDH_new() calloc(1,sizeof(MDH)) #define MDH_free(dh) do {MP_free(((MDH*)(dh))->p); MP_free(((MDH*)(dh))->g); MP_free(((MDH*)(dh))->pub_key); MP_free(((MDH*)(dh))->priv_key); free(dh);} while(0) -extern MP_t gnutls_calc_dh_secret(MP_t *priv, MP_t g, MP_t p); -extern MP_t gnutls_calc_dh_key(MP_t y, MP_t x, MP_t p); +static int MDH_generate_key(MDH *dh) +{ + int num_bytes; + uint32_t seed; + gmp_randstate_t rs; + + num_bytes = (mpz_sizeinbase(dh->p, 2) + 7) / 8 - 1; + if (num_bytes <= 0 || num_bytes > 18000) + return 0; + + dh->priv_key = calloc(1, sizeof(*dh->priv_key)); + if (!dh->priv_key) + return 0; + mpz_init2(dh->priv_key, 1); + gnutls_rnd(GNUTLS_RND_RANDOM, &seed, sizeof(seed)); + gmp_randinit_mt(rs); + gmp_randseed_ui(rs, seed); + mpz_urandomb(dh->priv_key, rs, num_bytes); + gmp_randclear(rs); + + dh->pub_key = calloc(1, sizeof(*dh->pub_key)); + if (!dh->pub_key) + return 0; + mpz_init2(dh->pub_key, 1); + if (!dh->pub_key) { + mpz_clear(dh->priv_key); + free(dh->priv_key); + return 0; + } + + mpz_powm(dh->pub_key, dh->g, dh->priv_key, dh->p); + + return 1; +} -#define MDH_generate_key(dh) (dh->pub_key = gnutls_calc_dh_secret(&dh->priv_key, dh->g, dh->p)) static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh) { - MP_t sec = gnutls_calc_dh_key(pub, dh->priv_key, dh->p); - if (sec) - { - MP_setbin(sec, secret, len); - MP_free(sec); - return 0; - } - else + mpz_ptr k; + int num_bytes; + + num_bytes = (mpz_sizeinbase(dh->p, 2) + 7) / 8; + if (num_bytes <= 0 || num_bytes > 18000) + return -1; + + k = calloc(1, sizeof(*k)); + if (!k) return -1; + mpz_init2(k, 1); + + mpz_powm(k, pub, dh->priv_key, dh->p); + nettle_mpz_get_str_256(len, secret, k); + mpz_clear(k); + free(k); + + /* return the length of the shared secret key like DH_compute_key */ + return len; } #else /* USE_OPENSSL */ @@ -145,6 +195,17 @@ typedef BIGNUM * MP_t; #define MDH_generate_key(dh) DH_generate_key(dh) #define MDH_compute_key(secret, seclen, pub, dh) DH_compute_key(secret, pub, dh) +#if OPENSSL_VERSION_NUMBER >= 0x10100000 +#define MP_setpg(dh, p, g) DH_set0_pqg(dh, p, NULL, g) +#define MP_setlength(dh, l) DH_set_length(dh, l) +#define MP_getp(dh) DH_get0_p(dh) +#define MP_getpubkey(dh) DH_get0_pub_key(dh) +#else +#define MP_setpg(dh, p, g) dh->p = p; dh->g = g +#define MP_setlength(dh, l) dh->length = l +#define MP_getp(dh) dh->p +#define MP_getpubkey(dh) dh->pub_key +#endif #endif #include "log.h" @@ -207,24 +268,26 @@ DHInit(int nKeyBits) { size_t res; MDH *dh = MDH_new(); + MP_t g, p; if (!dh) goto failed; - MP_new(dh->g); + MP_new(g); - if (!dh->g) + if (!g) goto failed; - MP_gethex(dh->p, P1024, res); /* prime P1024, see dhgroups.h */ + MP_gethex(p, P1024, res); /* prime P1024, see dhgroups.h */ if (!res) { goto failed; } - MP_set_w(dh->g, 2); /* base 2 */ + MP_set_w(g, 2); /* base 2 */ + MP_setpg(dh, p, g); - dh->length = nKeyBits; + MP_setlength(dh, nKeyBits); return dh; failed: @@ -237,31 +300,35 @@ failed: static int DHGenerateKey(MDH *dh) { - size_t res = 0; + MP_t q1; + size_t res; if (!dh) return 0; - while (!res) - { - MP_t q1 = NULL; - - if (!MDH_generate_key(dh)) - return 0; - - MP_gethex(q1, Q1024, res); - assert(res); + MP_gethex(q1, Q1024, res); + assert(res); - res = isValidPublicKey(dh->pub_key, dh->p, q1); - if (!res) + do + { + if (MDH_generate_key(dh)) + { + MP_t key = (MP_t)MP_getpubkey(dh); + MP_t p = (MP_t)MP_getp(dh); + res = isValidPublicKey(key, p, q1); + } + else { +#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000 MP_free(dh->pub_key); MP_free(dh->priv_key); dh->pub_key = dh->priv_key = 0; +#endif + res = 0; + break; } - - MP_free(q1); - } - return 1; + } while (!res); + MP_free(q1); + return res; } /* fill pubkey with the public key in BIG ENDIAN order @@ -272,15 +339,16 @@ static int DHGetPublicKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen) { int len; - if (!dh || !dh->pub_key) + MP_t pub_key; + if (!dh || !(pub_key = (MP_t)MP_getpubkey(dh))) return 0; - len = MP_bytes(dh->pub_key); + len = MP_bytes(pub_key); if (len <= 0 || len > (int) nPubkeyLen) return 0; memset(pubkey, 0, nPubkeyLen); - MP_setbin(dh->pub_key, pubkey + (nPubkeyLen - len), len); + MP_setbin(pub_key, pubkey + (nPubkeyLen - len), len); return 1; } @@ -322,7 +390,7 @@ DHComputeSharedSecretKey(MDH *dh, uint8_ MP_gethex(q1, Q1024, len); assert(len); - if (isValidPublicKey(pubkeyBn, dh->p, q1)) + if (isValidPublicKey(pubkeyBn, (MP_t)MP_getp(dh), q1)) res = MDH_compute_key(secret, nPubkeyLen, pubkeyBn, dh); else res = -1; diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/handshake.h rtmpdump/librtmp/handshake.h --- rtmpdump-2.3/librtmp/handshake.h 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/handshake.h 2026-06-19 14:15:18.301606620 -0600 @@ -43,21 +43,24 @@ typedef arc4_context * RC4_handle; #define RC4_free(h) free(h) #elif defined(USE_GNUTLS) -#include +#include +#include #ifndef SHA256_DIGEST_LENGTH #define SHA256_DIGEST_LENGTH 32 #endif -#define HMAC_CTX gcry_md_hd_t -#define HMAC_setup(ctx, key, len) gcry_md_open(&ctx, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); gcry_md_setkey(ctx, key, len) -#define HMAC_crunch(ctx, buf, len) gcry_md_write(ctx, buf, len) -#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; memcpy(dig, gcry_md_read(ctx, 0), dlen); gcry_md_close(ctx) - -typedef gcry_cipher_hd_t RC4_handle; -#define RC4_alloc(h) gcry_cipher_open(h, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0) -#define RC4_setkey(h,l,k) gcry_cipher_setkey(h,k,l) -#define RC4_encrypt(h,l,d) gcry_cipher_encrypt(h,(void *)d,l,NULL,0) -#define RC4_encrypt2(h,l,s,d) gcry_cipher_encrypt(h,(void *)d,l,(void *)s,l) -#define RC4_free(h) gcry_cipher_close(h) +#undef HMAC_CTX +#define HMAC_CTX struct hmac_sha256_ctx +#define HMAC_setup(ctx, key, len) hmac_sha256_set_key(&ctx, len, key) +#define HMAC_crunch(ctx, buf, len) hmac_sha256_update(&ctx, len, buf) +#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig) +#define HMAC_close(ctx) + +typedef struct arcfour_ctx* RC4_handle; +#define RC4_alloc(h) *h = malloc(sizeof(struct arcfour_ctx)) +#define RC4_setkey(h,l,k) arcfour_set_key(h, l, k) +#define RC4_encrypt(h,l,d) arcfour_crypt(h,l,(uint8_t *)d,(uint8_t *)d) +#define RC4_encrypt2(h,l,s,d) arcfour_crypt(h,l,(uint8_t *)d,(uint8_t *)s) +#define RC4_free(h) free(h) #else /* USE_OPENSSL */ #include @@ -66,9 +69,16 @@ typedef gcry_cipher_hd_t RC4_handle; #if OPENSSL_VERSION_NUMBER < 0x0090800 || !defined(SHA256_DIGEST_LENGTH) #error Your OpenSSL is too old, need 0.9.8 or newer with SHA256 #endif +#if OPENSSL_VERSION_NUMBER >= 0x10100000 +#define HMAC_CTX HMAC_CTX * +#define HMAC_setup(ctx, key, len) ctx = HMAC_CTX_new(); HMAC_Init_ex(ctx, key, len, EVP_sha256(), 0) +#define HMAC_crunch(ctx, buf, len) HMAC_Update(ctx, buf, len) +#define HMAC_finish(ctx, dig, dlen) HMAC_Final(ctx, dig, &dlen); HMAC_CTX_free(ctx) +#else #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, key, len, EVP_sha256(), 0) #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, buf, len) #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, dig, &dlen); HMAC_CTX_cleanup(&ctx) +#endif typedef RC4_KEY * RC4_handle; #define RC4_alloc(h) *h = malloc(sizeof(RC4_KEY)) @@ -358,6 +368,337 @@ static void rtmpe8_sig(uint8_t *in, uint out[7] = v1; } +/* RTMPE type 9 uses Blowfish on the regular signature + * http://en.wikipedia.org/wiki/Blowfish_(cipher) + */ +#define BF_ROUNDS 16 +typedef struct bf_key { + uint32_t s[4][256]; + uint32_t p[BF_ROUNDS+2]; +} bf_key; + +static const uint32_t bf_sinit[][256] = { + + /* S-Box 0 */ + { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, + 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, + 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, + 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, + 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, + 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, + 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, + 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, + 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, + 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, + 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, + 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, + 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, + 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, + 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, + 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, + 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, + 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, + 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, + 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, + 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, + 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, }, + + /* S-Box 1 */ + { 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, + 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, + 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, + 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, + 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, + 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, + 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, + 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, + 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, + 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, + 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, + 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, + 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, + 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, + 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, + 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, + 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, + 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, + 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, + 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, + 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, + 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, }, + + /* S-Box 2 */ + { 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, + 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, + 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, + 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, + 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, + 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, + 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, + 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, + 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, + 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, + 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, + 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, + 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, + 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, + 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, + 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, + 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, + 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, + 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, + 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, + 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, + 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, }, + + /* S-Box 3 */ + { 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, + 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, + 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, + 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, + 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, + 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, + 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, + 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, + 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, + 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, + 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, + 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, + 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, + 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, + 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, + 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, + 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, + 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, + 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, + 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, + 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, + 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, }, +}; + +static const uint32_t bf_pinit[] = { + /* P-Box */ + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, + 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b, +}; + +#define KEYBYTES 24 + +static const unsigned char rtmpe9_keys[16][KEYBYTES] = { + { 0x79, 0x34, 0x77, 0x4c, 0x67, 0xd1, 0x38, 0x3a, 0xdf, 0xb3, 0x56, 0xbe, + 0x8b, 0x7b, 0xd0, 0x24, 0x38, 0xe0, 0x73, 0x58, 0x41, 0x5d, 0x69, 0x67, }, + { 0x46, 0xf6, 0xb4, 0xcc, 0x01, 0x93, 0xe3, 0xa1, 0x9e, 0x7d, 0x3c, 0x65, + 0x55, 0x86, 0xfd, 0x09, 0x8f, 0xf7, 0xb3, 0xc4, 0x6f, 0x41, 0xca, 0x5c, }, + { 0x1a, 0xe7, 0xe2, 0xf3, 0xf9, 0x14, 0x79, 0x94, 0xc0, 0xd3, 0x97, 0x43, + 0x08, 0x7b, 0xb3, 0x84, 0x43, 0x2f, 0x9d, 0x84, 0x3f, 0x21, 0x01, 0x9b, }, + { 0xd3, 0xe3, 0x54, 0xb0, 0xf7, 0x1d, 0xf6, 0x2b, 0x5a, 0x43, 0x4d, 0x04, + 0x83, 0x64, 0x3e, 0x0d, 0x59, 0x2f, 0x61, 0xcb, 0xb1, 0x6a, 0x59, 0x0d, }, + { 0xc8, 0xc1, 0xe9, 0xb8, 0x16, 0x56, 0x99, 0x21, 0x7b, 0x5b, 0x36, 0xb7, + 0xb5, 0x9b, 0xdf, 0x06, 0x49, 0x2c, 0x97, 0xf5, 0x95, 0x48, 0x85, 0x7e, }, + { 0xeb, 0xe5, 0xe6, 0x2e, 0xa4, 0xba, 0xd4, 0x2c, 0xf2, 0x16, 0xe0, 0x8f, + 0x66, 0x23, 0xa9, 0x43, 0x41, 0xce, 0x38, 0x14, 0x84, 0x95, 0x00, 0x53, }, + { 0x66, 0xdb, 0x90, 0xf0, 0x3b, 0x4f, 0xf5, 0x6f, 0xe4, 0x9c, 0x20, 0x89, + 0x35, 0x5e, 0xd2, 0xb2, 0xc3, 0x9e, 0x9f, 0x7f, 0x63, 0xb2, 0x28, 0x81, }, + { 0xbb, 0x20, 0xac, 0xed, 0x2a, 0x04, 0x6a, 0x19, 0x94, 0x98, 0x9b, 0xc8, + 0xff, 0xcd, 0x93, 0xef, 0xc6, 0x0d, 0x56, 0xa7, 0xeb, 0x13, 0xd9, 0x30, }, + { 0xbc, 0xf2, 0x43, 0x82, 0x09, 0x40, 0x8a, 0x87, 0x25, 0x43, 0x6d, 0xe6, + 0xbb, 0xa4, 0xb9, 0x44, 0x58, 0x3f, 0x21, 0x7c, 0x99, 0xbb, 0x3f, 0x24, }, + { 0xec, 0x1a, 0xaa, 0xcd, 0xce, 0xbd, 0x53, 0x11, 0xd2, 0xfb, 0x83, 0xb6, + 0xc3, 0xba, 0xab, 0x4f, 0x62, 0x79, 0xe8, 0x65, 0xa9, 0x92, 0x28, 0x76, }, + { 0xc6, 0x0c, 0x30, 0x03, 0x91, 0x18, 0x2d, 0x7b, 0x79, 0xda, 0xe1, 0xd5, + 0x64, 0x77, 0x9a, 0x12, 0xc5, 0xb1, 0xd7, 0x91, 0x4f, 0x96, 0x4c, 0xa3, }, + { 0xd7, 0x7c, 0x2a, 0xbf, 0xa6, 0xe7, 0x85, 0x7c, 0x45, 0xad, 0xff, 0x12, + 0x94, 0xd8, 0xde, 0xa4, 0x5c, 0x3d, 0x79, 0xa4, 0x44, 0x02, 0x5d, 0x22, }, + { 0x16, 0x19, 0x0d, 0x81, 0x6a, 0x4c, 0xc7, 0xf8, 0xb8, 0xf9, 0x4e, 0xcd, + 0x2c, 0x9e, 0x90, 0x84, 0xb2, 0x08, 0x25, 0x60, 0xe1, 0x1e, 0xae, 0x18, }, + { 0xe9, 0x7c, 0x58, 0x26, 0x1b, 0x51, 0x9e, 0x49, 0x82, 0x60, 0x61, 0xfc, + 0xa0, 0xa0, 0x1b, 0xcd, 0xf5, 0x05, 0xd6, 0xa6, 0x6d, 0x07, 0x88, 0xa3, }, + { 0x2b, 0x97, 0x11, 0x8b, 0xd9, 0x4e, 0xd9, 0xdf, 0x20, 0xe3, 0x9c, 0x10, + 0xe6, 0xa1, 0x35, 0x21, 0x11, 0xf9, 0x13, 0x0d, 0x0b, 0x24, 0x65, 0xb2, }, + { 0x53, 0x6a, 0x4c, 0x54, 0xac, 0x8b, 0x9b, 0xb8, 0x97, 0x29, 0xfc, 0x60, + 0x2c, 0x5b, 0x3a, 0x85, 0x68, 0xb5, 0xaa, 0x6a, 0x44, 0xcd, 0x3f, 0xa7, }, +}; + +#define BF_ENC(X,S) \ + (((S[0][X>>24] + S[1][X>>16 & 0xff]) ^ S[2][(X>>8) & 0xff]) + S[3][X & 0xff]) + +static void bf_enc(uint32_t *x, bf_key *key) +{ + uint32_t Xl; + uint32_t Xr; + uint32_t temp; + int i; + + Xl = x[0]; + Xr = x[1]; + + for (i = 0; i < BF_ROUNDS; ++i) { + Xl ^= key->p[i]; + Xr ^= BF_ENC(Xl,key->s); + + temp = Xl; + Xl = Xr; + Xr = temp; + } + + Xl ^= key->p[BF_ROUNDS]; + Xr ^= key->p[BF_ROUNDS + 1]; + + x[0] = Xr; + x[1] = Xl; +} + +static void bf_setkey(const unsigned char *kp, int keybytes, bf_key *key) +{ + int i; + int j; + int k; + uint32_t data; + uint32_t d[2]; + + memcpy(key->p, bf_pinit, sizeof(key->p)); + memcpy(key->s, bf_sinit, sizeof(key->s)); + + j = 0; + for (i = 0; i < BF_ROUNDS + 2; ++i) { + data = 0x00000000; + for (k = 0; k < 4; ++k) { + data = (data << 8) | kp[j]; + j = j + 1; + if (j >= keybytes) { + j = 0; + } + } + key->p[i] ^= data; + } + + d[0] = 0x00000000; + d[1] = 0x00000000; + + for (i = 0; i < BF_ROUNDS + 2; i += 2) { + bf_enc(d, key); + + key->p[i] = d[0]; + key->p[i + 1] = d[1]; + } + + for (i = 0; i < 4; ++i) { + for (j = 0; j < 256; j += 2) { + + bf_enc(d, key); + + key->s[i][j] = d[0]; + key->s[i][j + 1] = d[1]; + } + } +} + +static void rtmpe9_sig(uint8_t *in, uint8_t *out, int keyid) +{ + uint32_t d[2]; + bf_key key; + + bf_setkey(rtmpe9_keys[keyid], KEYBYTES, &key); + + /* input is little-endian */ + d[0] = in[0] | (in[1] << 8) | (in[2] << 16) | (in[3] << 24); + d[1] = in[4] | (in[5] << 8) | (in[6] << 16) | (in[7] << 24); + bf_enc(d, &key); + out[0] = d[0] & 0xff; + out[1] = (d[0] >> 8) & 0xff; + out[2] = (d[0] >> 16) & 0xff; + out[3] = (d[0] >> 24) & 0xff; + out[4] = d[1] & 0xff; + out[5] = (d[1] >> 8) & 0xff; + out[6] = (d[1] >> 16) & 0xff; + out[7] = (d[1] >> 24) & 0xff; +} + static int HandShake(RTMP * r, int FP9HandShake) { @@ -605,8 +946,7 @@ HandShake(RTMP * r, int FP9HandShake) for (i=0; i #include #include +#include #include "rtmp_sys.h" #include "log.h" @@ -42,33 +43,43 @@ #define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig) #define HMAC_close(ctx) #elif defined(USE_GNUTLS) -#include -#include +#include #ifndef SHA256_DIGEST_LENGTH #define SHA256_DIGEST_LENGTH 32 #endif -#define HMAC_CTX gcry_md_hd_t -#define HMAC_setup(ctx, key, len) gcry_md_open(&ctx, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); gcry_md_setkey(ctx, key, len) -#define HMAC_crunch(ctx, buf, len) gcry_md_write(ctx, buf, len) -#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; memcpy(dig, gcry_md_read(ctx, 0), dlen) -#define HMAC_close(ctx) gcry_md_close(ctx) +#undef HMAC_CTX +#define HMAC_CTX struct hmac_sha256_ctx +#define HMAC_setup(ctx, key, len) hmac_sha256_set_key(&ctx, len, key) +#define HMAC_crunch(ctx, buf, len) hmac_sha256_update(&ctx, len, buf) +#define HMAC_finish(ctx, dig, dlen) dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig) +#define HMAC_close(ctx) #else /* USE_OPENSSL */ #include #include #include #include +#if OPENSSL_VERSION_NUMBER >= 0x10100000 +#define HMAC_CTX HMAC_CTX * +#define HMAC_setup(ctx, key, len) ctx = HMAC_CTX_new(); HMAC_Init_ex(ctx, key, len, EVP_sha256(), 0) +#define HMAC_crunch(ctx, buf, len) HMAC_Update(ctx, buf, len) +#define HMAC_finish(ctx, dig, dlen) HMAC_Final(ctx, dig, &dlen) +#define HMAC_close(ctx) HMAC_CTX_free(ctx) +#else #define HMAC_setup(ctx, key, len) HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0) #define HMAC_crunch(ctx, buf, len) HMAC_Update(&ctx, (unsigned char *)buf, len) #define HMAC_finish(ctx, dig, dlen) HMAC_Final(&ctx, (unsigned char *)dig, &dlen); #define HMAC_close(ctx) HMAC_CTX_cleanup(&ctx) #endif +#endif extern void RTMP_TLS_Init(); extern TLS_CTX RTMP_TLS_ctx; +#include + #endif /* CRYPTO */ -#include +#define DATELEN 64 #define AGENT "Mozilla/5.0" @@ -82,7 +93,8 @@ HTTP_get(struct HTTP_ctx *http, const ch #ifdef CRYPTO int ssl = 0; #endif - int hlen, flen = 0; + int hlen; + long flen = 0; int rc, i; int len_known; HTTPResult ret = HTTPRES_OK; @@ -141,7 +153,7 @@ HTTP_get(struct HTTP_ctx *http, const ch return HTTPRES_LOST_CONNECTION; i = sprintf(sb.sb_buf, - "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferrer: %.*s\r\n", + "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferer: %.*s\r\n", path, AGENT, host, (int)(path - url + 1), url); if (http->date[0]) i += sprintf(sb.sb_buf + i, "If-Modified-Since: %s\r\n", http->date); @@ -163,7 +175,7 @@ HTTP_get(struct HTTP_ctx *http, const ch #else TLS_client(RTMP_TLS_ctx, sb.sb_ssl); TLS_setfd(sb.sb_ssl, sb.sb_socket); - if ((i = TLS_connect(sb.sb_ssl)) < 0) + if (TLS_connect(sb.sb_ssl) < 0) { RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__); ret = HTTPRES_LOST_CONNECTION; @@ -241,14 +253,20 @@ HTTP_get(struct HTTP_ctx *http, const ch if (!strncasecmp (sb.sb_start, "Content-Length: ", sizeof("Content-Length: ") - 1)) { - flen = atoi(sb.sb_start + sizeof("Content-Length: ") - 1); + flen = strtol(sb.sb_start + sizeof("Content-Length: ") - 1, NULL, 10); + if (flen < 1 || flen > INT_MAX) + { + ret = HTTPRES_BAD_REQUEST; + goto leave; + } } else if (!strncasecmp (sb.sb_start, "Last-Modified: ", sizeof("Last-Modified: ") - 1)) { *p2 = '\0'; - strcpy(http->date, sb.sb_start + sizeof("Last-Modified: ") - 1); + strncpy(http->date, sb.sb_start + sizeof("Last-Modified: ") - 1, DATELEN-1); + http->date[DATELEN-1] = '\0'; } p2 += 2; sb.sb_size -= p2 - sb.sb_start; @@ -435,7 +453,7 @@ make_unix_time(char *s) /* Convert a Unix time to a network time string * Weekday, DD-MMM-YYYY HH:MM:SS GMT */ -void +static void strtime(time_t * t, char *s) { struct tm *tm; @@ -453,7 +471,7 @@ RTMP_HashSWF(const char *url, unsigned i int age) { FILE *f = NULL; - char *path, date[64], cctim[64]; + char *path, date[DATELEN], cctim[DATELEN]; long pos = 0; time_t ctim = -1, cnow; int i, got = 0, ret = 0; @@ -466,7 +484,7 @@ RTMP_HashSWF(const char *url, unsigned i date[0] = '\0'; #ifdef _WIN32 -#ifdef _XBOX +#ifdef XBMC4XBOX hpre.av_val = "Q:"; hpre.av_len = 2; home.av_val = "\\UserData"; @@ -554,7 +572,8 @@ RTMP_HashSWF(const char *url, unsigned i else if (!strncmp(buf, "date: ", 6)) { buf[strlen(buf) - 1] = '\0'; - strncpy(date, buf + 6, sizeof(date)); + strncpy(date, buf + 6, sizeof(date)-1); + date[DATELEN-1] = '\0'; got++; } else if (!strncmp(buf, "ctim: ", 6)) diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/librtmp.3 rtmpdump/librtmp/librtmp.3 --- rtmpdump-2.3/librtmp/librtmp.3 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/librtmp.3 2026-06-19 14:15:18.301606620 -0600 @@ -1,5 +1,5 @@ -.TH LIBRTMP 3 "2010-05-29" "RTMPDump v2.2e" -.\" Copyright 2010 Howard Chu. +.TH LIBRTMP 3 "2011-07-20" "RTMPDump v2.4" +.\" Copyright 2011 Howard Chu. .\" Copying permitted according to the GNU General Public License V2. .SH NAME librtmp \- RTMPDump Real-Time Messaging Protocol API @@ -57,14 +57,19 @@ The session handle is freed using .BR RTMP_Free (). All data is transferred using FLV format. The basic session requires -an RTMP URL. Additional options may be specified by appending -space-separated key=value pairs to the URL. The RTMP URL format -is of the form +an RTMP URL. The RTMP URL format is of the form .nf rtmp[t][e|s]://hostname[:port][/app[/playpath]] .fi Plain rtmp, as well as tunneled and encrypted sessions are supported. + +Additional options may be specified by appending space-separated +key=value pairs to the URL. Special characters in values may need +to be escaped to prevent misinterpretation by the option parser. +The escape encoding uses a backslash followed by two hexadecimal digits +representing the ASCII value of the character. E.g., spaces must +be escaped as \fB\\20\fP and backslashes must be escaped as \fB\\5c\fP. .SH OPTIONS .SS "Network Parameters" These options define how to connect to the media server. @@ -156,6 +161,9 @@ These options handle additional authenti Key for SecureToken response, used if the server requires SecureToken authentication. .TP +.BI jtv= JSON +JSON token used by legacy Justin.tv servers. Invokes NetStream.Authenticate.UsherToken +.TP .BI swfVfy= 0|1 If the value is 1 or TRUE, the SWF player is retrieved from the specified diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/librtmp.3.html rtmpdump/librtmp/librtmp.3.html --- rtmpdump-2.3/librtmp/librtmp.3.html 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/librtmp.3.html 2026-06-19 14:15:18.301606620 -0600 @@ -6,10 +6,10 @@ LIBRTMP(3)LIBRTMP(3) -RTMPDump v2.2e2010-05-29LIBRTMP(3) +RTMPDump v2.42011-07-20LIBRTMP(3)

    -
@@ -75,14 +75,19 @@ The session handle is freed using RTMP_Free().

All data is transferred using FLV format. The basic session requires -an RTMP URL. Additional options may be specified by appending -space-separated key=value pairs to the URL. The RTMP URL format -is of the form +an RTMP URL. The RTMP URL format is of the form

   rtmp[t][e|s]://hostname[:port][/app[/playpath]]
 

Plain rtmp, as well as tunneled and encrypted sessions are supported. +

+Additional options may be specified by appending space-separated +key=value pairs to the URL. Special characters in values may need +to be escaped to prevent misinterpretation by the option parser. +The escape encoding uses a backslash followed by two hexadecimal digits +representing the ASCII value of the character. E.g., spaces must +be escaped as \20 and backslashes must be escaped as \5c.

OPTIONS

    @@ -233,6 +238,12 @@ authentication.

    +jtv=JSON +
    +JSON token used by legacy Justin.tv servers. Invokes NetStream.Authenticate.UsherToken +
    +

    +

    swfVfy=0|1
    If the value is 1 or TRUE, the SWF player is retrieved from the diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/librtmp.pc.in rtmpdump/librtmp/librtmp.pc.in --- rtmpdump-2.3/librtmp/librtmp.pc.in 2010-06-30 13:58:34.000000000 -0600 +++ rtmpdump/librtmp/librtmp.pc.in 2026-06-19 14:15:18.301606620 -0600 @@ -1,6 +1,6 @@ prefix=@prefix@ exec_prefix=${prefix} -libdir=${exec_prefix}/lib +libdir=@libdir@ incdir=${prefix}/include Name: librtmp @@ -8,5 +8,6 @@ Description: RTMP implementation Version: @VERSION@ Requires: @CRYPTO_REQ@ URL: http://rtmpdump.mplayerhq.hu -Libs: -L${libdir} -lrtmp -lz +Libs: -L${libdir} -lrtmp -lz @PUBLIC_LIBS@ +Libs.private: @PRIVATE_LIBS@ Cflags: -I${incdir} diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/log.c rtmpdump/librtmp/log.c --- rtmpdump-2.3/librtmp/log.c 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/log.c 2026-06-19 14:15:18.301606620 -0600 @@ -92,6 +92,10 @@ RTMP_LogLevel RTMP_LogGetLevel() void RTMP_Log(int level, const char *format, ...) { va_list args; + + if ( level > RTMP_debuglevel ) + return; + va_start(args, format); cb(level, format, args); va_end(args); diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/log.h rtmpdump/librtmp/log.h --- rtmpdump-2.3/librtmp/log.h 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/log.h 2026-06-19 14:15:18.301606620 -0600 @@ -48,9 +48,15 @@ extern RTMP_LogLevel RTMP_debuglevel; typedef void (RTMP_LogCallback)(int level, const char *fmt, va_list); void RTMP_LogSetCallback(RTMP_LogCallback *cb); void RTMP_LogSetOutput(FILE *file); +#ifdef __GNUC__ +void RTMP_LogPrintf(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +void RTMP_LogStatus(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +void RTMP_Log(int level, const char *format, ...) __attribute__ ((__format__ (__printf__, 2, 3))); +#else void RTMP_LogPrintf(const char *format, ...); void RTMP_LogStatus(const char *format, ...); void RTMP_Log(int level, const char *format, ...); +#endif void RTMP_LogHex(int level, const uint8_t *data, unsigned long len); void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len); void RTMP_LogSetLevel(RTMP_LogLevel lvl); diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/Makefile rtmpdump/librtmp/Makefile --- rtmpdump-2.3/librtmp/Makefile 2010-06-30 14:01:28.000000000 -0600 +++ rtmpdump/librtmp/Makefile 2026-06-19 14:15:18.301606620 -0600 @@ -1,7 +1,16 @@ -VERSION=v2.3 +VERSION=v2.6 prefix=/usr/local +incdir=$(prefix)/include/librtmp +bindir=$(prefix)/bin +libdir=$(prefix)/lib +mandir=$(prefix)/man +BINDIR=$(DESTDIR)$(bindir) +INCDIR=$(DESTDIR)$(incdir) +LIBDIR=$(DESTDIR)$(libdir) +MANDIR=$(DESTDIR)$(mandir) + CC=$(CROSS_COMPILE)gcc LD=$(CROSS_COMPILE)ld AR=$(CROSS_COMPILE)ar @@ -13,26 +22,52 @@ DEF_POLARSSL=-DUSE_POLARSSL DEF_OPENSSL=-DUSE_OPENSSL DEF_GNUTLS=-DUSE_GNUTLS DEF_=-DNO_CRYPTO -REQ_GNUTLS=gnutls +REQ_GNUTLS=gnutls,hogweed,nettle REQ_OPENSSL=libssl,libcrypto +PUB_GNUTLS=-lgmp LIBZ=-lz LIBS_posix= +LIBS_darwin= LIBS_mingw=-lws2_32 -lwinmm -lgdi32 -LIB_GNUTLS=-lgnutls -lgcrypt $(LIBZ) +LIB_GNUTLS=-lgnutls -lhogweed -lnettle -lgmp $(LIBZ) LIB_OPENSSL=-lssl -lcrypto $(LIBZ) LIB_POLARSSL=-lpolarssl $(LIBZ) -CRYPTO_LIB=$(LIB_$(CRYPTO)) $(LIBS_$(SYS)) +PRIVATE_LIBS=$(LIBS_$(SYS)) +CRYPTO_LIB=$(LIB_$(CRYPTO)) $(PRIVATE_LIBS) CRYPTO_REQ=$(REQ_$(CRYPTO)) CRYPTO_DEF=$(DEF_$(CRYPTO)) +PUBLIC_LIBS=$(PUB_$(CRYPTO)) -SO_posix=so.0 -SO_mingw=dll +SO_VERSION=1 +SOX_posix=so +SOX_darwin=dylib +SOX_mingw=dll +SOX=$(SOX_$(SYS)) +SO_posix=.$(SOX).$(SO_VERSION) +SO_darwin=.$(SO_VERSION).$(SOX) +SO_mingw=-$(SO_VERSION).$(SOX) SO_EXT=$(SO_$(SYS)) +SODIR_posix=$(LIBDIR) +SODIR_darwin=$(LIBDIR) +SODIR_mingw=$(BINDIR) +SODIR=$(SODIR_$(SYS)) + +SO_LDFLAGS_posix=-shared -Wl,-soname,$@ +SO_LDFLAGS_darwin=-dynamiclib -twolevel_namespace -undefined dynamic_lookup \ + -fno-common -headerpad_max_install_names -install_name $(libdir)/$@ +SO_LDFLAGS_mingw=-shared -Wl,--out-implib,librtmp.dll.a +SO_LDFLAGS=$(SO_LDFLAGS_$(SYS)) + +INSTALL_IMPLIB_posix= +INSTALL_IMPLIB_darwin= +INSTALL_IMPLIB_mingw=cp librtmp.dll.a $(LIBDIR) +INSTALL_IMPLIB=$(INSTALL_IMPLIB_$(SYS)) + SHARED=yes SODEF_yes=-fPIC -SOLIB_yes=librtmp.$(SO_EXT) -SOINST_yes=install_$(SO_EXT) +SOLIB_yes=librtmp$(SO_EXT) +SOINST_yes=install_so SO_DEF=$(SODEF_$(SHARED)) SO_LIB=$(SOLIB_$(SHARED)) SO_INST=$(SOINST_$(SHARED)) @@ -40,29 +75,22 @@ SO_INST=$(SOINST_$(SHARED)) DEF=-DRTMPDUMP_VERSION=\"$(VERSION)\" $(CRYPTO_DEF) $(XDEF) OPT=-O2 CFLAGS=-Wall $(XCFLAGS) $(INC) $(DEF) $(OPT) $(SO_DEF) +LDFLAGS=$(XLDFLAGS) -incdir=$(prefix)/include/librtmp -bindir=$(prefix)/bin -libdir=$(prefix)/lib -mandir=$(prefix)/man -BINDIR=$(DESTDIR)$(bindir) -INCDIR=$(DESTDIR)$(incdir) -LIBDIR=$(DESTDIR)$(libdir) -MANDIR=$(DESTDIR)$(mandir) OBJS=rtmp.o log.o amf.o hashswf.o parseurl.o all: librtmp.a $(SO_LIB) clean: - rm -f *.o *.a *.so *.$(SO_EXT) + rm -f *.o *.a *.$(SOX) *$(SO_EXT) librtmp.pc librtmp.a: $(OBJS) $(AR) rs $@ $? -librtmp.$(SO_EXT): $(OBJS) - $(CC) -shared -Wl,-soname,$@ $(LDFLAGS) -o $@ $^ $> $(CRYPTO_LIB) - ln -sf $@ librtmp.so +librtmp$(SO_EXT): $(OBJS) + $(CC) $(SO_LDFLAGS) $(LDFLAGS) -o $@ $^ $> $(CRYPTO_LIB) + ln -sf $@ librtmp.$(SOX) log.o: log.c log.h Makefile rtmp.o: rtmp.c rtmp.h rtmp_sys.h handshake.h dh.h log.h amf.h Makefile @@ -71,8 +99,11 @@ hashswf.o: hashswf.c http.h rtmp.h rtmp_ parseurl.o: parseurl.c rtmp.h rtmp_sys.h log.h Makefile librtmp.pc: librtmp.pc.in Makefile - sed -e "s;@prefix@;$(prefix);" -e "s;@VERSION@;$(VERSION);" \ - -e "s;@CRYPTO_REQ@;$(CRYPTO_REQ);" librtmp.pc.in > $@ + sed -e "s;@prefix@;$(prefix);" -e "s;@libdir@;$(libdir);" \ + -e "s;@VERSION@;$(VERSION);" \ + -e "s;@CRYPTO_REQ@;$(CRYPTO_REQ);" \ + -e "s;@PUBLIC_LIBS@;$(PUBLIC_LIBS);" \ + -e "s;@PRIVATE_LIBS@;$(PRIVATE_LIBS);" librtmp.pc.in > $@ install: install_base $(SO_INST) @@ -83,9 +114,9 @@ install_base: librtmp.a librtmp.pc cp librtmp.pc $(LIBDIR)/pkgconfig cp librtmp.3 $(MANDIR)/man3 -install_so.0: librtmp.so.0 - cp librtmp.so.0 $(LIBDIR) - cd $(LIBDIR); ln -sf librtmp.so.0 librtmp.so +install_so: librtmp$(SO_EXT) + -mkdir -p $(SODIR) + cp librtmp$(SO_EXT) $(SODIR) + $(INSTALL_IMPLIB) + cd $(SODIR); ln -sf librtmp$(SO_EXT) librtmp.$(SOX) -install_dll: librtmp.dll - cp librtmp.dll $(BINDIR) diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/parseurl.c rtmpdump/librtmp/parseurl.c --- rtmpdump-2.3/librtmp/parseurl.c 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/parseurl.c 2026-06-19 14:15:18.301606620 -0600 @@ -137,12 +137,14 @@ parsehost: * application = app[/appinstance] */ - char *slash2, *slash3 = NULL; + char *slash2, *slash3 = NULL, *slash4 = NULL; int applen, appnamelen; slash2 = strchr(p, '/'); if(slash2) slash3 = strchr(slash2+1, '/'); + if(slash3) + slash4 = strchr(slash3+1, '/'); applen = end-p; /* ondemand, pass all parameters as app */ appnamelen = applen; /* ondemand length */ @@ -156,7 +158,9 @@ parsehost: appnamelen = 8; } else { /* app!=ondemand, so app is app[/appinstance] */ - if(slash3) + if(slash4) + appnamelen = slash4-p; + else if(slash3) appnamelen = slash3-p; else if(slash2) appnamelen = slash2-p; diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/rtmp.c rtmpdump/librtmp/rtmp.c --- rtmpdump-2.3/librtmp/rtmp.c 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/rtmp.c 2026-06-19 14:15:18.303594982 -0600 @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "rtmp_sys.h" #include "log.h" @@ -34,11 +36,33 @@ #ifdef CRYPTO #ifdef USE_POLARSSL #include +#include +#include +#define MD5_DIGEST_LENGTH 16 + +static const char *my_dhm_P = + "E4004C1F94182000103D883A448B3F80" \ + "2CE4B44A83301270002C20D0321CFD00" \ + "11CCEF784C26A400F43DFB901BCA7538" \ + "F2C6B176001CF5A0FD16D2C48B1D0C1C" \ + "F6AC8E1DA6BCC3B4E1F96B0564965300" \ + "FFA1D0B601EB2800F489AA512C4B248C" \ + "01F76949A60BB7F00A40B1EAB64BDD48" \ + "E8A700D60B7F1200FA8E77B0A979DABF"; + +static const char *my_dhm_G = "4"; + #elif defined(USE_GNUTLS) #include +#define MD5_DIGEST_LENGTH 16 +#include +#include #else /* USE_OPENSSL */ #include #include +#include +#include +#include #endif TLS_CTX RTMP_TLS_ctx; #endif @@ -96,6 +120,7 @@ static int SendDeleteStream(RTMP *r, dou 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); @@ -118,6 +143,8 @@ static void DecodeTEA(AVal *key, AVal *t static int HTTP_Post(RTMP *r, RTMPTCmd cmd, const char *buf, int len); static int HTTP_read(RTMP *r, int fill); +static void CloseInternal(RTMP *r, int reconnect); + #ifndef _WIN32 static int clk_tck; #endif @@ -160,9 +187,12 @@ RTMPPacket_Reset(RTMPPacket *p) } int -RTMPPacket_Alloc(RTMPPacket *p, int nSize) +RTMPPacket_Alloc(RTMPPacket *p, uint32_t nSize) { - char *ptr = calloc(1, nSize + RTMP_MAX_HEADER_SIZE); + char *ptr; + if (nSize > SIZE_MAX - RTMP_MAX_HEADER_SIZE) + return FALSE; + ptr = calloc(1, nSize + RTMP_MAX_HEADER_SIZE); if (!ptr) return FALSE; p->m_body = ptr + RTMP_MAX_HEADER_SIZE; @@ -184,7 +214,7 @@ void RTMPPacket_Dump(RTMPPacket *p) { RTMP_Log(RTMP_LOGDEBUG, - "RTMP PACKET: packet type: 0x%02x. channel: 0x%02x. info 1: %d info 2: %d. Body size: %lu. body: 0x%02x", + "RTMP PACKET: packet type: 0x%02x. channel: 0x%02x. info 1: %d info 2: %d. Body size: %u. body: 0x%02x", p->m_packetType, p->m_nChannel, p->m_nTimeStamp, p->m_nInfoField2, p->m_nBodySize, p->m_body ? (unsigned char)p->m_body[0] : 0); } @@ -226,6 +256,64 @@ RTMP_TLS_Init() #endif } +void * +RTMP_TLS_AllocServerContext(const char* cert, const char* key) +{ + void *ctx = NULL; +#ifdef CRYPTO + if (!RTMP_TLS_ctx) + RTMP_TLS_Init(); +#ifdef USE_POLARSSL + tls_server_ctx *tc = ctx = calloc(1, sizeof(struct tls_server_ctx)); + tc->dhm_P = my_dhm_P; + tc->dhm_G = my_dhm_G; + tc->hs = &RTMP_TLS_ctx->hs; + if (x509parse_crtfile(&tc->cert, cert)) { + free(tc); + return NULL; + } + if (x509parse_keyfile(&tc->key, key, NULL)) { + x509_free(&tc->cert); + free(tc); + return NULL; + } +#elif defined(USE_GNUTLS) && !defined(NO_SSL) + gnutls_certificate_allocate_credentials((gnutls_certificate_credentials*) &ctx); + if (gnutls_certificate_set_x509_key_file(ctx, cert, key, GNUTLS_X509_FMT_PEM) != 0) { + gnutls_certificate_free_credentials(ctx); + return NULL; + } +#elif !defined(NO_SSL) /* USE_OPENSSL */ + ctx = SSL_CTX_new(SSLv23_server_method()); + if (!SSL_CTX_use_certificate_chain_file(ctx, cert)) { + SSL_CTX_free(ctx); + return NULL; + } + if (!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) { + SSL_CTX_free(ctx); + return NULL; + } +#endif +#endif + return ctx; +} + +void +RTMP_TLS_FreeServerContext(void *ctx) +{ +#ifdef CRYPTO +#ifdef USE_POLARSSL + x509_free(&((tls_server_ctx*)ctx)->cert); + rsa_free(&((tls_server_ctx*)ctx)->key); + free(ctx); +#elif defined(USE_GNUTLS) && !defined(NO_SSL) + gnutls_certificate_free_credentials(ctx); +#elif !defined(NO_SSL) /* USE_OPENSSL */ + SSL_CTX_free(ctx); +#endif +#endif +} + RTMP * RTMP_Alloc() { @@ -319,6 +407,31 @@ static const char DEFAULT_FLASH_VER[] = const AVal RTMP_DefaultFlashVer = { (char *)DEFAULT_FLASH_VER, sizeof(DEFAULT_FLASH_VER) - 1 }; +static void +SocksSetup(RTMP *r, AVal *sockshost) +{ + if (sockshost->av_len) + { + const char *socksport = strchr(sockshost->av_val, ':'); + char *hostname = strdup(sockshost->av_val); + + if (socksport) + hostname[socksport - sockshost->av_val] = '\0'; + r->Link.sockshost.av_val = hostname; + r->Link.sockshost.av_len = strlen(hostname); + + r->Link.socksport = socksport ? atoi(socksport + 1) : 1080; + RTMP_Log(RTMP_LOGDEBUG, "Connecting via SOCKS proxy: %s:%d", r->Link.sockshost.av_val, + r->Link.socksport); + } + else + { + r->Link.sockshost.av_val = NULL; + r->Link.sockshost.av_len = 0; + r->Link.socksport = 0; + } +} + void RTMP_SetupStream(RTMP *r, int protocol, @@ -335,6 +448,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 +469,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) @@ -363,7 +479,7 @@ RTMP_SetupStream(RTMP *r, RTMP_Log(RTMP_LOGDEBUG, "StopTime : %d msec", dStop); RTMP_Log(RTMP_LOGDEBUG, "live : %s", bLiveStream ? "yes" : "no"); - RTMP_Log(RTMP_LOGDEBUG, "timeout : %d sec", timeout); + RTMP_Log(RTMP_LOGDEBUG, "timeout : %ld sec", timeout); #ifdef CRYPTO if (swfSHA256Hash != NULL && swfSize > 0) @@ -372,7 +488,7 @@ RTMP_SetupStream(RTMP *r, r->Link.SWFSize = swfSize; RTMP_Log(RTMP_LOGDEBUG, "SWFSHA256:"); RTMP_LogHex(RTMP_LOGDEBUG, r->Link.SWFHash, sizeof(r->Link.SWFHash)); - RTMP_Log(RTMP_LOGDEBUG, "SWFSize : %lu", r->Link.SWFSize); + RTMP_Log(RTMP_LOGDEBUG, "SWFSize : %u", r->Link.SWFSize); } else { @@ -380,26 +496,7 @@ RTMP_SetupStream(RTMP *r, } #endif - if (sockshost->av_len) - { - const char *socksport = strchr(sockshost->av_val, ':'); - char *hostname = strdup(sockshost->av_val); - - if (socksport) - hostname[socksport - sockshost->av_val] = '\0'; - r->Link.sockshost.av_val = hostname; - r->Link.sockshost.av_len = strlen(hostname); - - r->Link.socksport = socksport ? atoi(socksport + 1) : 1080; - RTMP_Log(RTMP_LOGDEBUG, "Connecting via SOCKS proxy: %s:%d", r->Link.sockshost.av_val, - r->Link.socksport); - } - else - { - r->Link.sockshost.av_val = NULL; - r->Link.sockshost.av_len = 0; - r->Link.socksport = 0; - } + SocksSetup(r, sockshost); if (tcUrl && tcUrl->av_len) r->Link.tcUrl = *tcUrl; @@ -420,6 +517,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 +576,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, @@ -491,6 +592,10 @@ static struct urlopt { "Buffer time in milliseconds" }, { AVC("timeout"), OFF(Link.timeout), OPT_INT, 0, "Session timeout in seconds" }, + { AVC("pubUser"), OFF(Link.pubUser), OPT_STR, 0, + "Publisher username" }, + { AVC("pubPasswd"), OFF(Link.pubPasswd), OPT_STR, 0, + "Publisher password" }, { {NULL,0}, 0, 0} }; @@ -748,6 +853,8 @@ int RTMP_SetupURL(RTMP *r, char *url) (unsigned char *)r->Link.SWFHash, r->Link.swfAge); #endif + SocksSetup(r, &r->Link.sockshost); + if (r->Link.port == 0) { if (r->Link.protocol & RTMP_FEATURE_SSL) @@ -851,6 +958,23 @@ RTMP_Connect0(RTMP *r, struct sockaddr * } int +RTMP_TLS_Accept(RTMP *r, void *ctx) +{ +#if defined(CRYPTO) && !defined(NO_SSL) + TLS_server(ctx, r->m_sb.sb_ssl); + TLS_setfd(r->m_sb.sb_ssl, r->m_sb.sb_socket); + if (TLS_accept(r->m_sb.sb_ssl) < 0) + { + RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__); + return FALSE; + } + return TRUE; +#else + return FALSE; +#endif +} + +int RTMP_Connect1(RTMP *r, RTMPPacket *cp) { if (r->Link.protocol & RTMP_FEATURE_SSL) @@ -877,7 +1001,13 @@ RTMP_Connect1(RTMP *r, RTMPPacket *cp) r->m_clientID.av_val = NULL; r->m_clientID.av_len = 0; HTTP_Post(r, RTMPT_OPEN, "", 1); - HTTP_read(r, 1); + if (HTTP_read(r, 1) != 0) + { + r->m_msgCounter = 0; + RTMP_Log(RTMP_LOGDEBUG, "%s, Could not connect for handshake", __FUNCTION__); + RTMP_Close(r); + return 0; + } r->m_msgCounter = 0; } RTMP_Log(RTMP_LOGDEBUG, "%s, ... connected, handshaking", __FUNCTION__); @@ -960,7 +1090,7 @@ SocksNegotiate(RTMP *r) } else { - RTMP_Log(RTMP_LOGERROR, "%s, SOCKS returned error code %d", packet[1]); + RTMP_Log(RTMP_LOGERROR, "%s, SOCKS returned error code %d", __FUNCTION__, packet[1]); return FALSE; } } @@ -1019,6 +1149,9 @@ RTMP_ToggleStream(RTMP *r) if (!r->m_pausing) { + if (RTMP_IsTimedout(r) && r->m_read.status == RTMP_READ_EOF) + r->m_read.status = 0; + res = RTMP_SendPause(r, TRUE, r->m_pauseStamp); if (!res) return res; @@ -1051,7 +1184,7 @@ RTMP_GetNextMediaPacket(RTMP *r, RTMPPac while (!bHasMediaPacket && RTMP_IsConnected(r) && RTMP_ReadPacket(r, packet)) { - if (!RTMPPacket_IsReady(packet)) + if (!RTMPPacket_IsReady(packet) || !packet->m_nBodySize) { continue; } @@ -1074,6 +1207,7 @@ RTMP_GetNextMediaPacket(RTMP *r, RTMPPac packet->m_nTimeStamp, packet->m_hasAbsTimestamp, r->m_mediaStamp); #endif + RTMPPacket_Free(packet); continue; } r->m_pausing = 0; @@ -1083,7 +1217,8 @@ RTMP_GetNextMediaPacket(RTMP *r, RTMPPac if (bHasMediaPacket) r->m_bPlaying = TRUE; else if (r->m_sb.sb_timedout && !r->m_pausing) - r->m_pauseStamp = r->m_channelTimestamp[r->m_mediaChannel]; + r->m_pauseStamp = r->m_mediaChannel < r->m_channelsAllocatedIn ? + r->m_channelTimestamp[r->m_mediaChannel] : 0; return bHasMediaPacket; } @@ -1094,32 +1229,32 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *p int bHasMediaPacket = 0; switch (packet->m_packetType) { - case 0x01: + case RTMP_PACKET_TYPE_CHUNK_SIZE: /* chunk size */ HandleChangeChunkSize(r, packet); break; - case 0x03: + case RTMP_PACKET_TYPE_BYTES_READ_REPORT: /* bytes read report */ RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__); break; - case 0x04: + case RTMP_PACKET_TYPE_CONTROL: /* ctrl */ HandleCtrl(r, packet); break; - case 0x05: + case RTMP_PACKET_TYPE_SERVER_BW: /* server bw */ HandleServerBW(r, packet); break; - case 0x06: + case RTMP_PACKET_TYPE_CLIENT_BW: /* client bw */ HandleClientBW(r, packet); break; - case 0x08: + case RTMP_PACKET_TYPE_AUDIO: /* audio data */ /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */ HandleAudio(r, packet); @@ -1130,7 +1265,7 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *p r->m_mediaStamp = packet->m_nTimeStamp; break; - case 0x09: + case RTMP_PACKET_TYPE_VIDEO: /* video data */ /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */ HandleVideo(r, packet); @@ -1141,22 +1276,25 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *p r->m_mediaStamp = packet->m_nTimeStamp; break; - case 0x0F: /* flex stream send */ + case RTMP_PACKET_TYPE_FLEX_STREAM_SEND: + /* flex stream send */ RTMP_Log(RTMP_LOGDEBUG, - "%s, flex stream send, size %lu bytes, not supported, ignoring", + "%s, flex stream send, size %u bytes, not supported, ignoring", __FUNCTION__, packet->m_nBodySize); break; - case 0x10: /* flex shared object */ + case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT: + /* flex shared object */ RTMP_Log(RTMP_LOGDEBUG, - "%s, flex shared object, size %lu bytes, not supported, ignoring", + "%s, flex shared object, size %u bytes, not supported, ignoring", __FUNCTION__, packet->m_nBodySize); break; - case 0x11: /* flex message */ + case RTMP_PACKET_TYPE_FLEX_MESSAGE: + /* flex message */ { RTMP_Log(RTMP_LOGDEBUG, - "%s, flex message, size %lu bytes, not fully supported", + "%s, flex message, size %u bytes, not fully supported", __FUNCTION__, packet->m_nBodySize); /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */ @@ -1176,22 +1314,22 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *p bHasMediaPacket = 2; break; } - case 0x12: + case RTMP_PACKET_TYPE_INFO: /* metadata (notify) */ - RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %lu bytes", __FUNCTION__, + RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %u bytes", __FUNCTION__, packet->m_nBodySize); if (HandleMetadata(r, packet->m_body, packet->m_nBodySize)) bHasMediaPacket = 1; break; - case 0x13: + case RTMP_PACKET_TYPE_SHARED_OBJECT: RTMP_Log(RTMP_LOGDEBUG, "%s, shared object, not supported, ignoring", __FUNCTION__); break; - case 0x14: + case RTMP_PACKET_TYPE_INVOKE: /* invoke */ - RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__, + RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__, packet->m_nBodySize); /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */ @@ -1199,7 +1337,7 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *p bHasMediaPacket = 2; break; - case 0x16: + case RTMP_PACKET_TYPE_FLASH_VIDEO: { /* go through FLV packets and handle metadata packets */ unsigned int pos = 0; @@ -1268,9 +1406,11 @@ ReadN(RTMP *r, char *buffer, int n) int nBytes = 0, nRead; if (r->Link.protocol & RTMP_FEATURE_HTTP) { + int refill = 0; while (!r->m_resplen) { - if (r->m_sb.sb_size < 144) + int ret; + if (r->m_sb.sb_size < 13 || refill) { if (!r->m_unackd) HTTP_Post(r, RTMPT_IDLE, "", 1); @@ -1281,7 +1421,20 @@ ReadN(RTMP *r, char *buffer, int n) return 0; } } - HTTP_read(r, 0); + if ((ret = HTTP_read(r, 0)) == -1) + { + RTMP_Log(RTMP_LOGDEBUG, "%s, No valid HTTP response found", __FUNCTION__); + RTMP_Close(r); + return 0; + } + else if (ret == -2) + { + refill = 1; + } + else + { + refill = 0; + } } if (r->m_resplen && !r->m_sb.sb_size) RTMPSockBuf_Fill(&r->m_sb); @@ -1312,8 +1465,9 @@ ReadN(RTMP *r, char *buffer, int n) nBytes = nRead; r->m_nBytesIn += nRead; if (r->m_bSendCounter - && r->m_nBytesIn > r->m_nBytesInSent + r->m_nClientBW / 2) - SendBytesReceived(r); + && r->m_nBytesIn > ( r->m_nBytesInSent + r->m_nClientBW / 10)) + if (!SendBytesReceived(r)) + return FALSE; } /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */ #ifdef _DEBUG @@ -1434,7 +1588,7 @@ SendConnectPacket(RTMP *r, RTMPPacket *c packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_LARGE; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1545,7 +1699,7 @@ SendBGHasStream(RTMP *r, double dId, AVa packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1577,7 +1731,7 @@ RTMP_SendCreateStream(RTMP *r) packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1603,7 +1757,7 @@ SendFCSubscribe(RTMP *r, AVal *subscribe char *enc; packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1624,6 +1778,39 @@ SendFCSubscribe(RTMP *r, AVal *subscribe return RTMP_SendPacket(r, &packet, TRUE); } +/* Justin.tv specific authentication */ +static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken"); + +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 @@ -1635,7 +1822,7 @@ SendReleaseStream(RTMP *r) packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1665,7 +1852,7 @@ SendFCPublish(RTMP *r) packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1695,7 +1882,7 @@ SendFCUnpublish(RTMP *r) packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1727,7 +1914,7 @@ SendPublish(RTMP *r) packet.m_nChannel = 0x04; /* source channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_LARGE; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = r->m_stream_id; packet.m_hasAbsTimestamp = 0; @@ -1762,7 +1949,7 @@ SendDeleteStream(RTMP *r, double dStream packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1791,7 +1978,7 @@ RTMP_SendPause(RTMP *r, int DoPause, int packet.m_nChannel = 0x08; /* video channel */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; /* invoke */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1813,7 +2000,8 @@ RTMP_SendPause(RTMP *r, int DoPause, int int RTMP_Pause(RTMP *r, int DoPause) { if (DoPause) - r->m_pauseStamp = r->m_channelTimestamp[r->m_mediaChannel]; + r->m_pauseStamp = r->m_mediaChannel < r->m_channelsAllocatedIn ? + r->m_channelTimestamp[r->m_mediaChannel] : 0; return RTMP_SendPause(r, DoPause, r->m_pauseStamp); } @@ -1828,7 +2016,7 @@ RTMP_SendSeek(RTMP *r, int iTime) packet.m_nChannel = 0x08; /* video channel */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; /* invoke */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1856,7 +2044,7 @@ RTMP_SendServerBW(RTMP *r) packet.m_nChannel = 0x02; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_LARGE; - packet.m_packetType = 0x05; /* Server BW */ + packet.m_packetType = RTMP_PACKET_TYPE_SERVER_BW; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1876,7 +2064,7 @@ RTMP_SendClientBW(RTMP *r) packet.m_nChannel = 0x02; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_LARGE; - packet.m_packetType = 0x06; /* Client BW */ + packet.m_packetType = RTMP_PACKET_TYPE_CLIENT_BW; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1897,7 +2085,7 @@ SendBytesReceived(RTMP *r) packet.m_nChannel = 0x02; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x03; /* bytes in */ + packet.m_packetType = RTMP_PACKET_TYPE_BYTES_READ_REPORT; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1923,7 +2111,7 @@ SendCheckBW(RTMP *r) packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_LARGE; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; /* RTMP_GetTime(); */ packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1951,7 +2139,7 @@ SendCheckBWResult(RTMP *r, double txn) packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0x16 * r->m_nBWCheckCounter; /* temp inc value. till we figure it out. */ packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -1980,7 +2168,7 @@ SendPong(RTMP *r, double txn) packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0x16 * r->m_nBWCheckCounter; /* temp inc value. till we figure it out. */ packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -2007,7 +2195,7 @@ SendPlay(RTMP *r) packet.m_nChannel = 0x08; /* we make 8 our stream channel */ packet.m_headerType = RTMP_PACKET_SIZE_LARGE; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = r->m_stream_id; /*0x01000000; */ packet.m_hasAbsTimestamp = 0; @@ -2075,7 +2263,7 @@ SendPlaylist(RTMP *r) packet.m_nChannel = 0x08; /* we make 8 our stream channel */ packet.m_headerType = RTMP_PACKET_SIZE_LARGE; - packet.m_packetType = 0x14; /* INVOKE */ + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = r->m_stream_id; /*0x01000000; */ packet.m_hasAbsTimestamp = 0; @@ -2113,7 +2301,7 @@ SendSecureTokenResponse(RTMP *r, AVal *r packet.m_nChannel = 0x03; /* control channel (invoke) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x14; + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -2160,7 +2348,7 @@ RTMP_SendCtrl(RTMP *r, short nType, unsi packet.m_nChannel = 0x02; /* control channel (ping) */ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; - packet.m_packetType = 0x04; /* ctrl */ + packet.m_packetType = RTMP_PACKET_TYPE_CONTROL; packet.m_nTimeStamp = 0; /* RTMP_GetTime(); */ packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -2246,6 +2434,449 @@ AV_clear(RTMP_METHOD *vals, int num) free(vals); } + +#ifdef CRYPTO +static int +b64enc(const unsigned char *input, int length, char *output, int maxsize) +{ +#ifdef USE_POLARSSL + size_t buf_size = maxsize; + if(base64_encode((unsigned char *) output, &buf_size, input, length) == 0) + { + output[buf_size] = '\0'; + return 1; + } + else + { + RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__); + return 0; + } +#elif defined(USE_GNUTLS) + if (BASE64_ENCODE_RAW_LENGTH(length) <= maxsize) + base64_encode_raw((uint8_t*) output, length, input); + else + { + RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__); + return 0; + } +#else /* USE_OPENSSL */ + BIO *bmem, *b64; + BUF_MEM *bptr; + + b64 = BIO_new(BIO_f_base64()); + bmem = BIO_new(BIO_s_mem()); + b64 = BIO_push(b64, bmem); + BIO_write(b64, input, length); + if (BIO_flush(b64) == 1) + { + BIO_get_mem_ptr(b64, &bptr); + memcpy(output, bptr->data, bptr->length-1); + output[bptr->length-1] = '\0'; + } + else + { + RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__); + return 0; + } + BIO_free_all(b64); +#endif + return 1; +} + +#ifdef USE_POLARSSL +#define MD5_CTX md5_context +#define MD5_Init(ctx) md5_starts(ctx) +#define MD5_Update(ctx,data,len) md5_update(ctx,(unsigned char *)data,len) +#define MD5_Final(dig,ctx) md5_finish(ctx,dig) +#elif defined(USE_GNUTLS) +typedef struct md5_ctx MD5_CTX; +#define MD5_Init(ctx) md5_init(ctx) +#define MD5_Update(ctx,data,len) md5_update(ctx,len,data) +#define MD5_Final(dig,ctx) md5_digest(ctx,MD5_DIGEST_LENGTH,dig) +#else +#endif + +static const AVal av_authmod_adobe = AVC("authmod=adobe"); +static const AVal av_authmod_llnw = AVC("authmod=llnw"); + +static void hexenc(unsigned char *inbuf, int len, char *dst) +{ + char *ptr = dst; + while(len--) { + sprintf(ptr, "%02x", *inbuf++); + ptr += 2; + } + *ptr = '\0'; +} + +static char * +AValChr(AVal *av, char c) +{ + int i; + for (i = 0; i < av->av_len; i++) + if (av->av_val[i] == c) + return &av->av_val[i]; + return NULL; +} + +static int +PublisherAuth(RTMP *r, AVal *description) +{ + char *token_in = NULL; + char *ptr; + unsigned char md5sum_val[MD5_DIGEST_LENGTH+1]; + MD5_CTX md5ctx; + int challenge2_data; +#define RESPONSE_LEN 32 +#define CHALLENGE2_LEN 16 +#define SALTED2_LEN (32+8+8+8) +#define B64DIGEST_LEN 24 /* 16 byte digest => 22 b64 chars + 2 chars padding */ +#define B64INT_LEN 8 /* 4 byte int => 6 b64 chars + 2 chars padding */ +#define HEXHASH_LEN (2*MD5_DIGEST_LENGTH) + char response[RESPONSE_LEN]; + char challenge2[CHALLENGE2_LEN]; + char salted2[SALTED2_LEN]; + AVal pubToken; + + if (strstr(description->av_val, av_authmod_adobe.av_val) != NULL) + { + if(strstr(description->av_val, "code=403 need auth") != NULL) + { + if (strstr(r->Link.app.av_val, av_authmod_adobe.av_val) != NULL) { + RTMP_Log(RTMP_LOGERROR, "%s, wrong pubUser & pubPasswd for publisher auth", __FUNCTION__); + return 0; + } else if(r->Link.pubUser.av_len && r->Link.pubPasswd.av_len) { + pubToken.av_val = malloc(r->Link.pubUser.av_len + av_authmod_adobe.av_len + 8); + pubToken.av_len = sprintf(pubToken.av_val, "?%s&user=%s", + av_authmod_adobe.av_val, + r->Link.pubUser.av_val); + RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken1: %s", __FUNCTION__, pubToken.av_val); + } else { + RTMP_Log(RTMP_LOGERROR, "%s, need to set pubUser & pubPasswd for publisher auth", __FUNCTION__); + return 0; + } + } + else if((token_in = strstr(description->av_val, "?reason=needauth")) != NULL) + { + char *par, *val = NULL, *orig_ptr; + AVal user, salt, opaque, challenge, *aptr = NULL; + opaque.av_len = 0; + challenge.av_len = 0; + + ptr = orig_ptr = strdup(token_in); + while (ptr) + { + par = ptr; + ptr = strchr(par, '&'); + if(ptr) + *ptr++ = '\0'; + + val = strchr(par, '='); + if(val) + *val++ = '\0'; + + if (aptr) { + aptr->av_len = par - aptr->av_val - 1; + aptr = NULL; + } + if (strcmp(par, "user") == 0){ + user.av_val = val; + aptr = &user; + } else if (strcmp(par, "salt") == 0){ + salt.av_val = val; + aptr = &salt; + } else if (strcmp(par, "opaque") == 0){ + opaque.av_val = val; + aptr = &opaque; + } else if (strcmp(par, "challenge") == 0){ + challenge.av_val = val; + aptr = &challenge; + } + + RTMP_Log(RTMP_LOGDEBUG, "%s, par:\"%s\" = val:\"%s\"", __FUNCTION__, par, val); + } + if (aptr) + aptr->av_len = strlen(aptr->av_val); + + /* hash1 = base64enc(md5(user + _aodbeAuthSalt + password)) */ + MD5_Init(&md5ctx); + MD5_Update(&md5ctx, user.av_val, user.av_len); + MD5_Update(&md5ctx, salt.av_val, salt.av_len); + MD5_Update(&md5ctx, r->Link.pubPasswd.av_val, r->Link.pubPasswd.av_len); + MD5_Final(md5sum_val, &md5ctx); + RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s%s%s) =>", __FUNCTION__, + user.av_val, salt.av_val, r->Link.pubPasswd.av_val); + RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH); + + b64enc(md5sum_val, MD5_DIGEST_LENGTH, salted2, SALTED2_LEN); + RTMP_Log(RTMP_LOGDEBUG, "%s, b64(md5_1) = %s", __FUNCTION__, salted2); + + challenge2_data = rand(); + + b64enc((unsigned char *) &challenge2_data, sizeof(int), challenge2, CHALLENGE2_LEN); + RTMP_Log(RTMP_LOGDEBUG, "%s, b64(%d) = %s", __FUNCTION__, challenge2_data, challenge2); + + MD5_Init(&md5ctx); + MD5_Update(&md5ctx, salted2, B64DIGEST_LEN); + /* response = base64enc(md5(hash1 + opaque + challenge2)) */ + if (opaque.av_len) + MD5_Update(&md5ctx, opaque.av_val, opaque.av_len); + else if (challenge.av_len) + MD5_Update(&md5ctx, challenge.av_val, challenge.av_len); + MD5_Update(&md5ctx, challenge2, B64INT_LEN); + MD5_Final(md5sum_val, &md5ctx); + + RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s%s%s) =>", __FUNCTION__, + salted2, opaque.av_len ? opaque.av_val : "", challenge2); + RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH); + + b64enc(md5sum_val, MD5_DIGEST_LENGTH, response, RESPONSE_LEN); + RTMP_Log(RTMP_LOGDEBUG, "%s, b64(md5_2) = %s", __FUNCTION__, response); + + /* have all hashes, create auth token for the end of app */ + pubToken.av_val = malloc(32 + B64INT_LEN + B64DIGEST_LEN + opaque.av_len); + pubToken.av_len = sprintf(pubToken.av_val, + "&challenge=%s&response=%s&opaque=%s", + challenge2, + response, + opaque.av_len ? opaque.av_val : ""); + RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken2: %s", __FUNCTION__, pubToken.av_val); + free(orig_ptr); + } + else if(strstr(description->av_val, "?reason=authfailed") != NULL) + { + RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: wrong password", __FUNCTION__); + return 0; + } + else if(strstr(description->av_val, "?reason=nosuchuser") != NULL) + { + RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: no such user", __FUNCTION__); + return 0; + } + else + { + RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: unknown auth mode: %s", + __FUNCTION__, description->av_val); + return 0; + } + + ptr = malloc(r->Link.app.av_len + pubToken.av_len); + strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); + strncpy(ptr + r->Link.app.av_len, pubToken.av_val, pubToken.av_len); + r->Link.app.av_len += pubToken.av_len; + if(r->Link.lFlags & RTMP_LF_FAPU) + free(r->Link.app.av_val); + r->Link.app.av_val = ptr; + + ptr = malloc(r->Link.tcUrl.av_len + pubToken.av_len); + strncpy(ptr, r->Link.tcUrl.av_val, r->Link.tcUrl.av_len); + strncpy(ptr + r->Link.tcUrl.av_len, pubToken.av_val, pubToken.av_len); + r->Link.tcUrl.av_len += pubToken.av_len; + if(r->Link.lFlags & RTMP_LF_FTCU) + free(r->Link.tcUrl.av_val); + r->Link.tcUrl.av_val = ptr; + + free(pubToken.av_val); + r->Link.lFlags |= RTMP_LF_FTCU | RTMP_LF_FAPU; + + RTMP_Log(RTMP_LOGDEBUG, "%s, new app: %.*s tcUrl: %.*s playpath: %s", __FUNCTION__, + r->Link.app.av_len, r->Link.app.av_val, + r->Link.tcUrl.av_len, r->Link.tcUrl.av_val, + r->Link.playpath.av_val); + } + else if (strstr(description->av_val, av_authmod_llnw.av_val) != NULL) + { + if(strstr(description->av_val, "code=403 need auth") != NULL) + { + /* This part seems to be the same for llnw and adobe */ + + if (strstr(r->Link.app.av_val, av_authmod_llnw.av_val) != NULL) { + RTMP_Log(RTMP_LOGERROR, "%s, wrong pubUser & pubPasswd for publisher auth", __FUNCTION__); + return 0; + } else if(r->Link.pubUser.av_len && r->Link.pubPasswd.av_len) { + pubToken.av_val = malloc(r->Link.pubUser.av_len + av_authmod_llnw.av_len + 8); + pubToken.av_len = sprintf(pubToken.av_val, "?%s&user=%s", + av_authmod_llnw.av_val, + r->Link.pubUser.av_val); + RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken1: %s", __FUNCTION__, pubToken.av_val); + } else { + RTMP_Log(RTMP_LOGERROR, "%s, need to set pubUser & pubPasswd for publisher auth", __FUNCTION__); + return 0; + } + } + else if((token_in = strstr(description->av_val, "?reason=needauth")) != NULL) + { + char *orig_ptr; + char *par, *val = NULL; + char hash1[HEXHASH_LEN+1], hash2[HEXHASH_LEN+1], hash3[HEXHASH_LEN+1]; + AVal user, nonce, *aptr = NULL; + AVal apptmp; + + /* llnw auth method + * Seems to be closely based on HTTP Digest Auth: + * http://tools.ietf.org/html/rfc2617 + * http://en.wikipedia.org/wiki/Digest_access_authentication + */ + + const char authmod[] = "llnw"; + const char realm[] = "live"; + const char method[] = "publish"; + const char qop[] = "auth"; + /* nc = 1..connection count (or rather, number of times cnonce has been reused) */ + int nc = 1; + /* nchex = hexenc(nc) (8 hex digits according to RFC 2617) */ + char nchex[9]; + /* cnonce = hexenc(4 random bytes) (initialized on first connection) */ + char cnonce[9]; + + ptr = orig_ptr = strdup(token_in); + /* Extract parameters (we need user and nonce) */ + while (ptr) + { + par = ptr; + ptr = strchr(par, '&'); + if(ptr) + *ptr++ = '\0'; + + val = strchr(par, '='); + if(val) + *val++ = '\0'; + + if (aptr) { + aptr->av_len = par - aptr->av_val - 1; + aptr = NULL; + } + if (strcmp(par, "user") == 0){ + user.av_val = val; + aptr = &user; + } else if (strcmp(par, "nonce") == 0){ + nonce.av_val = val; + aptr = &nonce; + } + + RTMP_Log(RTMP_LOGDEBUG, "%s, par:\"%s\" = val:\"%s\"", __FUNCTION__, par, val); + } + if (aptr) + aptr->av_len = strlen(aptr->av_val); + + /* FIXME: handle case where user==NULL or nonce==NULL */ + + sprintf(nchex, "%08x", nc); + sprintf(cnonce, "%08x", rand()); + + /* hash1 = hexenc(md5(user + ":" + realm + ":" + password)) */ + MD5_Init(&md5ctx); + MD5_Update(&md5ctx, user.av_val, user.av_len); + MD5_Update(&md5ctx, ":", 1); + MD5_Update(&md5ctx, realm, sizeof(realm)-1); + MD5_Update(&md5ctx, ":", 1); + MD5_Update(&md5ctx, r->Link.pubPasswd.av_val, r->Link.pubPasswd.av_len); + MD5_Final(md5sum_val, &md5ctx); + RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s:%s:%s) =>", __FUNCTION__, + user.av_val, realm, r->Link.pubPasswd.av_val); + RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH); + hexenc(md5sum_val, MD5_DIGEST_LENGTH, hash1); + + /* hash2 = hexenc(md5(method + ":/" + app + "/" + appInstance)) */ + /* Extract appname + appinstance without query parameters */ + apptmp = r->Link.app; + ptr = AValChr(&apptmp, '?'); + if (ptr) + apptmp.av_len = ptr - apptmp.av_val; + + MD5_Init(&md5ctx); + MD5_Update(&md5ctx, method, sizeof(method)-1); + MD5_Update(&md5ctx, ":/", 2); + MD5_Update(&md5ctx, apptmp.av_val, apptmp.av_len); + if (!AValChr(&apptmp, '/')) + MD5_Update(&md5ctx, "/_definst_", sizeof("/_definst_") - 1); + MD5_Final(md5sum_val, &md5ctx); + RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s:/%.*s) =>", __FUNCTION__, + method, apptmp.av_len, apptmp.av_val); + RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH); + hexenc(md5sum_val, MD5_DIGEST_LENGTH, hash2); + + /* hash3 = hexenc(md5(hash1 + ":" + nonce + ":" + nchex + ":" + cnonce + ":" + qop + ":" + hash2)) */ + MD5_Init(&md5ctx); + MD5_Update(&md5ctx, hash1, HEXHASH_LEN); + MD5_Update(&md5ctx, ":", 1); + MD5_Update(&md5ctx, nonce.av_val, nonce.av_len); + MD5_Update(&md5ctx, ":", 1); + MD5_Update(&md5ctx, nchex, sizeof(nchex)-1); + MD5_Update(&md5ctx, ":", 1); + MD5_Update(&md5ctx, cnonce, sizeof(cnonce)-1); + MD5_Update(&md5ctx, ":", 1); + MD5_Update(&md5ctx, qop, sizeof(qop)-1); + MD5_Update(&md5ctx, ":", 1); + MD5_Update(&md5ctx, hash2, HEXHASH_LEN); + MD5_Final(md5sum_val, &md5ctx); + RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s:%s:%s:%s:%s:%s) =>", __FUNCTION__, + hash1, nonce.av_val, nchex, cnonce, qop, hash2); + RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH); + hexenc(md5sum_val, MD5_DIGEST_LENGTH, hash3); + + /* pubToken = &authmod=&user=&nonce=&cnonce=&nc=&response= */ + /* Append nonces and response to query string which already contains + * user + authmod */ + pubToken.av_val = malloc(64 + sizeof(authmod)-1 + user.av_len + nonce.av_len + sizeof(cnonce)-1 + sizeof(nchex)-1 + HEXHASH_LEN); + sprintf(pubToken.av_val, + "&nonce=%s&cnonce=%s&nc=%s&response=%s", + nonce.av_val, cnonce, nchex, hash3); + pubToken.av_len = strlen(pubToken.av_val); + RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken2: %s", __FUNCTION__, pubToken.av_val); + + free(orig_ptr); + } + else if(strstr(description->av_val, "?reason=authfail") != NULL) + { + RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed", __FUNCTION__); + return 0; + } + else if(strstr(description->av_val, "?reason=nosuchuser") != NULL) + { + RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: no such user", __FUNCTION__); + return 0; + } + else + { + RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: unknown auth mode: %s", + __FUNCTION__, description->av_val); + return 0; + } + + ptr = malloc(r->Link.app.av_len + pubToken.av_len); + strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); + strncpy(ptr + r->Link.app.av_len, pubToken.av_val, pubToken.av_len); + r->Link.app.av_len += pubToken.av_len; + if(r->Link.lFlags & RTMP_LF_FAPU) + free(r->Link.app.av_val); + r->Link.app.av_val = ptr; + + ptr = malloc(r->Link.tcUrl.av_len + pubToken.av_len); + strncpy(ptr, r->Link.tcUrl.av_val, r->Link.tcUrl.av_len); + strncpy(ptr + r->Link.tcUrl.av_len, pubToken.av_val, pubToken.av_len); + r->Link.tcUrl.av_len += pubToken.av_len; + if(r->Link.lFlags & RTMP_LF_FTCU) + free(r->Link.tcUrl.av_val); + r->Link.tcUrl.av_val = ptr; + + free(pubToken.av_val); + r->Link.lFlags |= RTMP_LF_FTCU | RTMP_LF_FAPU; + + RTMP_Log(RTMP_LOGDEBUG, "%s, new app: %.*s tcUrl: %.*s playpath: %s", __FUNCTION__, + r->Link.app.av_len, r->Link.app.av_val, + r->Link.tcUrl.av_len, r->Link.tcUrl.av_val, + r->Link.playpath.av_val); + } + else + { + return 0; + } + return 1; +} +#endif + + SAVC(onBWDone); SAVC(onFCSubscribe); SAVC(onFCUnsubscribe); @@ -2255,6 +2886,7 @@ SAVC(_error); SAVC(close); SAVC(code); SAVC(level); +SAVC(description); SAVC(onStatus); SAVC(playlist_ready); static const AVal av_NetStream_Failed = AVC("NetStream.Failed"); @@ -2268,9 +2900,13 @@ static const AVal av_NetStream_Play_Comp static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop"); static const AVal av_NetStream_Seek_Notify = AVC("NetStream.Seek.Notify"); static const AVal av_NetStream_Pause_Notify = AVC("NetStream.Pause.Notify"); +static const AVal av_NetStream_Play_PublishNotify = +AVC("NetStream.Play.PublishNotify"); static const AVal av_NetStream_Play_UnpublishNotify = AVC("NetStream.Play.UnpublishNotify"); static const AVal av_NetStream_Publish_Start = AVC("NetStream.Publish.Start"); +static const AVal av_NetConnection_Connect_Rejected = +AVC("NetConnection.Connect.Rejected"); /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */ static int @@ -2278,7 +2914,7 @@ HandleInvoke(RTMP *r, const char *body, { AMFObject obj; AVal method; - int txn; + double txn; int ret = 0, nRes; if (body[0] != 0x02) /* make sure it is a string method name we start with */ { @@ -2296,7 +2932,7 @@ HandleInvoke(RTMP *r, const char *body, AMF_Dump(&obj); AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method); - txn = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1)); + txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1)); RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val); if (AVMATCH(&method, &av__result)) @@ -2305,14 +2941,14 @@ HandleInvoke(RTMP *r, const char *body, int i; for (i=0; im_numCalls; i++) { - if (r->m_methodCalls[i].num == txn) { + if (r->m_methodCalls[i].num == (int)txn) { methodInvoked = r->m_methodCalls[i].name; AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE); break; } } if (!methodInvoked.av_val) { - RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %d without matching request", + RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request", __FUNCTION__, txn); goto leave; } @@ -2345,6 +2981,9 @@ HandleInvoke(RTMP *r, const char *body, 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); @@ -2409,7 +3048,57 @@ HandleInvoke(RTMP *r, const char *body, } else if (AVMATCH(&method, &av__error)) { +#ifdef CRYPTO + AVal methodInvoked = {0}; + int i; + + if (r->Link.protocol & RTMP_FEATURE_WRITE) + { + for (i=0; im_numCalls; i++) + { + if (r->m_methodCalls[i].num == txn) + { + methodInvoked = r->m_methodCalls[i].name; + AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE); + break; + } + } + if (!methodInvoked.av_val) + { + RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request", + __FUNCTION__, txn); + goto leave; + } + + RTMP_Log(RTMP_LOGDEBUG, "%s, received error for method call <%s>", __FUNCTION__, + methodInvoked.av_val); + + if (AVMATCH(&methodInvoked, &av_connect)) + { + AMFObject obj2; + AVal code, level, description; + AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2); + AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code); + AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level); + AMFProp_GetString(AMF_GetProp(&obj2, &av_description, -1), &description); + RTMP_Log(RTMP_LOGDEBUG, "%s, error description: %s", __FUNCTION__, description.av_val); + /* if PublisherAuth returns 1, then reconnect */ + if (PublisherAuth(r, &description) == 1) + { + CloseInternal(r, 1); + if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) + goto leave; + } + } + } + else + { + RTMP_Log(RTMP_LOGERROR, "rtmp server sent error"); + } + free(methodInvoked.av_val); +#else RTMP_Log(RTMP_LOGERROR, "rtmp server sent error"); +#endif } else if (AVMATCH(&method, &av_close)) { @@ -2435,7 +3124,8 @@ HandleInvoke(RTMP *r, const char *body, RTMP_Log(RTMP_LOGERROR, "Closing connection: %s", code.av_val); } - else if (AVMATCH(&code, &av_NetStream_Play_Start)) + else if (AVMATCH(&code, &av_NetStream_Play_Start) + || AVMATCH(&code, &av_NetStream_Play_PublishNotify)) { int i; r->m_bPlaying = TRUE; @@ -2519,11 +3209,11 @@ RTMP_FindFirstMatchingProperty(AMFObject if (AVMATCH(&prop->p_name, name)) { - *p = *prop; + memcpy(p, prop, sizeof(*prop)); return TRUE; } - if (prop->p_type == AMF_OBJECT) + if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY) { if (RTMP_FindFirstMatchingProperty(&prop->p_vu.p_object, name, p)) return TRUE; @@ -2545,7 +3235,7 @@ RTMP_FindPrefixProperty(AMFObject *obj, if (prop->p_name.av_len > name->av_len && !memcmp(prop->p_name.av_val, name->av_val, name->av_len)) { - *p = *prop; + memcpy(p, prop, sizeof(*prop)); return TRUE; } @@ -2562,47 +3252,44 @@ static int DumpMetaData(AMFObject *obj) { AMFObjectProperty *prop; - int n; + int n, len; for (n = 0; n < obj->o_num; n++) { + char str[256] = ""; prop = AMF_GetProp(obj, NULL, n); - if (prop->p_type != AMF_OBJECT) - { - char str[256] = ""; - switch (prop->p_type) - { - case AMF_NUMBER: - snprintf(str, 255, "%.2f", prop->p_vu.p_number); - break; - case AMF_BOOLEAN: - snprintf(str, 255, "%s", - prop->p_vu.p_number != 0. ? "TRUE" : "FALSE"); - break; - case AMF_STRING: - snprintf(str, 255, "%.*s", prop->p_vu.p_aval.av_len, - prop->p_vu.p_aval.av_val); - break; - case AMF_DATE: - snprintf(str, 255, "timestamp:%.2f", prop->p_vu.p_number); - break; - default: - snprintf(str, 255, "INVALID TYPE 0x%02x", - (unsigned char)prop->p_type); - } - if (prop->p_name.av_len) - { - /* chomp */ - if (strlen(str) >= 1 && str[strlen(str) - 1] == '\n') - str[strlen(str) - 1] = '\0'; - RTMP_Log(RTMP_LOGINFO, " %-22.*s%s", prop->p_name.av_len, - prop->p_name.av_val, str); - } - } - else + switch (prop->p_type) { + case AMF_OBJECT: + case AMF_ECMA_ARRAY: + case AMF_STRICT_ARRAY: if (prop->p_name.av_len) RTMP_Log(RTMP_LOGINFO, "%.*s:", prop->p_name.av_len, prop->p_name.av_val); DumpMetaData(&prop->p_vu.p_object); + break; + case AMF_NUMBER: + snprintf(str, 255, "%.2f", prop->p_vu.p_number); + break; + case AMF_BOOLEAN: + snprintf(str, 255, "%s", + prop->p_vu.p_number != 0. ? "TRUE" : "FALSE"); + break; + case AMF_STRING: + len = snprintf(str, 255, "%.*s", prop->p_vu.p_aval.av_len, + prop->p_vu.p_aval.av_val); + if (len >= 1 && str[len-1] == '\n') + str[len-1] = '\0'; + break; + case AMF_DATE: + snprintf(str, 255, "timestamp:%.2f", prop->p_vu.p_number); + break; + default: + snprintf(str, 255, "INVALID TYPE 0x%02x", + (unsigned char)prop->p_type); + } + if (str[0] && prop->p_name.av_len) + { + RTMP_Log(RTMP_LOGINFO, " %-22.*s%s", prop->p_name.av_len, + prop->p_name.av_val, str); } } return FALSE; @@ -2762,7 +3449,8 @@ HandleCtrl(RTMP *r, const RTMPPacket *pa break; if (!r->m_pausing) { - r->m_pauseStamp = r->m_channelTimestamp[r->m_mediaChannel]; + r->m_pauseStamp = r->m_mediaChannel < r->m_channelsAllocatedIn ? + r->m_channelTimestamp[r->m_mediaChannel] : 0; RTMP_SendPause(r, TRUE, r->m_pauseStamp); r->m_pausing = 1; } @@ -2789,11 +3477,17 @@ HandleCtrl(RTMP *r, const RTMPPacket *pa if (nType == 0x1A) { RTMP_Log(RTMP_LOGDEBUG, "%s, SWFVerification ping received: ", __FUNCTION__); + if (packet->m_nBodySize > 2 && packet->m_body[2] > 0x01) + { + RTMP_Log(RTMP_LOGERROR, + "%s: SWFVerification Type %d request not supported! Patches welcome...", + __FUNCTION__, packet->m_body[2]); + } #ifdef CRYPTO /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */ /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */ - if (r->Link.SWFSize) + else if (r->Link.SWFSize) { RTMP_SendCtrl(r, 0x1B, 0, 0); } @@ -2859,7 +3553,7 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac uint8_t hbuf[RTMP_MAX_HEADER_SIZE] = { 0 }; char *header = (char *)hbuf; int nSize, hSize, nToRead, nChunk; - int didAlloc = FALSE; + int extendedTimestamp; RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d", __FUNCTION__, r->m_sb.sb_socket); @@ -2901,6 +3595,26 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac nSize = packetSize[packet->m_headerType]; + if (packet->m_nChannel >= r->m_channelsAllocatedIn) + { + int n = packet->m_nChannel + 10; + int *timestamp = realloc(r->m_channelTimestamp, sizeof(int) * n); + RTMPPacket **packets = realloc(r->m_vecChannelsIn, sizeof(RTMPPacket*) * n); + if (!timestamp) + free(r->m_channelTimestamp); + if (!packets) + free(r->m_vecChannelsIn); + r->m_channelTimestamp = timestamp; + r->m_vecChannelsIn = packets; + if (!timestamp || !packets) { + r->m_channelsAllocatedIn = 0; + return FALSE; + } + memset(r->m_channelTimestamp + r->m_channelsAllocatedIn, 0, sizeof(int) * (n - r->m_channelsAllocatedIn)); + memset(r->m_vecChannelsIn + r->m_channelsAllocatedIn, 0, sizeof(RTMPPacket*) * (n - r->m_channelsAllocatedIn)); + r->m_channelsAllocatedIn = n; + } + if (nSize == RTMP_LARGE_HEADER_SIZE) /* if we get a full header the timestamp is absolute */ packet->m_hasAbsTimestamp = TRUE; @@ -2933,6 +3647,8 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac packet->m_nBodySize = AMF_DecodeInt24(header + 3); packet->m_nBytesRead = 0; RTMPPacket_Free(packet); + if (r->m_vecChannelsIn[packet->m_nChannel]) + r->m_vecChannelsIn[packet->m_nChannel]->m_body = NULL; if (nSize > 6) { @@ -2942,17 +3658,19 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac packet->m_nInfoField2 = DecodeInt32LE(header + 7); } } - if (packet->m_nTimeStamp == 0xffffff) + } + + extendedTimestamp = packet->m_nTimeStamp == 0xffffff; + if (extendedTimestamp) + { + if (ReadN(r, header + nSize, 4) != 4) { - if (ReadN(r, header + nSize, 4) != 4) - { - RTMP_Log(RTMP_LOGERROR, "%s, failed to read extended timestamp", - __FUNCTION__); - return FALSE; - } - packet->m_nTimeStamp = AMF_DecodeInt32(header + nSize); - hSize += 4; + RTMP_Log(RTMP_LOGERROR, "%s, failed to read extended timestamp", + __FUNCTION__); + return FALSE; } + packet->m_nTimeStamp = AMF_DecodeInt32(header + nSize); + hSize += 4; } RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)hbuf, hSize); @@ -2964,7 +3682,6 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac RTMP_Log(RTMP_LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__); return FALSE; } - didAlloc = TRUE; packet->m_headerType = (hbuf[0] & 0xc0) >> 6; } @@ -2984,7 +3701,7 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac if (ReadN(r, packet->m_body + packet->m_nBytesRead, nChunk) != nChunk) { - RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet body. len: %lu", + RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet body. len: %u", __FUNCTION__, packet->m_nBodySize); return FALSE; } @@ -2997,6 +3714,10 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac if (!r->m_vecChannelsIn[packet->m_nChannel]) r->m_vecChannelsIn[packet->m_nChannel] = malloc(sizeof(RTMPPacket)); memcpy(r->m_vecChannelsIn[packet->m_nChannel], packet, sizeof(RTMPPacket)); + if (extendedTimestamp) + { + r->m_vecChannelsIn[packet->m_nChannel]->m_nTimeStamp = 0xffffff; + } if (RTMPPacket_IsReady(packet)) { @@ -3176,7 +3897,7 @@ RTMP_SendChunk(RTMP *r, RTMPChunk *chunk int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue) { - const RTMPPacket *prevPacket = r->m_vecChannelsOut[packet->m_nChannel]; + const RTMPPacket *prevPacket; uint32_t last = 0; int nSize; int hSize, cSize; @@ -3186,6 +3907,22 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac int nChunkSize; int tlen; + if (packet->m_nChannel >= r->m_channelsAllocatedOut) + { + int n = packet->m_nChannel + 10; + RTMPPacket **packets = realloc(r->m_vecChannelsOut, sizeof(RTMPPacket*) * n); + if (!packets) { + free(r->m_vecChannelsOut); + r->m_vecChannelsOut = NULL; + r->m_channelsAllocatedOut = 0; + return FALSE; + } + r->m_vecChannelsOut = packets; + memset(r->m_vecChannelsOut + r->m_channelsAllocatedOut, 0, sizeof(RTMPPacket*) * (n - r->m_channelsAllocatedOut)); + r->m_channelsAllocatedOut = n; + } + + prevPacket = r->m_vecChannelsOut[packet->m_nChannel]; if (prevPacket && packet->m_headerType != RTMP_PACKET_SIZE_LARGE) { /* compress a bit by using the prev packet's attributes */ @@ -3232,10 +3969,11 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac hSize += cSize; } - if (nSize > 1 && t >= 0xffffff) + if (t >= 0xffffff) { header -= 4; hSize += 4; + RTMP_Log(RTMP_LOGWARNING, "Larger timestamp than 24-bit: 0x%x", t); } hptr = header; @@ -3274,7 +4012,7 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac if (nSize > 8) hptr += EncodeInt32LE(hptr, packet->m_nInfoField2); - if (nSize > 1 && t >= 0xffffff) + if (t >= 0xffffff) hptr = AMF_EncodeInt32(hptr, hend, t); nSize = packet->m_nBodySize; @@ -3329,6 +4067,11 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac header -= cSize; hSize += cSize; } + if (t >= 0xffffff) + { + header -= 4; + hSize += 4; + } *header = (0xc0 | c); if (cSize) { @@ -3337,6 +4080,11 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac if (cSize == 2) header[2] = tmp >> 8; } + if (t >= 0xffffff) + { + char* extendedTimestamp = header + 1 + cSize; + AMF_EncodeInt32(extendedTimestamp, extendedTimestamp + 4, t); + } } } if (tbuf) @@ -3349,7 +4097,7 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac } /* we invoked a remote method */ - if (packet->m_packetType == 0x14) + if (packet->m_packetType == RTMP_PACKET_TYPE_INVOKE) { AVal method; char *ptr; @@ -3380,16 +4128,22 @@ RTMP_Serve(RTMP *r) void RTMP_Close(RTMP *r) { + CloseInternal(r, 0); +} + +static void +CloseInternal(RTMP *r, int reconnect) +{ int i; if (RTMP_IsConnected(r)) { if (r->m_stream_id > 0) { - if ((r->Link.protocol & RTMP_FEATURE_WRITE)) - SendFCUnpublish(r); i = r->m_stream_id; r->m_stream_id = 0; + if ((r->Link.protocol & RTMP_FEATURE_WRITE)) + SendFCUnpublish(r); SendDeleteStream(r, i); } if (r->m_clientID.av_val) @@ -3422,7 +4176,7 @@ RTMP_Close(RTMP *r) r->m_write.m_nBytesRead = 0; RTMPPacket_Free(&r->m_write); - for (i = 0; i < RTMP_CHANNELS; i++) + for (i = 0; i < r->m_channelsAllocatedIn; i++) { if (r->m_vecChannelsIn[i]) { @@ -3430,12 +4184,23 @@ RTMP_Close(RTMP *r) free(r->m_vecChannelsIn[i]); r->m_vecChannelsIn[i] = NULL; } + } + free(r->m_vecChannelsIn); + r->m_vecChannelsIn = NULL; + free(r->m_channelTimestamp); + r->m_channelTimestamp = NULL; + r->m_channelsAllocatedIn = 0; + for (i = 0; i < r->m_channelsAllocatedOut; i++) + { if (r->m_vecChannelsOut[i]) { free(r->m_vecChannelsOut[i]); r->m_vecChannelsOut[i] = NULL; } } + free(r->m_vecChannelsOut); + r->m_vecChannelsOut = NULL; + r->m_channelsAllocatedOut = 0; AV_clear(r->m_methodCalls, r->m_numCalls); r->m_methodCalls = NULL; r->m_numCalls = 0; @@ -3448,16 +4213,24 @@ RTMP_Close(RTMP *r) r->m_resplen = 0; r->m_unackd = 0; - free(r->Link.playpath0.av_val); - r->Link.playpath0.av_val = NULL; - - if (r->Link.lFlags & RTMP_LF_FTCU) + if (r->Link.lFlags & RTMP_LF_FTCU && !reconnect) { free(r->Link.tcUrl.av_val); r->Link.tcUrl.av_val = NULL; r->Link.lFlags ^= RTMP_LF_FTCU; } + if (r->Link.lFlags & RTMP_LF_FAPU && !reconnect) + { + free(r->Link.app.av_val); + r->Link.app.av_val = NULL; + r->Link.lFlags ^= RTMP_LF_FAPU; + } + if (!reconnect) + { + free(r->Link.playpath0.av_val); + r->Link.playpath0.av_val = NULL; + } #ifdef CRYPTO if (r->Link.dh) { @@ -3487,7 +4260,7 @@ RTMPSockBuf_Fill(RTMPSockBuf *sb) while (1) { - nBytes = sizeof(sb->sb_buf) - sb->sb_size - (sb->sb_start - sb->sb_buf); + nBytes = sizeof(sb->sb_buf) - 1 - sb->sb_size - (sb->sb_start - sb->sb_buf); #if defined(CRYPTO) && !defined(NO_SSL) if (sb->sb_ssl) { @@ -3555,7 +4328,9 @@ RTMPSockBuf_Close(RTMPSockBuf *sb) sb->sb_ssl = NULL; } #endif - return closesocket(sb->sb_socket); + if (sb->sb_socket != -1) + return closesocket(sb->sb_socket); + return 0; } #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf)) @@ -3637,8 +4412,8 @@ HTTP_Post(RTMP *r, RTMPTCmd cmd, const c int hlen = snprintf(hbuf, sizeof(hbuf), "POST /%s%s/%d HTTP/1.1\r\n" "Host: %.*s:%d\r\n" "Accept: */*\r\n" - "User-Agent: Shockwave Flash\n" - "Connection: Keep-Alive\n" + "User-Agent: Shockwave Flash\r\n" + "Connection: Keep-Alive\r\n" "Cache-Control: no-cache\r\n" "Content-type: application/x-fcs\r\n" "Content-length: %d\r\n\r\n", RTMPT_cmds[cmd], @@ -3656,22 +4431,45 @@ static int HTTP_read(RTMP *r, int fill) { char *ptr; - int hlen; + long hlen; +restart: if (fill) RTMPSockBuf_Fill(&r->m_sb); - if (r->m_sb.sb_size < 144) - return -1; + if (r->m_sb.sb_size < 13) { + if (fill) + goto restart; + return -2; + } if (strncmp(r->m_sb.sb_start, "HTTP/1.1 200 ", 13)) return -1; - ptr = strstr(r->m_sb.sb_start, "Content-Length:"); + r->m_sb.sb_start[r->m_sb.sb_size] = '\0'; + if (!strstr(r->m_sb.sb_start, "\r\n\r\n")) { + if (fill) + goto restart; + return -2; + } + + ptr = r->m_sb.sb_start + sizeof("HTTP/1.1 200"); + while ((ptr = strstr(ptr, "Content-"))) { + if (!strncasecmp(ptr+8, "length:", 7)) break; + ptr += 8; + } if (!ptr) return -1; - hlen = atoi(ptr+16); - ptr = strstr(ptr, "\r\n\r\n"); + hlen = strtol(ptr+16, NULL, 10); + if (hlen < 1 || hlen > INT_MAX) + return -1; + ptr = strstr(ptr+16, "\r\n\r\n"); if (!ptr) return -1; ptr += 4; + if (ptr + (r->m_clientID.av_val ? 1 : hlen) > r->m_sb.sb_start + r->m_sb.sb_size) + { + if (fill) + goto restart; + return -2; + } r->m_sb.sb_size -= ptr - r->m_sb.sb_start; r->m_sb.sb_start = ptr; r->m_unackd--; @@ -3721,8 +4519,8 @@ Read_1_Packet(RTMP *r, char *buf, unsign char *packetBody = packet.m_body; unsigned int nPacketLen = packet.m_nBodySize; - /* Return -3 if this was completed nicely with invoke message - * Play.Stop or Play.Complete + /* Return RTMP_READ_COMPLETE if this was completed nicely with + * invoke message Play.Stop or Play.Complete */ if (rtnGetNextMediaPacket == 2) { @@ -3733,17 +4531,17 @@ Read_1_Packet(RTMP *r, char *buf, unsign break; } - r->m_read.dataType |= (((packet.m_packetType == 0x08) << 2) | - (packet.m_packetType == 0x09)); + r->m_read.dataType |= (((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO) << 2) | + (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO)); - if (packet.m_packetType == 0x09 && nPacketLen <= 5) + if (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO && nPacketLen <= 5) { RTMP_Log(RTMP_LOGDEBUG, "ignoring too small video packet: size: %d", nPacketLen); ret = RTMP_READ_IGNORE; break; } - if (packet.m_packetType == 0x08 && nPacketLen <= 1) + if (packet.m_packetType == RTMP_PACKET_TYPE_AUDIO && nPacketLen <= 1) { RTMP_Log(RTMP_LOGDEBUG, "ignoring too small audio packet: size: %d", nPacketLen); @@ -3760,7 +4558,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign RTMP_Log(RTMP_LOGDEBUG, "type: %02X, size: %d, TS: %d ms, abs TS: %d", packet.m_packetType, nPacketLen, packet.m_nTimeStamp, packet.m_hasAbsTimestamp); - if (packet.m_packetType == 0x09) + if (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO) RTMP_Log(RTMP_LOGDEBUG, "frametype: %02X", (*packetBody & 0xf0)); #endif @@ -3770,7 +4568,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign if (packet.m_nTimeStamp == 0) { if (r->m_read.nMetaHeaderSize > 0 - && packet.m_packetType == 0x12) + && packet.m_packetType == RTMP_PACKET_TYPE_INFO) { AMFObject metaObj; int nRes = @@ -3831,7 +4629,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign * in the first FLV stream chunk and we have to compare * it and filter it out !! */ - if (packet.m_packetType == 0x16) + if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO) { /* basically we have to find the keyframe with the * correct TS being nResumeTS @@ -3943,7 +4741,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign * (seeking might put us somewhere before it) */ if (!(r->m_read.flags & RTMP_READ_GOTKF) && - packet.m_packetType != 0x16) + packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO) { RTMP_Log(RTMP_LOGWARNING, "Stream does not start with requested frame, ignoring data... "); @@ -3956,7 +4754,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign } /* ok, do the same for FLV streams */ if (!(r->m_read.flags & RTMP_READ_GOTFLVK) && - packet.m_packetType == 0x16) + packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO) { RTMP_Log(RTMP_LOGWARNING, "Stream does not start with requested FLV frame, ignoring data... "); @@ -3975,9 +4773,11 @@ Read_1_Packet(RTMP *r, char *buf, unsign * the preceding if clause) */ if (!(r->m_read.flags & RTMP_READ_NO_IGNORE) && - packet.m_packetType != 0x16) - { /* exclude type 0x16 (FLV) since it can - * contain several FLV packets */ + packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO) + { + /* exclude type RTMP_PACKET_TYPE_FLASH_VIDEO since it can + * contain several FLV packets + */ if (packet.m_nTimeStamp == 0) { ret = RTMP_READ_IGNORE; @@ -3993,9 +4793,10 @@ Read_1_Packet(RTMP *r, char *buf, unsign /* calculate packet size and allocate slop buffer if necessary */ size = nPacketLen + - ((packet.m_packetType == 0x08 || packet.m_packetType == 0x09 - || packet.m_packetType == 0x12) ? 11 : 0) + - (packet.m_packetType != 0x16 ? 4 : 0); + ((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO + || packet.m_packetType == RTMP_PACKET_TYPE_VIDEO + || packet.m_packetType == RTMP_PACKET_TYPE_INFO) ? 11 : 0) + + (packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO ? 4 : 0); if (size + 4 > buflen) { @@ -4021,8 +4822,9 @@ Read_1_Packet(RTMP *r, char *buf, unsign /* audio (0x08), video (0x09) or metadata (0x12) packets : * construct 11 byte header then add rtmp packet's data */ - if (packet.m_packetType == 0x08 || packet.m_packetType == 0x09 - || packet.m_packetType == 0x12) + if (packet.m_packetType == RTMP_PACKET_TYPE_AUDIO + || packet.m_packetType == RTMP_PACKET_TYPE_VIDEO + || packet.m_packetType == RTMP_PACKET_TYPE_INFO) { nTimeStamp = r->m_read.nResumeTS + packet.m_nTimeStamp; prevTagSize = 11 + nPacketLen; @@ -4032,7 +4834,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign ptr = AMF_EncodeInt24(ptr, pend, nPacketLen); #if 0 - if(packet.m_packetType == 0x09) { /* video */ + if(packet.m_packetType == RTMP_PACKET_TYPE_VIDEO) { /* H264 fix: */ if((packetBody[0] & 0x0f) == 7) { /* CodecId = H264 */ @@ -4062,7 +4864,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign len = nPacketLen; /* correct tagSize and obtain timestamp if we have an FLV stream */ - if (packet.m_packetType == 0x16) + if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO) { unsigned int pos = 0; int delta; @@ -4070,7 +4872,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign /* grab first timestamp and see if it needs fixing */ nTimeStamp = AMF_DecodeInt24(packetBody + 4); nTimeStamp |= (packetBody[7] << 24); - delta = packet.m_nTimeStamp - nTimeStamp; + delta = packet.m_nTimeStamp - nTimeStamp + r->m_read.nResumeTS; while (pos + 11 < nPacketLen) { @@ -4095,7 +4897,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign if (pos + 11 + dataSize > nPacketLen) { RTMP_Log(RTMP_LOGERROR, - "Wrong data size (%lu), stream corrupted, aborting!", + "Wrong data size (%u), stream corrupted, aborting!", dataSize); ret = RTMP_READ_ERROR; break; @@ -4140,7 +4942,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign } ptr += len; - if (packet.m_packetType != 0x16) + if (packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO) { /* FLV tag packets contain their own prevTagSize */ AMF_EncodeInt32(ptr, pend, prevTagSize); @@ -4208,6 +5010,7 @@ fail: memcpy(mybuf, flvHeader, sizeof(flvHeader)); r->m_read.buf += sizeof(flvHeader); r->m_read.buflen -= sizeof(flvHeader); + cnt += sizeof(flvHeader); while (r->m_read.timestamp == 0) { @@ -4224,6 +5027,7 @@ fail: if (r->m_read.buf < mybuf || r->m_read.buf > end) { mybuf = realloc(mybuf, cnt + nRead); memcpy(mybuf+cnt, r->m_read.buf, nRead); + free(r->m_read.buf); r->m_read.buf = mybuf+cnt+nRead; break; } @@ -4325,11 +5129,12 @@ RTMP_Write(RTMP *r, const char *buf, int buf += 3; s2 -= 11; - if (((pkt->m_packetType == 0x08 || pkt->m_packetType == 0x09) && - !pkt->m_nTimeStamp) || pkt->m_packetType == 0x12) + if (((pkt->m_packetType == RTMP_PACKET_TYPE_AUDIO + || pkt->m_packetType == RTMP_PACKET_TYPE_VIDEO) && + !pkt->m_nTimeStamp) || pkt->m_packetType == RTMP_PACKET_TYPE_INFO) { pkt->m_headerType = RTMP_PACKET_SIZE_LARGE; - if (pkt->m_packetType == 0x12) + if (pkt->m_packetType == RTMP_PACKET_TYPE_INFO) pkt->m_nBodySize += 16; } else @@ -4344,7 +5149,7 @@ RTMP_Write(RTMP *r, const char *buf, int } enc = pkt->m_body; pend = enc + pkt->m_nBodySize; - if (pkt->m_packetType == 0x12) + if (pkt->m_packetType == RTMP_PACKET_TYPE_INFO) { enc = AMF_EncodeString(enc, pend, &av_setDataFrame); pkt->m_nBytesRead = enc - pkt->m_body; diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/rtmp.h rtmpdump/librtmp/rtmp.h --- rtmpdump-2.3/librtmp/rtmp.h 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/rtmp.h 2026-06-19 14:15:18.303594982 -0600 @@ -71,9 +71,29 @@ extern "C" uint32_t RTMP_GetTime(void); -#define RTMP_PACKET_TYPE_AUDIO 0x08 -#define RTMP_PACKET_TYPE_VIDEO 0x09 -#define RTMP_PACKET_TYPE_INFO 0x12 +/* RTMP_PACKET_TYPE_... 0x00 */ +#define RTMP_PACKET_TYPE_CHUNK_SIZE 0x01 +/* RTMP_PACKET_TYPE_... 0x02 */ +#define RTMP_PACKET_TYPE_BYTES_READ_REPORT 0x03 +#define RTMP_PACKET_TYPE_CONTROL 0x04 +#define RTMP_PACKET_TYPE_SERVER_BW 0x05 +#define RTMP_PACKET_TYPE_CLIENT_BW 0x06 +/* RTMP_PACKET_TYPE_... 0x07 */ +#define RTMP_PACKET_TYPE_AUDIO 0x08 +#define RTMP_PACKET_TYPE_VIDEO 0x09 +/* RTMP_PACKET_TYPE_... 0x0A */ +/* RTMP_PACKET_TYPE_... 0x0B */ +/* RTMP_PACKET_TYPE_... 0x0C */ +/* RTMP_PACKET_TYPE_... 0x0D */ +/* RTMP_PACKET_TYPE_... 0x0E */ +#define RTMP_PACKET_TYPE_FLEX_STREAM_SEND 0x0F +#define RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT 0x10 +#define RTMP_PACKET_TYPE_FLEX_MESSAGE 0x11 +#define RTMP_PACKET_TYPE_INFO 0x12 +#define RTMP_PACKET_TYPE_SHARED_OBJECT 0x13 +#define RTMP_PACKET_TYPE_INVOKE 0x14 +/* RTMP_PACKET_TYPE_... 0x15 */ +#define RTMP_PACKET_TYPE_FLASH_VIDEO 0x16 #define RTMP_MAX_HEADER_SIZE 18 @@ -116,7 +136,7 @@ extern "C" void RTMPPacket_Reset(RTMPPacket *p); void RTMPPacket_Dump(RTMPPacket *p); - int RTMPPacket_Alloc(RTMPPacket *p, int nSize); + int RTMPPacket_Alloc(RTMPPacket *p, uint32_t nSize); void RTMPPacket_Free(RTMPPacket *p); #define RTMPPacket_IsReady(a) ((a)->m_nBytesRead == (a)->m_nBodySize) @@ -135,7 +155,10 @@ extern "C" AVal auth; AVal flashVer; AVal subscribepath; + AVal usherToken; AVal token; + AVal pubUser; + AVal pubPasswd; AMFObject extras; int edepth; @@ -148,6 +171,7 @@ extern "C" #define RTMP_LF_PLST 0x0008 /* send playlist before play */ #define RTMP_LF_BUFX 0x0010 /* toggle stream on BufferEmpty msg */ #define RTMP_LF_FTCU 0x0020 /* free tcUrl on close */ +#define RTMP_LF_FAPU 0x0040 /* free app on close */ int lFlags; int swfAge; @@ -155,6 +179,8 @@ extern "C" int protocol; int timeout; /* connection timeout in seconds */ + int pFlags; /* unused, but kept to avoid breaking ABI */ + unsigned short socksport; unsigned short port; @@ -232,9 +258,11 @@ extern "C" int m_numCalls; RTMP_METHOD *m_methodCalls; /* remote method calls queue */ - RTMPPacket *m_vecChannelsIn[RTMP_CHANNELS]; - RTMPPacket *m_vecChannelsOut[RTMP_CHANNELS]; - int m_channelTimestamp[RTMP_CHANNELS]; /* abs timestamp of last packet */ + int m_channelsAllocatedIn; + int m_channelsAllocatedOut; + RTMPPacket **m_vecChannelsIn; + RTMPPacket **m_vecChannelsOut; + int *m_channelTimestamp; /* abs timestamp of last packet */ double m_fAudioCodecs; /* audioCodecs for the connect packet */ double m_fVideoCodecs; /* videoCodecs for the connect packet */ @@ -277,6 +305,7 @@ extern "C" uint32_t swfSize, AVal *flashVer, AVal *subscribepath, + AVal *usherToken, int dStart, int dStop, int bLiveStream, long int timeout); @@ -285,6 +314,7 @@ extern "C" int RTMP_Connect0(RTMP *r, struct sockaddr *svc); int RTMP_Connect1(RTMP *r, RTMPPacket *cp); int RTMP_Serve(RTMP *r); + int RTMP_TLS_Accept(RTMP *r, void *ctx); int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet); int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue); @@ -307,6 +337,9 @@ extern "C" void RTMP_Free(RTMP *r); void RTMP_EnableWrite(RTMP *r); + void *RTMP_TLS_AllocServerContext(const char* cert, const char* key); + void RTMP_TLS_FreeServerContext(void *ctx); + int RTMP_LibVersion(void); void RTMP_UserInterrupt(void); /* user typed Ctrl-C */ diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/rtmp_sys.h rtmpdump/librtmp/rtmp_sys.h --- rtmpdump-2.3/librtmp/rtmp_sys.h 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/librtmp/rtmp_sys.h 2026-06-19 14:15:18.303594982 -0600 @@ -24,17 +24,14 @@ #ifdef _WIN32 -#ifdef _XBOX -#include -#include +#include +#include + +#ifdef _MSC_VER /* MSVC */ #define snprintf _snprintf #define strcasecmp stricmp #define strncasecmp strnicmp #define vsnprintf _vsnprintf - -#else /* !_XBOX */ -#include -#include #endif #define GetSockError() WSAGetLastError() @@ -49,10 +46,10 @@ #include #include #include -#include #include #include #include +#include #define GetSockError() errno #define SetSockError(e) errno = e #undef closesocket @@ -64,20 +61,46 @@ #include "rtmp.h" #ifdef USE_POLARSSL +#include #include #include #include +#if POLARSSL_VERSION_NUMBER < 0x01010000 +#define havege_random havege_rand +#endif +#if POLARSSL_VERSION_NUMBER >= 0x01020000 +#define SSL_SET_SESSION(S,resume,timeout,ctx) ssl_set_session(S,ctx) +#else +#define SSL_SET_SESSION(S,resume,timeout,ctx) ssl_set_session(S,resume,timeout,ctx) +#endif typedef struct tls_ctx { havege_state hs; ssl_session ssn; } tls_ctx; +typedef struct tls_server_ctx { + havege_state *hs; + x509_cert cert; + rsa_context key; + ssl_session ssn; + const char *dhm_P, *dhm_G; +} tls_server_ctx; + #define TLS_CTX tls_ctx * #define TLS_client(ctx,s) s = malloc(sizeof(ssl_context)); ssl_init(s);\ ssl_set_endpoint(s, SSL_IS_CLIENT); ssl_set_authmode(s, SSL_VERIFY_NONE);\ - ssl_set_rng(s, havege_rand, &ctx->hs); ssl_set_ciphers(s, ssl_default_ciphers);\ - ssl_set_session(s, 1, 600, &ctx->ssn) + ssl_set_rng(s, havege_random, &ctx->hs);\ + ssl_set_ciphersuites(s, ssl_default_ciphersuites);\ + SSL_SET_SESSION(s, 1, 600, &ctx->ssn) +#define TLS_server(ctx,s) s = malloc(sizeof(ssl_context)); ssl_init(s);\ + ssl_set_endpoint(s, SSL_IS_SERVER); ssl_set_authmode(s, SSL_VERIFY_NONE);\ + ssl_set_rng(s, havege_random, ((tls_server_ctx*)ctx)->hs);\ + ssl_set_ciphersuites(s, ssl_default_ciphersuites);\ + SSL_SET_SESSION(s, 1, 600, &((tls_server_ctx*)ctx)->ssn);\ + ssl_set_own_cert(s, &((tls_server_ctx*)ctx)->cert, &((tls_server_ctx*)ctx)->key);\ + ssl_set_dh_param(s, ((tls_server_ctx*)ctx)->dhm_P, ((tls_server_ctx*)ctx)->dhm_G) #define TLS_setfd(s,fd) ssl_set_bio(s, net_recv, &fd, net_send, &fd) #define TLS_connect(s) ssl_handshake(s) +#define TLS_accept(s) ssl_handshake(s) #define TLS_read(s,b,l) ssl_read(s,(unsigned char *)b,l) #define TLS_write(s,b,l) ssl_write(s,(unsigned char *)b,l) #define TLS_shutdown(s) ssl_close_notify(s) @@ -91,8 +114,10 @@ typedef struct tls_ctx { } tls_ctx; #define TLS_CTX tls_ctx * #define TLS_client(ctx,s) gnutls_init((gnutls_session_t *)(&s), GNUTLS_CLIENT); gnutls_priority_set(s, ctx->prios); gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, ctx->cred) +#define TLS_server(ctx,s) gnutls_init((gnutls_session_t *)(&s), GNUTLS_SERVER); gnutls_priority_set_direct(s, "NORMAL", NULL); gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, ctx) #define TLS_setfd(s,fd) gnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)(long)fd) #define TLS_connect(s) gnutls_handshake(s) +#define TLS_accept(s) gnutls_handshake(s) #define TLS_read(s,b,l) gnutls_record_recv(s,b,l) #define TLS_write(s,b,l) gnutls_record_send(s,b,l) #define TLS_shutdown(s) gnutls_bye(s, GNUTLS_SHUT_RDWR) @@ -101,8 +126,10 @@ typedef struct tls_ctx { #else /* USE_OPENSSL */ #define TLS_CTX SSL_CTX * #define TLS_client(ctx,s) s = SSL_new(ctx) +#define TLS_server(ctx,s) s = SSL_new(ctx) #define TLS_setfd(s,fd) SSL_set_fd(s,fd) #define TLS_connect(s) SSL_connect(s) +#define TLS_accept(s) SSL_accept(s) #define TLS_read(s,b,l) SSL_read(s,b,l) #define TLS_write(s,b,l) SSL_write(s,b,l) #define TLS_shutdown(s) SSL_shutdown(s) diff '--color=auto' -Naurp rtmpdump-2.3/Makefile rtmpdump/Makefile --- rtmpdump-2.3/Makefile 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/Makefile 2026-06-19 14:15:18.300594955 -0600 @@ -1,4 +1,4 @@ -VERSION=v2.3 +VERSION=v2.6 prefix=/usr/local @@ -11,9 +11,10 @@ SYS=posix CRYPTO=OPENSSL #CRYPTO=POLARSSL #CRYPTO=GNUTLS -LIB_GNUTLS=-lgnutls -lgcrypt -LIB_OPENSSL=-lssl -lcrypto -LIB_POLARSSL=-lpolarssl +LIBZ=-lz +LIB_GNUTLS=-lgnutls -lhogweed -lnettle -lgmp $(LIBZ) +LIB_OPENSSL=-lssl -lcrypto $(LIBZ) +LIB_POLARSSL=-lpolarssl $(LIBZ) CRYPTO_LIB=$(LIB_$(CRYPTO)) DEF_=-DNO_CRYPTO CRYPTO_DEF=$(DEF_$(CRYPTO)) @@ -32,10 +33,13 @@ SBINDIR=$(DESTDIR)$(sbindir) MANDIR=$(DESTDIR)$(mandir) LIBS_posix= +LIBS_darwin= LIBS_mingw=-lws2_32 -lwinmm -lgdi32 -LIBS=$(CRYPTO_LIB) -lz $(LIBS_$(SYS)) $(XLIBS) +LIB_RTMP=-Llibrtmp -lrtmp +LIBS=$(LIB_RTMP) $(CRYPTO_LIB) $(LIBS_$(SYS)) $(XLIBS) THREADLIB_posix=-lpthread +THREADLIB_darwin=-lpthread THREADLIB_mingw= THREADLIB=$(THREADLIB_$(SYS)) SLIBS=$(THREADLIB) $(LIBS) @@ -44,14 +48,17 @@ LIBRTMP=librtmp/librtmp.a INCRTMP=librtmp/rtmp_sys.h librtmp/rtmp.h librtmp/log.h librtmp/amf.h EXT_posix= +EXT_darwin= EXT_mingw=.exe EXT=$(EXT_$(SYS)) -all: $(LIBRTMP) progs +PROGS=rtmpdump rtmpgw rtmpsrv rtmpsuck -progs: rtmpdump rtmpgw rtmpsrv rtmpsuck +all: $(LIBRTMP) $(PROGS) -install: progs +$(PROGS): $(LIBRTMP) + +install: $(PROGS) -mkdir -p $(BINDIR) $(SBINDIR) $(MANDIR)/man1 $(MANDIR)/man8 cp rtmpdump$(EXT) $(BINDIR) cp rtmpgw$(EXT) rtmpsrv$(EXT) rtmpsuck$(EXT) $(SBINDIR) @@ -68,20 +75,17 @@ FORCE: $(LIBRTMP): FORCE @cd librtmp; $(MAKE) all -# note: $^ is GNU Make's equivalent to BSD $> -# we use both since either make will ignore the one it doesn't recognize - -rtmpdump: rtmpdump.o $(LIBRTMP) - $(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(LIBS) +rtmpdump: rtmpdump.o + $(CC) $(LDFLAGS) -o $@$(EXT) $@.o $(LIBS) -rtmpsrv: rtmpsrv.o thread.o $(LIBRTMP) - $(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(SLIBS) +rtmpsrv: rtmpsrv.o thread.o + $(CC) $(LDFLAGS) -o $@$(EXT) $@.o thread.o $(SLIBS) -rtmpsuck: rtmpsuck.o thread.o $(LIBRTMP) - $(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(SLIBS) +rtmpsuck: rtmpsuck.o thread.o + $(CC) $(LDFLAGS) -o $@$(EXT) $@.o thread.o $(SLIBS) -rtmpgw: rtmpgw.o thread.o $(LIBRTMP) - $(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(SLIBS) +rtmpgw: rtmpgw.o thread.o + $(CC) $(LDFLAGS) -o $@$(EXT) $@.o thread.o $(SLIBS) rtmpgw.o: rtmpgw.c $(INCRTMP) Makefile rtmpdump.o: rtmpdump.c $(INCRTMP) Makefile diff '--color=auto' -Naurp rtmpdump-2.3/README rtmpdump/README --- rtmpdump-2.3/README 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/README 2026-06-19 14:15:18.300594955 -0600 @@ -1,7 +1,8 @@ -RTMP Dump v2.3 +RTMP Dump v2.4 (C) 2009 Andrej Stepanchuk -(C) 2009-2010 Howard Chu +(C) 2009-2011 Howard Chu (C) 2010 2a665470ced7adb7156fcef47f8199a6371c117b8a79e399a2771e0b36384090 +(C) 2011 33ae1ce77301f4b4494faaa5f609f3c48b9dcf82 License: GPLv2 librtmp license: LGPLv2.1 http://rtmpdump.mplayerhq.hu/ @@ -10,7 +11,11 @@ To compile type "make" with SYS=RTMPDUMP(1)RTMPDUMP(1) -RTMPDump v2.2e2010-05-02RTMPDUMP(1) +RTMPDump v2.42012-07-24RTMPDUMP(1)

      -
    @@ -34,6 +34,7 @@ rtmpdump − RTMP streaming media cl [−y playpath] [−Y] [−v] +[−R] [−d subscription] [−e] [−k skip] @@ -42,6 +43,7 @@ rtmpdump − RTMP streaming media cl [−b buffer] [−m timeout] [−T key] +[−j JSON] [−w swfHash] [−x swfSize] [−W swfUrl] @@ -217,6 +219,15 @@ Name of live stream to subscribe to. Def

    +−−realtime −R +
    +Download approximately in realtime, without attempting to speed up via +Pause/Unpause commands ("the BUFX hack"). +Useful for servers that jump backwards in time at the Unpause command. +Resuming and seeking in realtime streams is still possible. +
    +

    +

    −−resume −e
    Resume an incomplete RTMP download. @@ -275,6 +286,12 @@ authentication.

    +−−jtv −j JSON +
    +JSON token used by legacy Justin.tv servers. Invokes NetStream.Authenticate.UsherToken +
    +

    +

    −−swfhash −w hexstring
    SHA256 hash of the decompressed SWF file. This option may be needed if diff '--color=auto' -Naurp rtmpdump-2.3/rtmpdump.c rtmpdump/rtmpdump.c --- rtmpdump-2.3/rtmpdump.c 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/rtmpdump.c 2026-06-19 14:15:18.304594991 -0600 @@ -46,6 +46,7 @@ #define RD_SUCCESS 0 #define RD_FAILED 1 #define RD_INCOMPLETE 2 +#define RD_NO_CONNECT 3 #define DEF_TIMEOUT 30 /* seconds */ #define DEF_BUFTIME (10 * 60 * 60 * 1000) /* 10 hours default */ @@ -440,11 +441,11 @@ GetLastKeyframe(FILE * file, // output f int Download(RTMP * rtmp, // connected RTMP object - FILE * file, uint32_t dSeek, uint32_t dStopOffset, double duration, int bResume, char *metaHeader, uint32_t nMetaHeaderSize, char *initialFrame, int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames, int bStdoutMode, int bLiveStream, int bHashes, int bOverrideBufferTime, uint32_t bufferTime, double *percent) // percentage downloaded [out] + FILE * file, uint32_t dSeek, uint32_t dStopOffset, double duration, int bResume, char *metaHeader, uint32_t nMetaHeaderSize, char *initialFrame, int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames, int bStdoutMode, int bLiveStream, int bRealtimeStream, int bHashes, int bOverrideBufferTime, uint32_t bufferTime, double *percent) // percentage downloaded [out] { int32_t now, lastUpdate; int bufferSize = 64 * 1024; - char *buffer = (char *) malloc(bufferSize); + char *buffer; int nRead = 0; off_t size = ftello(file); unsigned long lastPercent = 0; @@ -491,6 +492,8 @@ Download(RTMP * rtmp, // connected RTMP bResume ? "Resuming" : "Starting", (double) size / 1024.0); } + if (bRealtimeStream) + RTMP_LogPrintf(" in approximately realtime (disabled BUFX speedup hack)\n"); } if (dStopOffset > 0) @@ -505,6 +508,8 @@ Download(RTMP * rtmp, // connected RTMP rtmp->m_read.nMetaHeaderSize = nMetaHeaderSize; rtmp->m_read.nInitialFrameSize = nInitialFrameSize; + buffer = (char *) malloc(bufferSize); + now = RTMP_GetTime(); lastUpdate = now - 1000; do @@ -575,15 +580,17 @@ Download(RTMP * rtmp, // connected RTMP } } } -#ifdef _DEBUG else { +#ifdef _DEBUG RTMP_Log(RTMP_LOGDEBUG, "zero read!"); - } #endif + if (rtmp->m_read.status == RTMP_READ_EOF) + break; + } } - while (!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp)); + while (!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp) && !RTMP_IsTimedout(rtmp)); free(buffer); if (nRead < 0) nRead = rtmp->m_read.status; @@ -635,6 +642,8 @@ void usage(char *prog) ("\n%s: This program dumps the media content streamed over RTMP.\n\n", prog); RTMP_LogPrintf("--help|-h Prints this help screen.\n"); RTMP_LogPrintf + ("--url|-i url URL with options included (e.g. rtmp://host[:port]/path swfUrl=url tcUrl=url)\n"); + RTMP_LogPrintf ("--rtmp|-r url URL (e.g. rtmp://host[:port]/path)\n"); RTMP_LogPrintf ("--host|-n hostname Overrides the hostname in the rtmp url\n"); @@ -679,11 +688,13 @@ void usage(char *prog) RTMP_LogPrintf ("--subscribe|-d string Stream name to subscribe to (otherwise defaults to playpath if live is specifed)\n"); RTMP_LogPrintf + ("--realtime|-R Don't attempt to speed up download via the Pause/Unpause BUFX hack\n"); + RTMP_LogPrintf ("--flv|-o string FLV output file name, if the file name is - print stream to stdout\n"); RTMP_LogPrintf ("--resume|-e Resume a partial RTMP download\n"); RTMP_LogPrintf - ("--timeout|-m num Timeout connection num seconds (default: %lu)\n", + ("--timeout|-m num Timeout connection num seconds (default: %u)\n", DEF_TIMEOUT); RTMP_LogPrintf ("--start|-A num Start at num seconds into stream (not valid when using --live)\n"); @@ -692,9 +703,11 @@ 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", + ("--buffer|-b Buffer time in milliseconds (default: %u)\n", DEF_BUFTIME); RTMP_LogPrintf ("--skip|-k num Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: %d)\n\n", @@ -738,10 +751,12 @@ 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; int bLiveStream = FALSE; // is it a live stream? then we can't seek/resume + int bRealtimeStream = FALSE; // If true, disable the BUFX hack (be patient) int bHashes = FALSE; // display byte counters not hashes by default long int timeout = DEF_TIMEOUT; // timeout connection after 120 seconds @@ -749,6 +764,7 @@ main(int argc, char **argv) uint32_t dStopOffset = 0; RTMP rtmp = { 0 }; + AVal fullUrl = { 0, 0 }; AVal swfUrl = { 0, 0 }; AVal tcUrl = { 0, 0 }; AVal pageUrl = { 0, 0 }; @@ -811,6 +827,7 @@ main(int argc, char **argv) {"protocol", 1, NULL, 'l'}, {"playpath", 1, NULL, 'y'}, {"playlist", 0, NULL, 'Y'}, + {"url", 1, NULL, 'i'}, {"rtmp", 1, NULL, 'r'}, {"swfUrl", 1, NULL, 's'}, {"tcUrl", 1, NULL, 't'}, @@ -826,6 +843,7 @@ main(int argc, char **argv) #endif {"flashVer", 1, NULL, 'f'}, {"live", 0, NULL, 'v'}, + {"realtime", 0, NULL, 'R'}, {"flv", 1, NULL, 'o'}, {"resume", 0, NULL, 'e'}, {"timeout", 1, NULL, 'm'}, @@ -839,12 +857,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:#", + "hVveqzRr:s:t:i: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) @@ -929,6 +948,9 @@ main(int argc, char **argv) case 'v': bLiveStream = TRUE; // no seeking or resuming possible! break; + case 'R': + bRealtimeStream = TRUE; // seeking and resuming is still possible + break; case 'd': STR2AVAL(subscribepath, optarg); break; @@ -984,6 +1006,9 @@ main(int argc, char **argv) } break; } + case 'i': + STR2AVAL(fullUrl, optarg); + break; case 's': STR2AVAL(swfUrl, optarg); break; @@ -1051,6 +1076,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]); @@ -1059,32 +1087,32 @@ main(int argc, char **argv) } } - if (!hostname.av_len) + if (!hostname.av_len && !fullUrl.av_len) { RTMP_Log(RTMP_LOGERROR, "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname"); return RD_FAILED; } - if (playpath.av_len == 0) + if (playpath.av_len == 0 && !fullUrl.av_len) { RTMP_Log(RTMP_LOGERROR, "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath"); return RD_FAILED; } - if (protocol == RTMP_PROTOCOL_UNDEFINED) + if (protocol == RTMP_PROTOCOL_UNDEFINED && !fullUrl.av_len) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP"); protocol = RTMP_PROTOCOL_RTMP; } - if (port == -1) + if (port == -1 && !fullUrl.av_len) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a port (--port) or rtmp url (-r), using default port 1935"); port = 0; } - if (port == 0) + if (port == 0 && !fullUrl.av_len) { if (protocol & RTMP_FEATURE_SSL) port = 443; @@ -1142,13 +1170,14 @@ main(int argc, char **argv) if (tcUrl.av_len == 0) { - char str[512] = { 0 }; - - tcUrl.av_len = snprintf(str, 511, "%s://%.*s:%d/%.*s", + tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) + + hostname.av_len + app.av_len + sizeof("://:65535/"); + tcUrl.av_val = (char *) malloc(tcUrl.av_len); + if (!tcUrl.av_val) + return RD_FAILED; + tcUrl.av_len = snprintf(tcUrl.av_val, tcUrl.av_len, "%s://%.*s:%d/%.*s", RTMPProtocolStringsLower[protocol], hostname.av_len, hostname.av_val, port, app.av_len, app.av_val); - tcUrl.av_val = (char *) malloc(tcUrl.av_len + 1); - strcpy(tcUrl.av_val, str); } int first = 1; @@ -1165,12 +1194,23 @@ 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); + if (!fullUrl.av_len) + { + RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath, + &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, + &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout); + } + else + { + if (RTMP_SetupURL(&rtmp, fullUrl.av_val) == FALSE) + { + RTMP_Log(RTMP_LOGERROR, "Couldn't parse URL: %s", fullUrl.av_val); + return RD_FAILED; + } + } /* Try to keep the stream moving if it pauses on us */ - if (!bLiveStream && !(protocol & RTMP_FEATURE_HTTP)) + if (!bLiveStream && !bRealtimeStream && !(protocol & RTMP_FEATURE_HTTP)) rtmp.Link.lFlags |= RTMP_LF_BUFX; off_t size = 0; @@ -1244,7 +1284,7 @@ main(int argc, char **argv) if (!RTMP_Connect(&rtmp, NULL)) { - nStatus = RD_FAILED; + nStatus = RD_NO_CONNECT; break; } @@ -1337,8 +1377,8 @@ main(int argc, char **argv) nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume, metaHeader, nMetaHeaderSize, initialFrame, - initialFrameType, nInitialFrameSize, - nSkipKeyFrames, bStdoutMode, bLiveStream, bHashes, + initialFrameType, nInitialFrameSize, nSkipKeyFrames, + bStdoutMode, bLiveStream, bRealtimeStream, bHashes, bOverrideBufferTime, bufferTime, &percent); free(initialFrame); initialFrame = NULL; diff '--color=auto' -Naurp rtmpdump-2.3/rtmpgw.8 rtmpdump/rtmpgw.8 --- rtmpdump-2.3/rtmpgw.8 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/rtmpgw.8 2026-06-19 14:15:18.304594991 -0600 @@ -1,5 +1,5 @@ -.TH RTMPGW 8 "2010-05-02" "RTMPDump v2.2e" -.\" Copyright 2010 Howard Chu. +.TH RTMPGW 8 "2011-07-20" "RTMPDump v2.4" +.\" Copyright 2011 Howard Chu. .\" Copying permitted according to the GNU General Public License V2. .SH NAME rtmpgw \- RTMP streaming media gateway @@ -50,6 +50,8 @@ rtmpgw \- RTMP streaming media gateway [\c .BI \-T \ key\fR] [\c +.BI \-j \ JSON\fR] +[\c .BI \-w \ swfHash\fR] [\c .BI \-x \ swfSize\fR] @@ -193,6 +195,9 @@ These options handle additional authenti Key for SecureToken response, used if the server requires SecureToken authentication. .TP +\fB\-\-jtv \-j\fP\ \fIJSON\fP +JSON token used by legacy Justin.tv servers. Invokes NetStream.Authenticate.UsherToken +.TP \fB\-\-swfhash \-w\fP\ \fIhexstring\fP SHA256 hash of the decompressed SWF file. This option may be needed if the server uses SWF Verification, but see the diff '--color=auto' -Naurp rtmpdump-2.3/rtmpgw.8.html rtmpdump/rtmpgw.8.html --- rtmpdump-2.3/rtmpgw.8.html 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/rtmpgw.8.html 2026-06-19 14:15:18.304594991 -0600 @@ -6,10 +6,10 @@ RTMPGW(8)RTMPGW(8) -RTMPDump v2.2e2010-05-02RTMPGW(8) +RTMPDump v2.42011-07-20RTMPGW(8)

      -
    @@ -41,6 +41,7 @@ rtmpgw − RTMP streaming media gate [−b buffer] [−m timeout] [−T key] +[−j JSON] [−w swfHash] [−x swfSize] [−W swfUrl] @@ -249,6 +250,12 @@ authentication.

    +−−jtv −j JSON +
    +JSON token used by legacy Justin.tv servers. Invokes NetStream.Authenticate.UsherToken +
    +

    +

    −−swfhash −w hexstring
    SHA256 hash of the decompressed SWF file. This option may be needed if diff '--color=auto' -Naurp rtmpdump-2.3/rtmpgw.c rtmpdump/rtmpgw.c --- rtmpdump-2.3/rtmpgw.c 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/rtmpgw.c 2026-06-19 14:15:18.304594991 -0600 @@ -85,6 +85,7 @@ typedef struct uint32_t bufferTime; char *rtmpurl; + AVal fullUrl; AVal playpath; AVal swfUrl; AVal tcUrl; @@ -95,6 +96,7 @@ typedef struct AVal flashVer; AVal token; AVal subscribepath; + AVal usherToken; //Justin.tv auth token AVal sockshost; AMFObject extras; int edepth; @@ -468,14 +470,14 @@ void processTCPrequest(STREAMING_SERVER } // do necessary checks right here to make sure the combined request of default values and GET parameters is correct - if (!req.hostname.av_len) + if (!req.hostname.av_len && !req.fullUrl.av_len) { RTMP_Log(RTMP_LOGERROR, "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname"); status = "400 Missing Hostname"; goto filenotfound; } - if (req.playpath.av_len == 0) + if (req.playpath.av_len == 0 && !req.fullUrl.av_len) { RTMP_Log(RTMP_LOGERROR, "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath"); @@ -483,19 +485,19 @@ void processTCPrequest(STREAMING_SERVER goto filenotfound;; } - if (req.protocol == RTMP_PROTOCOL_UNDEFINED) + if (req.protocol == RTMP_PROTOCOL_UNDEFINED && !req.fullUrl.av_len) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP"); req.protocol = RTMP_PROTOCOL_RTMP; } - if (req.rtmpport == -1) + if (req.rtmpport == -1 && !req.fullUrl.av_len) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a port (--port) or rtmp url (-r), using default port"); req.rtmpport = 0; } - if (req.rtmpport == 0) + if (req.rtmpport == 0 && !req.fullUrl.av_len) { if (req.protocol & RTMP_FEATURE_SSL) req.rtmpport = 443; @@ -551,9 +553,20 @@ void processTCPrequest(STREAMING_SERVER RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", req.bufferTime); 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.bLiveStream, req.timeout); + if (!req.fullUrl.av_len) + { + 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, &req.usherToken, dSeek, req.dStopOffset, + req.bLiveStream, req.timeout); + } + else + { + if (RTMP_SetupURL(&rtmp, req.fullUrl.av_val) == FALSE) + { + RTMP_Log(RTMP_LOGERROR, "Couldn't parse URL: %s", req.fullUrl.av_val); + return; + } + } /* backward compatibility, we always sent this as true before */ if (req.auth.av_len) rtmp.Link.lFlags |= RTMP_LF_AUTH; @@ -562,7 +575,7 @@ void processTCPrequest(STREAMING_SERVER rtmp.Link.token = req.token; rtmp.m_read.timestamp = dSeek; - RTMP_LogPrintf("Connecting ... port: %d, app: %s\n", req.rtmpport, req.app); + RTMP_LogPrintf("Connecting ... port: %d, app: %s\n", req.rtmpport, req.app.av_val); if (!RTMP_Connect(&rtmp, NULL)) { RTMP_LogPrintf("%s, failed to connect!\n", __FUNCTION__); @@ -737,7 +750,7 @@ stopStreaming(STREAMING_SERVER * server) if (closesocket(server->socket)) RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d", - GetSockError()); + __FUNCTION__, GetSockError()); server->state = STREAMING_STOPPED; } @@ -907,6 +920,9 @@ ParseOption(char opt, char *arg, RTMP_RE } break; } + case 'i': + STR2AVAL(req->fullUrl, arg); + break; case 's': STR2AVAL(req->swfUrl, arg); break; @@ -926,7 +942,7 @@ ParseOption(char opt, char *arg, RTMP_RE STR2AVAL(req->auth, arg); break; case 'C': - parseAMF(&req->extras, optarg, &req->edepth); + parseAMF(&req->extras, arg, &req->edepth); break; case 'm': req->timeout = atoi(arg); @@ -953,6 +969,9 @@ ParseOption(char opt, char *arg, RTMP_RE 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; @@ -989,6 +1008,7 @@ main(int argc, char **argv) int opt; struct option longopts[] = { {"help", 0, NULL, 'h'}, + {"url", 1, NULL, 'i'}, {"host", 1, NULL, 'n'}, {"port", 1, NULL, 'c'}, {"socks", 1, NULL, 'S'}, @@ -1023,6 +1043,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 +1056,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:i: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) @@ -1045,6 +1066,8 @@ main(int argc, char **argv) ("\nThis program serves media content streamed from RTMP onto HTTP.\n\n"); RTMP_LogPrintf("--help|-h Prints this help screen.\n"); RTMP_LogPrintf + ("--url|-i url URL with options included (e.g. rtmp://host[:port]/path swfUrl=url tcUrl=url)\n"); + RTMP_LogPrintf ("--rtmp|-r url URL (e.g. rtmp://host[:port]/path)\n"); RTMP_LogPrintf ("--host|-n hostname Overrides the hostname in the rtmp url\n"); @@ -1096,7 +1119,9 @@ main(int argc, char **argv) RTMP_LogPrintf ("--token|-T key Key for SecureToken response\n"); RTMP_LogPrintf - ("--buffer|-b Buffer time in milliseconds (default: %lu)\n\n", + ("--jtv|-j JSON Authentication token for Justin.tv legacy servers\n"); + RTMP_LogPrintf + ("--buffer|-b Buffer time in milliseconds (default: %u)\n\n", defaultRTMPRequest.bufferTime); RTMP_LogPrintf diff '--color=auto' -Naurp rtmpdump-2.3/rtmpsrv.c rtmpdump/rtmpsrv.c --- rtmpdump-2.3/rtmpsrv.c 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/rtmpsrv.c 2026-06-19 14:15:18.304594991 -0600 @@ -1,6 +1,6 @@ /* Simple RTMP Server * Copyright (C) 2009 Andrej Stepanchuk - * Copyright (C) 2009 Howard Chu + * Copyright (C) 2009-2011 Howard Chu * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -92,9 +92,14 @@ typedef struct } STREAMING_SERVER; STREAMING_SERVER *rtmpServer = 0; // server structure pointer +void *sslCtx = NULL; STREAMING_SERVER *startStreaming(const char *address, int port); void stopStreaming(STREAMING_SERVER * server); +void AVreplace(AVal *src, const AVal *orig, const AVal *repl); + +static const AVal av_dquote = AVC("\""); +static const AVal av_escdquote = AVC("\\\""); typedef struct { @@ -175,7 +180,7 @@ SendConnectResult(RTMP *r, double txn) packet.m_nChannel = 0x03; // control channel (invoke) packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */ - packet.m_packetType = 0x14; // INVOKE + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -219,9 +224,6 @@ SendConnectResult(RTMP *r, double txn) *enc++ = 0; *enc++ = 0; *enc++ = AMF_OBJECT_END; - *enc++ = 0; - *enc++ = 0; - *enc++ = AMF_OBJECT_END; packet.m_nBodySize = enc - packet.m_body; @@ -236,7 +238,7 @@ SendResultNumber(RTMP *r, double txn, do packet.m_nChannel = 0x03; // control channel (invoke) packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */ - packet.m_packetType = 0x14; // INVOKE + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -261,16 +263,17 @@ static const AVal av_NetStream_Play_Stop static const AVal av_Stopped_playing = AVC("Stopped playing"); SAVC(details); SAVC(clientid); +static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken"); static int SendPlayStart(RTMP *r) { RTMPPacket packet; - char pbuf[384], *pend = pbuf+sizeof(pbuf); + char pbuf[512], *pend = pbuf+sizeof(pbuf); packet.m_nChannel = 0x03; // control channel (invoke) packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */ - packet.m_packetType = 0x14; // INVOKE + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -298,11 +301,11 @@ static int SendPlayStop(RTMP *r) { RTMPPacket packet; - char pbuf[384], *pend = pbuf+sizeof(pbuf); + char pbuf[512], *pend = pbuf+sizeof(pbuf); packet.m_nChannel = 0x03; // control channel (invoke) packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */ - packet.m_packetType = 0x14; // INVOKE + packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; packet.m_nTimeStamp = 0; packet.m_nInfoField2 = 0; packet.m_hasAbsTimestamp = 0; @@ -401,10 +404,10 @@ countAMF(AMFObject *obj, int *argc) static char * dumpAMF(AMFObject *obj, char *ptr, AVal *argv, int *argc) { - int i, len, ac = *argc; + int i, ac = *argc; const char opt[] = "NBSO Z"; - for (i=0, len=0; i < obj->o_num; i++) + for (i=0; i < obj->o_num; i++) { AMFObjectProperty *p = &obj->o_props[i]; argv[ac].av_val = ptr+1; @@ -575,6 +578,15 @@ ServeInvoke(STREAMING_SERVER *server, RT { SendResultNumber(r, txn, 10.0); } + else if (AVMATCH(&method, &av_NetStream_Authenticate_UsherToken)) + { + AVal usherToken; + AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken); + AVreplace(&usherToken, &av_dquote, &av_escdquote); + server->arglen += 6 + usherToken.av_len; + server->argc += 2; + r->Link.usherToken = usherToken; + } else if (AVMATCH(&method, &av_play)) { char *file, *p, *q, *cmd, *ptr; @@ -583,6 +595,8 @@ ServeInvoke(STREAMING_SERVER *server, RT uint32_t now; RTMPPacket pc = {0}; AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &r->Link.playpath); + if (!r->Link.playpath.av_len) + return 0; /* r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4)); if (obj.o_num > 5) @@ -640,6 +654,17 @@ ServeInvoke(STREAMING_SERVER *server, RT ptr += sprintf(ptr, " -p \"%s\"", r->Link.pageUrl.av_val); argv[argc++].av_len = r->Link.pageUrl.av_len; } + if (r->Link.usherToken.av_val) + { + argv[argc].av_val = ptr + 1; + argv[argc++].av_len = 2; + argv[argc].av_val = ptr + 5; + ptr += sprintf(ptr, " -j \"%s\"", r->Link.usherToken.av_val); + argv[argc++].av_len = r->Link.usherToken.av_len; + free(r->Link.usherToken.av_val); + r->Link.usherToken.av_val = NULL; + r->Link.usherToken.av_len = 0; + } if (r->Link.extras.o_num) { ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc); AMF_Reset(&r->Link.extras); @@ -740,54 +765,47 @@ ServePacket(STREAMING_SERVER *server, RT { int ret = 0; - RTMP_Log(RTMP_LOGDEBUG, "%s, received packet type %02X, size %lu bytes", __FUNCTION__, + RTMP_Log(RTMP_LOGDEBUG, "%s, received packet type %02X, size %u bytes", __FUNCTION__, packet->m_packetType, packet->m_nBodySize); switch (packet->m_packetType) { - case 0x01: - // chunk size + case RTMP_PACKET_TYPE_CHUNK_SIZE: // HandleChangeChunkSize(r, packet); break; - case 0x03: - // bytes read report + case RTMP_PACKET_TYPE_BYTES_READ_REPORT: break; - case 0x04: - // ctrl + case RTMP_PACKET_TYPE_CONTROL: // HandleCtrl(r, packet); break; - case 0x05: - // server bw + case RTMP_PACKET_TYPE_SERVER_BW: // HandleServerBW(r, packet); break; - case 0x06: - // client bw + case RTMP_PACKET_TYPE_CLIENT_BW: // HandleClientBW(r, packet); break; - case 0x08: - // audio data + case RTMP_PACKET_TYPE_AUDIO: //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); break; - case 0x09: - // video data + case RTMP_PACKET_TYPE_VIDEO: //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); break; - case 0x0F: // flex stream send + case RTMP_PACKET_TYPE_FLEX_STREAM_SEND: break; - case 0x10: // flex shared object + case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT: break; - case 0x11: // flex message + case RTMP_PACKET_TYPE_FLEX_MESSAGE: { - RTMP_Log(RTMP_LOGDEBUG, "%s, flex message, size %lu bytes, not fully supported", + RTMP_Log(RTMP_LOGDEBUG, "%s, flex message, size %u bytes, not fully supported", __FUNCTION__, packet->m_nBodySize); //RTMP_LogHex(packet.m_body, packet.m_nBodySize); @@ -805,17 +823,14 @@ ServePacket(STREAMING_SERVER *server, RT RTMP_Close(r); break; } - case 0x12: - // metadata (notify) + case RTMP_PACKET_TYPE_INFO: break; - case 0x13: - /* shared object */ + case RTMP_PACKET_TYPE_SHARED_OBJECT: break; - case 0x14: - // invoke - RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__, + case RTMP_PACKET_TYPE_INVOKE: + RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__, packet->m_nBodySize); //RTMP_LogHex(packet.m_body, packet.m_nBodySize); @@ -823,8 +838,7 @@ ServePacket(STREAMING_SERVER *server, RT RTMP_Close(r); break; - case 0x16: - /* flv */ + case RTMP_PACKET_TYPE_FLASH_VIDEO: break; default: RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__, @@ -864,7 +878,7 @@ void doServe(STREAMING_SERVER * server, { server->state = STREAMING_IN_PROGRESS; - RTMP rtmp = { 0 }; /* our session with the real client */ + RTMP *rtmp = RTMP_Alloc(); /* our session with the real client */ RTMPPacket packet = { 0 }; // timeout for http requests @@ -884,33 +898,44 @@ void doServe(STREAMING_SERVER * server, } else { - RTMP_Init(&rtmp); - rtmp.m_sb.sb_socket = sockfd; - if (!RTMP_Serve(&rtmp)) + RTMP_Init(rtmp); + rtmp->m_sb.sb_socket = sockfd; + if (sslCtx && !RTMP_TLS_Accept(rtmp, sslCtx)) + { + RTMP_Log(RTMP_LOGERROR, "TLS handshake failed"); + goto cleanup; + } + if (!RTMP_Serve(rtmp)) { RTMP_Log(RTMP_LOGERROR, "Handshake failed"); goto cleanup; } } server->arglen = 0; - while (RTMP_IsConnected(&rtmp) && RTMP_ReadPacket(&rtmp, &packet)) + while (RTMP_IsConnected(rtmp) && RTMP_ReadPacket(rtmp, &packet)) { if (!RTMPPacket_IsReady(&packet)) continue; - ServePacket(server, &rtmp, &packet); + ServePacket(server, rtmp, &packet); RTMPPacket_Free(&packet); } cleanup: RTMP_LogPrintf("Closing connection... "); - RTMP_Close(&rtmp); + RTMP_Close(rtmp); /* Should probably be done by RTMP_Close() ... */ - rtmp.Link.playpath.av_val = NULL; - rtmp.Link.tcUrl.av_val = NULL; - rtmp.Link.swfUrl.av_val = NULL; - rtmp.Link.pageUrl.av_val = NULL; - rtmp.Link.app.av_val = NULL; - rtmp.Link.flashVer.av_val = NULL; + rtmp->Link.playpath.av_val = NULL; + rtmp->Link.tcUrl.av_val = NULL; + rtmp->Link.swfUrl.av_val = NULL; + rtmp->Link.pageUrl.av_val = NULL; + rtmp->Link.app.av_val = NULL; + rtmp->Link.flashVer.av_val = NULL; + if (rtmp->Link.usherToken.av_val) + { + free(rtmp->Link.usherToken.av_val); + rtmp->Link.usherToken.av_val = NULL; + } + RTMP_Free(rtmp); RTMP_LogPrintf("done!\n\n"); quit: @@ -1023,7 +1048,7 @@ stopStreaming(STREAMING_SERVER * server) if (closesocket(server->socket)) RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d", - GetSockError()); + __FUNCTION__, GetSockError()); server->state = STREAMING_STOPPED; } @@ -1044,20 +1069,32 @@ int main(int argc, char **argv) { int nStatus = RD_SUCCESS; + int i; // http streaming server char DEFAULT_HTTP_STREAMING_DEVICE[] = "0.0.0.0"; // 0.0.0.0 is any device char *rtmpStreamingDevice = DEFAULT_HTTP_STREAMING_DEVICE; // streaming device, default 0.0.0.0 int nRtmpStreamingPort = 1935; // port + char *cert = NULL, *key = NULL; RTMP_LogPrintf("RTMP Server %s\n", RTMPDUMP_VERSION); RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n"); RTMP_debuglevel = RTMP_LOGINFO; - if (argc > 1 && !strcmp(argv[1], "-z")) - RTMP_debuglevel = RTMP_LOGALL; + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-z")) + RTMP_debuglevel = RTMP_LOGALL; + else if (!strcmp(argv[i], "-c") && i + 1 < argc) + cert = argv[++i]; + else if (!strcmp(argv[i], "-k") && i + 1 < argc) + key = argv[++i]; + } + + if (cert && key) + sslCtx = RTMP_TLS_AllocServerContext(cert, key); // init request memset(&defaultRTMPRequest, 0, sizeof(RTMP_REQUEST)); @@ -1101,6 +1138,9 @@ main(int argc, char **argv) } RTMP_Log(RTMP_LOGDEBUG, "Done, exiting..."); + if (sslCtx) + RTMP_TLS_FreeServerContext(sslCtx); + CleanupSockets(); #ifdef _DEBUG @@ -1111,3 +1151,43 @@ main(int argc, char **argv) #endif return nStatus; } + +void +AVreplace(AVal *src, const AVal *orig, const AVal *repl) +{ + char *srcbeg = src->av_val; + char *srcend = src->av_val + src->av_len; + char *dest, *sptr, *dptr; + int n = 0; + + /* count occurrences of orig in src */ + sptr = src->av_val; + while (sptr < srcend && (sptr = strstr(sptr, orig->av_val))) + { + n++; + sptr += orig->av_len; + } + if (!n) + return; + + dest = malloc(src->av_len + 1 + (repl->av_len - orig->av_len) * n); + + sptr = src->av_val; + dptr = dest; + while (sptr < srcend && (sptr = strstr(sptr, orig->av_val))) + { + n = sptr - srcbeg; + memcpy(dptr, srcbeg, n); + dptr += n; + memcpy(dptr, repl->av_val, repl->av_len); + dptr += repl->av_len; + sptr += orig->av_len; + srcbeg = sptr; + } + n = srcend - srcbeg; + memcpy(dptr, srcbeg, n); + dptr += n; + *dptr = '\0'; + src->av_val = dest; + src->av_len = dptr - dest; +} diff '--color=auto' -Naurp rtmpdump-2.3/rtmpsuck.c rtmpdump/rtmpsuck.c --- rtmpdump-2.3/rtmpsuck.c 2010-06-30 13:58:35.000000000 -0600 +++ rtmpdump/rtmpsuck.c 2026-06-19 14:15:18.305595000 -0600 @@ -456,72 +456,76 @@ ServePacket(STREAMING_SERVER *server, in { int ret = 0; - RTMP_Log(RTMP_LOGDEBUG, "%s, %s sent packet type %02X, size %lu bytes", __FUNCTION__, + RTMP_Log(RTMP_LOGDEBUG, "%s, %s sent packet type %02X, size %u bytes", __FUNCTION__, cst[which], packet->m_packetType, packet->m_nBodySize); switch (packet->m_packetType) { - case 0x01: + case RTMP_PACKET_TYPE_CHUNK_SIZE: // chunk size // HandleChangeChunkSize(r, packet); break; - case 0x03: + case RTMP_PACKET_TYPE_BYTES_READ_REPORT: // bytes read report break; - case 0x04: + case RTMP_PACKET_TYPE_CONTROL: // ctrl // HandleCtrl(r, packet); break; - case 0x05: + case RTMP_PACKET_TYPE_SERVER_BW: // server bw // HandleServerBW(r, packet); break; - case 0x06: + case RTMP_PACKET_TYPE_CLIENT_BW: // client bw // HandleClientBW(r, packet); break; - case 0x08: + case RTMP_PACKET_TYPE_AUDIO: // audio data //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); break; - case 0x09: + case RTMP_PACKET_TYPE_VIDEO: // video data //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); break; - case 0x0F: // flex stream send + case RTMP_PACKET_TYPE_FLEX_STREAM_SEND: + // flex stream send break; - case 0x10: // flex shared object + case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT: + // flex shared object break; - case 0x11: // flex message + case RTMP_PACKET_TYPE_FLEX_MESSAGE: + // flex message { ret = ServeInvoke(server, which, packet, packet->m_body + 1); break; } - case 0x12: + case RTMP_PACKET_TYPE_INFO: // metadata (notify) break; - case 0x13: + case RTMP_PACKET_TYPE_SHARED_OBJECT: /* shared object */ break; - case 0x14: + case RTMP_PACKET_TYPE_INVOKE: // invoke ret = ServeInvoke(server, which, packet, packet->m_body); break; - case 0x16: + case RTMP_PACKET_TYPE_FLASH_VIDEO: /* flv */ break; + default: RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__, packet->m_packetType); @@ -547,21 +551,21 @@ WriteStream(char **buf, // target pointe unsigned int nPacketLen = packet->m_nBodySize; // skip video info/command packets - if (packet->m_packetType == 0x09 && + if (packet->m_packetType == RTMP_PACKET_TYPE_VIDEO && nPacketLen == 2 && ((*packetBody & 0xf0) == 0x50)) { ret = 0; break; } - if (packet->m_packetType == 0x09 && nPacketLen <= 5) + if (packet->m_packetType == RTMP_PACKET_TYPE_VIDEO && nPacketLen <= 5) { RTMP_Log(RTMP_LOGWARNING, "ignoring too small video packet: size: %d", nPacketLen); ret = 0; break; } - if (packet->m_packetType == 0x08 && nPacketLen <= 1) + if (packet->m_packetType == RTMP_PACKET_TYPE_AUDIO && nPacketLen <= 1) { RTMP_Log(RTMP_LOGWARNING, "ignoring too small audio packet: size: %d", nPacketLen); @@ -571,19 +575,22 @@ WriteStream(char **buf, // target pointe #ifdef _DEBUG RTMP_Log(RTMP_LOGDEBUG, "type: %02X, size: %d, TS: %d ms", packet->m_packetType, nPacketLen, packet->m_nTimeStamp); - if (packet->m_packetType == 0x09) + if (packet->m_packetType == RTMP_PACKET_TYPE_VIDEO) RTMP_Log(RTMP_LOGDEBUG, "frametype: %02X", (*packetBody & 0xf0)); #endif // calculate packet size and reallocate buffer if necessary unsigned int size = nPacketLen + - ((packet->m_packetType == 0x08 || packet->m_packetType == 0x09 - || packet->m_packetType == 0x12) ? 11 : 0) + (packet->m_packetType != - 0x16 ? 4 : 0); + ((packet->m_packetType == RTMP_PACKET_TYPE_AUDIO + || packet->m_packetType == RTMP_PACKET_TYPE_VIDEO + || packet->m_packetType == RTMP_PACKET_TYPE_INFO) ? 11 : 0) + + (packet->m_packetType != 0x16 ? 4 : 0); if (size + 4 > len) - { // the extra 4 is for the case of an FLV stream without a last prevTagSize (we need extra 4 bytes to append it) + { + /* The extra 4 is for the case of an FLV stream without a last + * prevTagSize (we need extra 4 bytes to append it). */ *buf = (char *) realloc(*buf, size + 4); if (*buf == 0) { @@ -594,13 +601,15 @@ WriteStream(char **buf, // target pointe } char *ptr = *buf, *pend = ptr + size+4; - // audio (0x08), video (0x09) or metadata (0x12) packets : - // construct 11 byte header then add rtmp packet's data - if (packet->m_packetType == 0x08 || packet->m_packetType == 0x09 - || packet->m_packetType == 0x12) + /* audio (RTMP_PACKET_TYPE_AUDIO), video (RTMP_PACKET_TYPE_VIDEO) + * or metadata (RTMP_PACKET_TYPE_INFO) packets: construct 11 byte + * header then add rtmp packet's data. */ + if (packet->m_packetType == RTMP_PACKET_TYPE_AUDIO + || packet->m_packetType == RTMP_PACKET_TYPE_VIDEO + || packet->m_packetType == RTMP_PACKET_TYPE_INFO) { // set data type - //*dataType |= (((packet->m_packetType == 0x08)<<2)|(packet->m_packetType == 0x09)); + //*dataType |= (((packet->m_packetType == RTMP_PACKET_TYPE_AUDIO)<<2)|(packet->m_packetType == RTMP_PACKET_TYPE_VIDEO)); (*nTimeStamp) = packet->m_nTimeStamp; prevTagSize = 11 + nPacketLen; @@ -619,7 +628,7 @@ WriteStream(char **buf, // target pointe unsigned int len = nPacketLen; // correct tagSize and obtain timestamp if we have an FLV stream - if (packet->m_packetType == 0x16) + if (packet->m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO) { unsigned int pos = 0; @@ -629,15 +638,18 @@ WriteStream(char **buf, // target pointe *nTimeStamp = AMF_DecodeInt24(packetBody + pos + 4); *nTimeStamp |= (packetBody[pos + 7] << 24); - // set data type - //*dataType |= (((*(packetBody+pos) == 0x08)<<2)|(*(packetBody+pos) == 0x09)); +#if 0 + /* set data type */ + *dataType |= (((*(packetBody+pos) == RTMP_PACKET_TYPE_AUDIO) << 2) + | (*(packetBody+pos) == RTMP_PACKET_TYPE_VIDEO)); +#endif if (pos + 11 + dataSize + 4 > nPacketLen) { if (pos + 11 + dataSize > nPacketLen) { RTMP_Log(RTMP_LOGERROR, - "Wrong data size (%lu), stream corrupted, aborting!", + "Wrong data size (%u), stream corrupted, aborting!", dataSize); ret = -2; break; @@ -680,7 +692,7 @@ WriteStream(char **buf, // target pointe } ptr += len; - if (packet->m_packetType != 0x16) + if (packet->m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO) { // FLV tag packets contain their own prevTagSize AMF_EncodeInt32(ptr, pend, prevTagSize); //ptr += 4; @@ -718,15 +730,15 @@ controlServerThread(void *unused) TFRET(); } -void doServe(STREAMING_SERVER * server, // server socket and state (our listening socket) - int sockfd // client connection socket - ) +TFTYPE doServe(void *arg) // server socket and state (our listening socket) { + STREAMING_SERVER *server = arg; RTMPPacket pc = { 0 }, ps = { 0 }; RTMPChunk rk = { 0 }; char *buf = NULL; unsigned int buflen = 131072; int paused = FALSE; + int sockfd = server->socket; // timeout for http requests fd_set rfds; @@ -828,7 +840,7 @@ void doServe(STREAMING_SERVER * server, if (RTMPPacket_IsReady(&ps)) { /* change chunk size */ - if (ps.m_packetType == 0x01) + if (ps.m_packetType == RTMP_PACKET_TYPE_CHUNK_SIZE) { if (ps.m_nBodySize >= 4) { @@ -839,7 +851,7 @@ void doServe(STREAMING_SERVER * server, } } /* bytes received */ - else if (ps.m_packetType == 0x03) + else if (ps.m_packetType == RTMP_PACKET_TYPE_BYTES_READ_REPORT) { if (ps.m_nBodySize >= 4) { @@ -849,7 +861,7 @@ void doServe(STREAMING_SERVER * server, } } /* ctrl */ - else if (ps.m_packetType == 0x04) + else if (ps.m_packetType == RTMP_PACKET_TYPE_CONTROL) { short nType = AMF_DecodeInt16(ps.m_body); /* UpdateBufferMS */ @@ -875,13 +887,16 @@ void doServe(STREAMING_SERVER * server, } } } - else if (ps.m_packetType == 0x11 || ps.m_packetType == 0x14) - if (ServePacket(server, 0, &ps) && server->f_cur) - { - fclose(server->f_cur->f_file); - server->f_cur->f_file = NULL; - server->f_cur = NULL; - } + else if (ps.m_packetType == RTMP_PACKET_TYPE_FLEX_MESSAGE + || ps.m_packetType == RTMP_PACKET_TYPE_INVOKE) + { + if (ServePacket(server, 0, &ps) && server->f_cur) + { + fclose(server->f_cur->f_file); + server->f_cur->f_file = NULL; + server->f_cur = NULL; + } + } RTMP_SendPacket(&server->rc, &ps, FALSE); RTMPPacket_Free(&ps); break; @@ -902,7 +917,7 @@ void doServe(STREAMING_SERVER * server, server->rc.m_pausing = 0; } /* change chunk size */ - if (pc.m_packetType == 0x01) + if (pc.m_packetType == RTMP_PACKET_TYPE_CHUNK_SIZE) { if (pc.m_nBodySize >= 4) { @@ -912,7 +927,7 @@ void doServe(STREAMING_SERVER * server, server->rs.m_outChunkSize = server->rc.m_inChunkSize; } } - else if (pc.m_packetType == 0x04) + else if (pc.m_packetType == RTMP_PACKET_TYPE_CONTROL) { short nType = AMF_DecodeInt16(pc.m_body); /* SWFverification */ @@ -929,17 +944,18 @@ void doServe(STREAMING_SERVER * server, #endif } else if (server->f_cur && ( - pc.m_packetType == 0x08 || - pc.m_packetType == 0x09 || - pc.m_packetType == 0x12 || - pc.m_packetType == 0x16) && + pc.m_packetType == RTMP_PACKET_TYPE_AUDIO || + pc.m_packetType == RTMP_PACKET_TYPE_VIDEO || + pc.m_packetType == RTMP_PACKET_TYPE_INFO || + pc.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO) && RTMP_ClientPacket(&server->rc, &pc)) { int len = WriteStream(&buf, &buflen, &server->stamp, &pc); if (len > 0 && fwrite(buf, 1, len, server->f_cur->f_file) != len) goto cleanup; } - else if ( pc.m_packetType == 0x11 || pc.m_packetType == 0x14) + else if (pc.m_packetType == RTMP_PACKET_TYPE_FLEX_MESSAGE || + pc.m_packetType == RTMP_PACKET_TYPE_INVOKE) { if (ServePacket(server, 1, &pc) && server->f_cur) { @@ -990,7 +1006,7 @@ quit: if (server->state == STREAMING_IN_PROGRESS) server->state = STREAMING_ACCEPTING; - return; + TFRET(); } TFTYPE @@ -1003,6 +1019,7 @@ serverThread(void *arg) { struct sockaddr_in addr; socklen_t addrlen = sizeof(struct sockaddr_in); + STREAMING_SERVER *srv2 = malloc(sizeof(STREAMING_SERVER)); int sockfd = accept(server->socket, (struct sockaddr *) &addr, &addrlen); @@ -1020,8 +1037,10 @@ serverThread(void *arg) RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s\n", __FUNCTION__, inet_ntoa(addr.sin_addr)); #endif + *srv2 = *server; + srv2->socket = sockfd; /* Create a new thread and transfer the control to that */ - doServe(server, sockfd); + ThreadCreate(doServe, srv2); RTMP_Log(RTMP_LOGDEBUG, "%s: processed request\n", __FUNCTION__); } else @@ -1098,7 +1117,7 @@ stopStreaming(STREAMING_SERVER * server) if (fd && closesocket(fd)) RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d", - GetSockError()); + __FUNCTION__, GetSockError()); server->state = STREAMING_STOPPED; }