+ return rtmp_send_packet(rt, &pkt, 1);
+}
+
+/**
+ * Generate ping reply and send it to the server.
+ */
+static int gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
+{
+ RTMPPacket pkt;
+ uint8_t *p;
+ int ret;
+
+ if (ppkt->data_size < 6) {
+ av_log(s, AV_LOG_ERROR, "Too short ping packet (%d)\n",
+ ppkt->data_size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
+ ppkt->timestamp + 1, 6)) < 0)
+ return ret;
+
+ p = pkt.data;
+ bytestream_put_be16(&p, 7);
+ bytestream_put_be32(&p, AV_RB32(ppkt->data+2));
+
+ return rtmp_send_packet(rt, &pkt, 0);
+}
+
+/**
+ * Generate SWF verification message and send it to the server.
+ */
+static int gen_swf_verification(URLContext *s, RTMPContext *rt)
+{
+ RTMPPacket pkt;
+ uint8_t *p;
+ int ret;
+
+ av_log(s, AV_LOG_DEBUG, "Sending SWF verification...\n");
+ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
+ 0, 44)) < 0)
+ return ret;
+
+ p = pkt.data;
+ bytestream_put_be16(&p, 27);
+ memcpy(p, rt->swfverification, 42);
+
+ return rtmp_send_packet(rt, &pkt, 0);
+}
+
+/**
+ * Generate server bandwidth message and send it to the server.
+ */
+static int gen_server_bw(URLContext *s, RTMPContext *rt)
+{
+ RTMPPacket pkt;
+ uint8_t *p;
+ int ret;
+
+ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_SERVER_BW,
+ 0, 4)) < 0)
+ return ret;
+
+ p = pkt.data;
+ bytestream_put_be32(&p, rt->server_bw);
+
+ return rtmp_send_packet(rt, &pkt, 0);
+}
+
+/**
+ * Generate check bandwidth message and send it to the server.
+ */
+static int gen_check_bw(URLContext *s, RTMPContext *rt)
+{
+ RTMPPacket pkt;
+ uint8_t *p;
+ int ret;
+
+ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
+ 0, 21)) < 0)
+ return ret;
+
+ p = pkt.data;
+ ff_amf_write_string(&p, "_checkbw");
+ ff_amf_write_number(&p, ++rt->nb_invokes);
+ ff_amf_write_null(&p);
+
+ return rtmp_send_packet(rt, &pkt, 1);
+}
+
+/**
+ * Generate report on bytes read so far and send it to the server.
+ */
+static int gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts)
+{
+ RTMPPacket pkt;
+ uint8_t *p;
+ int ret;
+
+ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ,
+ ts, 4)) < 0)
+ return ret;
+
+ p = pkt.data;
+ bytestream_put_be32(&p, rt->bytes_read);
+
+ return rtmp_send_packet(rt, &pkt, 0);
+}
+
+static int gen_fcsubscribe_stream(URLContext *s, RTMPContext *rt,
+ const char *subscribe)
+{
+ RTMPPacket pkt;
+ uint8_t *p;
+ int ret;
+
+ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
+ 0, 27 + strlen(subscribe))) < 0)
+ return ret;
+
+ p = pkt.data;
+ ff_amf_write_string(&p, "FCSubscribe");
+ ff_amf_write_number(&p, ++rt->nb_invokes);
+ ff_amf_write_null(&p);
+ ff_amf_write_string(&p, subscribe);
+
+ return rtmp_send_packet(rt, &pkt, 1);
+}
+
+int ff_rtmp_calc_digest(const uint8_t *src, int len, int gap,
+ const uint8_t *key, int keylen, uint8_t *dst)
+{
+ struct AVSHA *sha;
+ uint8_t hmac_buf[64+32] = {0};
+ int i;
+
+ sha = av_sha_alloc();
+ if (!sha)
+ return AVERROR(ENOMEM);
+
+ if (keylen < 64) {
+ memcpy(hmac_buf, key, keylen);
+ } else {
+ av_sha_init(sha, 256);
+ av_sha_update(sha,key, keylen);
+ av_sha_final(sha, hmac_buf);
+ }
+ for (i = 0; i < 64; i++)
+ hmac_buf[i] ^= HMAC_IPAD_VAL;
+
+ av_sha_init(sha, 256);
+ av_sha_update(sha, hmac_buf, 64);
+ if (gap <= 0) {
+ av_sha_update(sha, src, len);
+ } else { //skip 32 bytes used for storing digest
+ av_sha_update(sha, src, gap);
+ av_sha_update(sha, src + gap + 32, len - gap - 32);
+ }
+ av_sha_final(sha, hmac_buf + 64);
+
+ for (i = 0; i < 64; i++)
+ hmac_buf[i] ^= HMAC_IPAD_VAL ^ HMAC_OPAD_VAL; //reuse XORed key for opad
+ av_sha_init(sha, 256);
+ av_sha_update(sha, hmac_buf, 64+32);
+ av_sha_final(sha, dst);
+
+ av_free(sha);
+
+ return 0;
+}
+
+int ff_rtmp_calc_digest_pos(const uint8_t *buf, int off, int mod_val,
+ int add_val)
+{
+ int i, digest_pos = 0;
+
+ for (i = 0; i < 4; i++)
+ digest_pos += buf[i + off];
+ digest_pos = digest_pos % mod_val + add_val;
+
+ return digest_pos;
+}
+
+/**
+ * Put HMAC-SHA2 digest of packet data (except for the bytes where this digest
+ * will be stored) into that packet.
+ *
+ * @param buf handshake data (1536 bytes)
+ * @param encrypted use an encrypted connection (RTMPE)
+ * @return offset to the digest inside input data
+ */
+static int rtmp_handshake_imprint_with_digest(uint8_t *buf, int encrypted)
+{
+ int ret, digest_pos;
+
+ if (encrypted)
+ digest_pos = ff_rtmp_calc_digest_pos(buf, 772, 728, 776);
+ else
+ digest_pos = ff_rtmp_calc_digest_pos(buf, 8, 728, 12);
+
+ ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
+ rtmp_player_key, PLAYER_KEY_OPEN_PART_LEN,
+ buf + digest_pos);
+ if (ret < 0)
+ return ret;
+
+ return digest_pos;
+}
+
+/**
+ * Verify that the received server response has the expected digest value.
+ *
+ * @param buf handshake data received from the server (1536 bytes)
+ * @param off position to search digest offset from
+ * @return 0 if digest is valid, digest position otherwise
+ */
+static int rtmp_validate_digest(uint8_t *buf, int off)
+{
+ uint8_t digest[32];
+ int ret, digest_pos;
+
+ digest_pos = ff_rtmp_calc_digest_pos(buf, off, 728, off + 4);
+
+ ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
+ rtmp_server_key, SERVER_KEY_OPEN_PART_LEN,
+ digest);
+ if (ret < 0)
+ return ret;
+
+ if (!memcmp(digest, buf + digest_pos, 32))
+ return digest_pos;
+ return 0;
+}
+
+static int rtmp_calc_swf_verification(URLContext *s, RTMPContext *rt,
+ uint8_t *buf)
+{
+ uint8_t *p;
+ int ret;
+
+ if (rt->swfhash_len != 32) {
+ av_log(s, AV_LOG_ERROR,
+ "Hash of the decompressed SWF file is not 32 bytes long.\n");
+ return AVERROR(EINVAL);
+ }
+
+ p = &rt->swfverification[0];
+ bytestream_put_byte(&p, 1);
+ bytestream_put_byte(&p, 1);
+ bytestream_put_be32(&p, rt->swfsize);
+ bytestream_put_be32(&p, rt->swfsize);
+
+ if ((ret = ff_rtmp_calc_digest(rt->swfhash, 32, 0, buf, 32, p)) < 0)
+ return ret;
+
+ return 0;
+}
+
+#if CONFIG_ZLIB
+static int rtmp_uncompress_swfplayer(uint8_t *in_data, int64_t in_size,
+ uint8_t **out_data, int64_t *out_size)
+{
+ z_stream zs = { 0 };
+ void *ptr;
+ int size;
+ int ret = 0;
+
+ zs.avail_in = in_size;
+ zs.next_in = in_data;
+ ret = inflateInit(&zs);
+ if (ret != Z_OK)
+ return AVERROR_UNKNOWN;
+
+ do {
+ uint8_t tmp_buf[16384];
+
+ zs.avail_out = sizeof(tmp_buf);
+ zs.next_out = tmp_buf;
+
+ ret = inflate(&zs, Z_NO_FLUSH);
+ if (ret != Z_OK && ret != Z_STREAM_END) {
+ ret = AVERROR_UNKNOWN;
+ goto fail;
+ }
+
+ size = sizeof(tmp_buf) - zs.avail_out;
+ if (!(ptr = av_realloc(*out_data, *out_size + size))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ *out_data = ptr;
+
+ memcpy(*out_data + *out_size, tmp_buf, size);
+ *out_size += size;
+ } while (zs.avail_out == 0);
+
+fail:
+ inflateEnd(&zs);
+ return ret;
+}
+#endif
+
+static int rtmp_calc_swfhash(URLContext *s)
+{
+ RTMPContext *rt = s->priv_data;
+ uint8_t *in_data = NULL, *out_data = NULL, *swfdata;
+ int64_t in_size, out_size;
+ URLContext *stream;
+ char swfhash[32];
+ int swfsize;
+ int ret = 0;
+
+ /* Get the SWF player file. */
+ if ((ret = ffurl_open(&stream, rt->swfverify, AVIO_FLAG_READ,
+ &s->interrupt_callback, NULL)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Cannot open connection %s.\n", rt->swfverify);
+ goto fail;
+ }
+
+ if ((in_size = ffurl_seek(stream, 0, AVSEEK_SIZE)) < 0) {
+ ret = AVERROR(EIO);
+ goto fail;
+ }
+
+ if (!(in_data = av_malloc(in_size))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ if ((ret = ffurl_read_complete(stream, in_data, in_size)) < 0)
+ goto fail;
+
+ if (in_size < 3) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ if (!memcmp(in_data, "CWS", 3)) {
+ /* Decompress the SWF player file using Zlib. */
+ if (!(out_data = av_malloc(8))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ *in_data = 'F'; // magic stuff
+ memcpy(out_data, in_data, 8);
+ out_size = 8;
+
+#if CONFIG_ZLIB
+ if ((ret = rtmp_uncompress_swfplayer(in_data + 8, in_size - 8,
+ &out_data, &out_size)) < 0)
+ goto fail;
+#else
+ av_log(s, AV_LOG_ERROR,
+ "Zlib is required for decompressing the SWF player file.\n");
+ ret = AVERROR(EINVAL);
+ goto fail;
+#endif
+ swfsize = out_size;
+ swfdata = out_data;
+ } else {
+ swfsize = in_size;
+ swfdata = in_data;
+ }
+
+ /* Compute the SHA256 hash of the SWF player file. */
+ if ((ret = ff_rtmp_calc_digest(swfdata, swfsize, 0,
+ "Genuine Adobe Flash Player 001", 30,
+ swfhash)) < 0)
+ goto fail;
+
+ /* Set SWFVerification parameters. */
+ av_opt_set_bin(rt, "rtmp_swfhash", swfhash, 32, 0);
+ rt->swfsize = swfsize;
+
+fail:
+ av_freep(&in_data);
+ av_freep(&out_data);
+ ffurl_close(stream);
+ return ret;
+}
+
+/**
+ * Perform handshake with the server by means of exchanging pseudorandom data
+ * signed with HMAC-SHA2 digest.
+ *
+ * @return 0 if handshake succeeds, negative value otherwise
+ */
+static int rtmp_handshake(URLContext *s, RTMPContext *rt)
+{
+ AVLFG rnd;
+ uint8_t tosend [RTMP_HANDSHAKE_PACKET_SIZE+1] = {
+ 3, // unencrypted data
+ 0, 0, 0, 0, // client uptime
+ RTMP_CLIENT_VER1,
+ RTMP_CLIENT_VER2,
+ RTMP_CLIENT_VER3,
+ RTMP_CLIENT_VER4,
+ };
+ uint8_t clientdata[RTMP_HANDSHAKE_PACKET_SIZE];
+ uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1];
+ int i;
+ int server_pos, client_pos;
+ uint8_t digest[32], signature[32];
+ int ret, type = 0;
+
+ av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
+
+ av_lfg_init(&rnd, 0xDEADC0DE);
+ // generate handshake packet - 1536 bytes of pseudorandom data
+ for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
+ tosend[i] = av_lfg_get(&rnd) >> 24;
+
+ if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ /* When the client wants to use RTMPE, we have to change the command
+ * byte to 0x06 which means to use encrypted data and we have to set
+ * the flash version to at least 9.0.115.0. */
+ tosend[0] = 6;
+ tosend[5] = 128;
+ tosend[6] = 0;
+ tosend[7] = 3;
+ tosend[8] = 2;
+
+ /* Initialize the Diffie-Hellmann context and generate the public key
+ * to send to the server. */
+ if ((ret = ff_rtmpe_gen_pub_key(rt->stream, tosend + 1)) < 0)
+ return ret;
+ }
+
+ client_pos = rtmp_handshake_imprint_with_digest(tosend + 1, rt->encrypted);
+ if (client_pos < 0)
+ return client_pos;
+
+ if ((ret = ffurl_write(rt->stream, tosend,
+ RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Cannot write RTMP handshake request\n");
+ return ret;
+ }
+
+ if ((ret = ffurl_read_complete(rt->stream, serverdata,
+ RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
+ return ret;
+ }
+
+ if ((ret = ffurl_read_complete(rt->stream, clientdata,
+ RTMP_HANDSHAKE_PACKET_SIZE)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
+ return ret;
+ }
+
+ av_log(s, AV_LOG_DEBUG, "Type answer %d\n", serverdata[0]);
+ av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
+ serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
+
+ if (rt->is_input && serverdata[5] >= 3) {
+ server_pos = rtmp_validate_digest(serverdata + 1, 772);
+ if (server_pos < 0)
+ return server_pos;
+
+ if (!server_pos) {
+ type = 1;
+ server_pos = rtmp_validate_digest(serverdata + 1, 8);
+ if (server_pos < 0)
+ return server_pos;
+
+ if (!server_pos) {
+ av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
+ return AVERROR(EIO);
+ }
+ }
+
+ /* Generate SWFVerification token (SHA256 HMAC hash of decompressed SWF,
+ * key are the last 32 bytes of the server handshake. */
+ if (rt->swfsize) {
+ if ((ret = rtmp_calc_swf_verification(s, rt, serverdata + 1 +
+ RTMP_HANDSHAKE_PACKET_SIZE - 32)) < 0)
+ return ret;
+ }
+
+ ret = ff_rtmp_calc_digest(tosend + 1 + client_pos, 32, 0,
+ rtmp_server_key, sizeof(rtmp_server_key),
+ digest);
+ if (ret < 0)
+ return ret;
+
+ ret = ff_rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE - 32,
+ 0, digest, 32, signature);
+ if (ret < 0)
+ return ret;
+
+ if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ /* Compute the shared secret key sent by the server and initialize
+ * the RC4 encryption. */
+ if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
+ tosend + 1, type)) < 0)
+ return ret;
+
+ /* Encrypt the signature received by the server. */
+ ff_rtmpe_encrypt_sig(rt->stream, signature, digest, serverdata[0]);
+ }
+
+ if (memcmp(signature, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
+ av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
+ return AVERROR(EIO);
+ }
+
+ for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
+ tosend[i] = av_lfg_get(&rnd) >> 24;
+ ret = ff_rtmp_calc_digest(serverdata + 1 + server_pos, 32, 0,
+ rtmp_player_key, sizeof(rtmp_player_key),
+ digest);
+ if (ret < 0)
+ return ret;
+
+ ret = ff_rtmp_calc_digest(tosend, RTMP_HANDSHAKE_PACKET_SIZE - 32, 0,
+ digest, 32,
+ tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
+ if (ret < 0)
+ return ret;
+
+ if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ /* Encrypt the signature to be send to the server. */
+ ff_rtmpe_encrypt_sig(rt->stream, tosend +
+ RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
+ serverdata[0]);
+ }
+
+ // write reply back to the server
+ if ((ret = ffurl_write(rt->stream, tosend,
+ RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
+ return ret;
+
+ if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ /* Set RC4 keys for encryption and update the keystreams. */
+ if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
+ return ret;
+ }
+ } else {
+ if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ /* Compute the shared secret key sent by the server and initialize
+ * the RC4 encryption. */
+ if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
+ tosend + 1, 1)) < 0)
+ return ret;
+
+ if (serverdata[0] == 9) {
+ /* Encrypt the signature received by the server. */
+ ff_rtmpe_encrypt_sig(rt->stream, signature, digest,
+ serverdata[0]);
+ }
+ }
+
+ if ((ret = ffurl_write(rt->stream, serverdata + 1,
+ RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
+ return ret;
+
+ if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
+ /* Set RC4 keys for encryption and update the keystreams. */
+ if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rtmp_receive_hs_packet(RTMPContext* rt, uint32_t *first_int,
+ uint32_t *second_int, char *arraydata,
+ int size)
+{
+ int inoutsize;
+
+ inoutsize = ffurl_read_complete(rt->stream, arraydata,
+ RTMP_HANDSHAKE_PACKET_SIZE);
+ if (inoutsize <= 0)
+ return AVERROR(EIO);
+ if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE) {
+ av_log(rt, AV_LOG_ERROR, "Erroneous Message size %d"
+ " not following standard\n", (int)inoutsize);
+ return AVERROR(EINVAL);
+ }
+
+ *first_int = AV_RB32(arraydata);
+ *second_int = AV_RB32(arraydata + 4);
+ return 0;
+}
+
+static int rtmp_send_hs_packet(RTMPContext* rt, uint32_t first_int,
+ uint32_t second_int, char *arraydata, int size)
+{
+ int inoutsize;
+
+ AV_WB32(arraydata, first_int);
+ AV_WB32(arraydata + 4, first_int);
+ inoutsize = ffurl_write(rt->stream, arraydata,
+ RTMP_HANDSHAKE_PACKET_SIZE);
+ if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE) {
+ av_log(rt, AV_LOG_ERROR, "Unable to write answer\n");
+ return AVERROR(EIO);
+ }
+
+ return 0;
+}
+
+/**
+ * rtmp handshake server side
+ */
+static int rtmp_server_handshake(URLContext *s, RTMPContext *rt)
+{
+ uint8_t buffer[RTMP_HANDSHAKE_PACKET_SIZE];
+ uint32_t hs_epoch;
+ uint32_t hs_my_epoch;
+ uint8_t hs_c1[RTMP_HANDSHAKE_PACKET_SIZE];
+ uint8_t hs_s1[RTMP_HANDSHAKE_PACKET_SIZE];
+ uint32_t zeroes;
+ uint32_t temp = 0;
+ int randomidx = 0;
+ int inoutsize = 0;
+ int ret;
+
+ inoutsize = ffurl_read_complete(rt->stream, buffer, 1); // Receive C0
+ if (inoutsize <= 0) {
+ av_log(s, AV_LOG_ERROR, "Unable to read handshake\n");
+ return AVERROR(EIO);
+ }
+ // Check Version
+ if (buffer[0] != 3) {
+ av_log(s, AV_LOG_ERROR, "RTMP protocol version mismatch\n");
+ return AVERROR(EIO);
+ }
+ if (ffurl_write(rt->stream, buffer, 1) <= 0) { // Send S0
+ av_log(s, AV_LOG_ERROR,
+ "Unable to write answer - RTMP S0\n");
+ return AVERROR(EIO);
+ }
+ /* Receive C1 */
+ ret = rtmp_receive_hs_packet(rt, &hs_epoch, &zeroes, hs_c1,
+ RTMP_HANDSHAKE_PACKET_SIZE);
+ if (ret) {
+ av_log(s, AV_LOG_ERROR, "RTMP Handshake C1 Error\n");
+ return ret;
+ }
+ if (zeroes)
+ av_log(s, AV_LOG_WARNING, "Erroneous C1 Message zero != 0\n");
+ /* Send S1 */
+ /* By now same epoch will be sent */
+ hs_my_epoch = hs_epoch;
+ /* Generate random */
+ for (randomidx = 8; randomidx < (RTMP_HANDSHAKE_PACKET_SIZE);
+ randomidx += 4)
+ AV_WB32(hs_s1 + randomidx, av_get_random_seed());
+
+ ret = rtmp_send_hs_packet(rt, hs_my_epoch, 0, hs_s1,
+ RTMP_HANDSHAKE_PACKET_SIZE);
+ if (ret) {
+ av_log(s, AV_LOG_ERROR, "RTMP Handshake S1 Error\n");
+ return ret;
+ }
+ /* Send S2 */
+ ret = rtmp_send_hs_packet(rt, hs_epoch, 0, hs_c1,
+ RTMP_HANDSHAKE_PACKET_SIZE);
+ if (ret) {
+ av_log(s, AV_LOG_ERROR, "RTMP Handshake S2 Error\n");
+ return ret;
+ }
+ /* Receive C2 */
+ ret = rtmp_receive_hs_packet(rt, &temp, &zeroes, buffer,
+ RTMP_HANDSHAKE_PACKET_SIZE);
+ if (ret) {
+ av_log(s, AV_LOG_ERROR, "RTMP Handshake C2 Error\n");
+ return ret;
+ }
+ if (temp != hs_my_epoch)
+ av_log(s, AV_LOG_WARNING,
+ "Erroneous C2 Message epoch does not match up with C1 epoch\n");
+ if (memcmp(buffer + 8, hs_s1 + 8,
+ RTMP_HANDSHAKE_PACKET_SIZE - 8))
+ av_log(s, AV_LOG_WARNING,
+ "Erroneous C2 Message random does not match up\n");
+
+ return 0;
+}
+
+static int handle_chunk_size(URLContext *s, RTMPPacket *pkt)
+{
+ RTMPContext *rt = s->priv_data;
+ int ret;
+
+ if (pkt->data_size < 4) {
+ av_log(s, AV_LOG_ERROR,
+ "Too short chunk size change packet (%d)\n",
+ pkt->data_size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (!rt->is_input) {
+ /* Send the same chunk size change packet back to the server,
+ * setting the outgoing chunk size to the same as the incoming one. */
+ if ((ret = ff_rtmp_packet_write(rt->stream, pkt, rt->out_chunk_size,
+ rt->prev_pkt[1])) < 0)
+ return ret;
+ rt->out_chunk_size = AV_RB32(pkt->data);
+ }
+
+ rt->in_chunk_size = AV_RB32(pkt->data);
+ if (rt->in_chunk_size <= 0) {
+ av_log(s, AV_LOG_ERROR, "Incorrect chunk size %d\n",
+ rt->in_chunk_size);
+ return AVERROR_INVALIDDATA;
+ }
+ av_log(s, AV_LOG_DEBUG, "New incoming chunk size = %d\n",
+ rt->in_chunk_size);
+
+ return 0;
+}
+
+static int handle_ping(URLContext *s, RTMPPacket *pkt)
+{
+ RTMPContext *rt = s->priv_data;
+ int t, ret;
+
+ if (pkt->data_size < 2) {
+ av_log(s, AV_LOG_ERROR, "Too short ping packet (%d)\n",
+ pkt->data_size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ t = AV_RB16(pkt->data);
+ if (t == 6) {
+ if ((ret = gen_pong(s, rt, pkt)) < 0)
+ return ret;
+ } else if (t == 26) {
+ if (rt->swfsize) {
+ if ((ret = gen_swf_verification(s, rt)) < 0)
+ return ret;
+ } else {
+ av_log(s, AV_LOG_WARNING, "Ignoring SWFVerification request.\n");
+ }
+ }
+
+ return 0;
+}
+
+static int handle_client_bw(URLContext *s, RTMPPacket *pkt)
+{
+ RTMPContext *rt = s->priv_data;
+
+ if (pkt->data_size < 4) {
+ av_log(s, AV_LOG_ERROR,
+ "Client bandwidth report packet is less than 4 bytes long (%d)\n",
+ pkt->data_size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ rt->client_report_size = AV_RB32(pkt->data);
+ if (rt->client_report_size <= 0) {
+ av_log(s, AV_LOG_ERROR, "Incorrect client bandwidth %d\n",
+ rt->client_report_size);
+ return AVERROR_INVALIDDATA;
+
+ }
+ av_log(s, AV_LOG_DEBUG, "Client bandwidth = %d\n", rt->client_report_size);
+ rt->client_report_size >>= 1;
+
+ return 0;