Even more documentation, RTMP Connector compiles again, but still extremely buggy. Will create RTMP debugging tool soon. Something wrong with RTMP Connector reading FLV::Tags... needs more investigation.

This commit is contained in:
Thulinma 2011-04-11 17:05:36 +02:00
parent b592442d6b
commit 6db6829545
29 changed files with 1451 additions and 1437 deletions

View file

@ -1,4 +1,4 @@
SRC = main.cpp ../util/ddv_socket.cpp ../util/flv_tag.cpp ../util/amf.cpp
SRC = main.cpp ../util/ddv_socket.cpp ../util/flv_tag.cpp ../util/amf.cpp ../util/rtmpchunks.cpp ../util/crypto.cpp
OBJ = $(SRC:.cpp=.o)
OUT = DDV_Conn_RTMP
INCLUDES =
@ -13,7 +13,7 @@ LIBS = -lssl -lcrypto
default: $(OUT)
.cpp.o:
$(CC) $(INCLUDES) $(CCFLAGS) -c $< -o $@
$(OUT): $(OBJ) chunkstream.cpp parsechunks.cpp handshake.cpp crypto.cpp
$(OUT): $(OBJ)
$(CC) -o $(OUT) $(OBJ) $(STATIC) $(LIBS)
clean:
rm -rf $(OBJ) $(OUT) Makefile.bak *~

View file

@ -1,38 +0,0 @@
#!/bin/sh
#
# description: DDVTech RTMP Connector
# processname: Connector_RTMP
prog="Connector_RTMP"
fullprog="/usr/bin/Connector_RTMP"
RETVAL=0
start() {
echo "Starting $prog"
$fullprog
return $?
}
stop() {
echo "Stopping $prog"
killall $prog
return $?
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo "Usage: $0 {start|stop|restart}"
RETVAL=1
esac
exit $RETVAL

View file

@ -1,501 +0,0 @@
#include <map>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <arpa/inet.h>
unsigned int getNowMS(){
timeval t;
gettimeofday(&t, 0);
return t.tv_sec + t.tv_usec/1000;
}
unsigned int chunk_rec_max = 128;
unsigned int chunk_snd_max = 128;
unsigned int rec_window_size = 0xFA00;
unsigned int snd_window_size = 1024*500;
unsigned int rec_window_at = 0;
unsigned int snd_window_at = 0;
unsigned int rec_cnt = 0;
unsigned int snd_cnt = 0;
unsigned int firsttime;
struct chunkinfo {
unsigned int cs_id;
unsigned int timestamp;
unsigned int len;
unsigned int real_len;
unsigned int len_left;
unsigned char msg_type_id;
unsigned int msg_stream_id;
};//chunkinfo
struct chunkpack {
unsigned char chunktype;
unsigned int cs_id;
unsigned int timestamp;
unsigned int len;
unsigned int real_len;
unsigned int len_left;
unsigned char msg_type_id;
unsigned int msg_stream_id;
unsigned char * data;
};//chunkpack
//clean a chunk so that it may be re-used without memory leaks
void scrubChunk(struct chunkpack c){
if (c.data){free(c.data);}
c.data = 0;
c.real_len = 0;
}//scrubChunk
//ugly global, but who cares...
std::map<unsigned int, chunkinfo> prevmap;
//return previous packet of this cs_id
chunkinfo GetPrev(unsigned int cs_id){
return prevmap[cs_id];
}//GetPrev
//store packet information of last packet of this cs_id
void PutPrev(chunkpack prev){
prevmap[prev.cs_id].timestamp = prev.timestamp;
prevmap[prev.cs_id].len = prev.len;
prevmap[prev.cs_id].real_len = prev.real_len;
prevmap[prev.cs_id].len_left = prev.len_left;
prevmap[prev.cs_id].msg_type_id = prev.msg_type_id;
prevmap[prev.cs_id].msg_stream_id = prev.msg_stream_id;
}//PutPrev
//ugly global, but who cares...
std::map<unsigned int, chunkinfo> sndprevmap;
//return previous packet of this cs_id
chunkinfo GetSndPrev(unsigned int cs_id){
return sndprevmap[cs_id];
}//GetPrev
//store packet information of last packet of this cs_id
void PutSndPrev(chunkpack prev){
sndprevmap[prev.cs_id].cs_id = prev.cs_id;
sndprevmap[prev.cs_id].timestamp = prev.timestamp;
sndprevmap[prev.cs_id].len = prev.len;
sndprevmap[prev.cs_id].real_len = prev.real_len;
sndprevmap[prev.cs_id].len_left = prev.len_left;
sndprevmap[prev.cs_id].msg_type_id = prev.msg_type_id;
sndprevmap[prev.cs_id].msg_stream_id = prev.msg_stream_id;
}//PutPrev
//sends the chunk over the network
void SendChunk(chunkpack ch){
unsigned char tmp;
unsigned int tmpi;
unsigned char chtype = 0x00;
chunkinfo prev = GetSndPrev(ch.cs_id);
ch.timestamp -= firsttime;
if (prev.cs_id == ch.cs_id){
if (ch.msg_stream_id == prev.msg_stream_id){
chtype = 0x40;//do not send msg_stream_id
if (ch.len == prev.len){
if (ch.msg_type_id == prev.msg_type_id){
chtype = 0x80;//do not send len and msg_type_id
if (ch.timestamp == prev.timestamp){
chtype = 0xC0;//do not send timestamp
}
}
}
}
}
if (ch.cs_id <= 63){
tmp = chtype | ch.cs_id; DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=1;
}else{
if (ch.cs_id <= 255+64){
tmp = chtype | 0; DDV_write(&tmp, 1, 1, CONN_fd);
tmp = ch.cs_id - 64; DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=2;
}else{
tmp = chtype | 1; DDV_write(&tmp, 1, 1, CONN_fd);
tmpi = ch.cs_id - 64;
tmp = tmpi % 256; DDV_write(&tmp, 1, 1, CONN_fd);
tmp = tmpi / 256; DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=3;
}
}
unsigned int ntime = 0;
if (chtype != 0xC0){
//timestamp or timestamp diff
if (chtype == 0x00){
tmpi = ch.timestamp;
if (tmpi >= 0x00ffffff){ntime = tmpi; tmpi = 0x00ffffff;}
tmp = tmpi / (256*256); DDV_write(&tmp, 1, 1, CONN_fd);
tmp = tmpi / 256; DDV_write(&tmp, 1, 1, CONN_fd);
tmp = tmpi % 256; DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=3;
}else{
tmpi = ch.timestamp - prev.timestamp;
if (tmpi >= 0x00ffffff){ntime = tmpi; tmpi = 0x00ffffff;}
tmp = tmpi / (256*256); DDV_write(&tmp, 1, 1, CONN_fd);
tmp = tmpi / 256; DDV_write(&tmp, 1, 1, CONN_fd);
tmp = tmpi % 256; DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=3;
}
if (chtype != 0x80){
//len
tmpi = ch.len;
tmp = tmpi / (256*256); DDV_write(&tmp, 1, 1, CONN_fd);
tmp = tmpi / 256; DDV_write(&tmp, 1, 1, CONN_fd);
tmp = tmpi % 256; DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=3;
//msg type id
tmp = ch.msg_type_id; DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=1;
if (chtype != 0x40){
//msg stream id
tmp = ch.msg_stream_id % 256; DDV_write(&tmp, 1, 1, CONN_fd);
tmp = ch.msg_stream_id / 256; DDV_write(&tmp, 1, 1, CONN_fd);
tmp = ch.msg_stream_id / (256*256); DDV_write(&tmp, 1, 1, CONN_fd);
tmp = ch.msg_stream_id / (256*256*256); DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=4;
}
}
}
//support for 0x00ffffff timestamps
if (ntime){
tmp = ntime / (256*256*256); DDV_write(&tmp, 1, 1, CONN_fd);
tmp = ntime / (256*256); DDV_write(&tmp, 1, 1, CONN_fd);
tmp = ntime / 256; DDV_write(&tmp, 1, 1, CONN_fd);
tmp = ntime % 256; DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=4;
}
ch.len_left = 0;
while (ch.len_left < ch.len){
tmpi = ch.len - ch.len_left;
if (tmpi > chunk_snd_max){tmpi = chunk_snd_max;}
DDV_write((ch.data + ch.len_left), 1, tmpi, CONN_fd);
snd_cnt+=tmpi;
ch.len_left += tmpi;
if (ch.len_left < ch.len){
if (ch.cs_id <= 63){
tmp = 0xC0 + ch.cs_id; DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=1;
}else{
if (ch.cs_id <= 255+64){
tmp = 0xC0; DDV_write(&tmp, 1, 1, CONN_fd);
tmp = ch.cs_id - 64; DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=2;
}else{
tmp = 0xC1; DDV_write(&tmp, 1, 1, CONN_fd);
tmpi = ch.cs_id - 64;
tmp = tmpi % 256; DDV_write(&tmp, 1, 1, CONN_fd);
tmp = tmpi / 256; DDV_write(&tmp, 1, 1, CONN_fd);
snd_cnt+=4;
}
}
}
}
PutSndPrev(ch);
}//SendChunk
//sends a chunk
void SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data){
chunkpack ch;
ch.cs_id = cs_id;
ch.timestamp = getNowMS();
ch.len = data.size();
ch.real_len = data.size();
ch.len_left = 0;
ch.msg_type_id = msg_type_id;
ch.msg_stream_id = msg_stream_id;
ch.data = (unsigned char*)malloc(data.size());
memcpy(ch.data, data.c_str(), data.size());
SendChunk(ch);
free(ch.data);
}//SendChunk
//sends a media chunk
void SendMedia(unsigned char msg_type_id, unsigned char * data, int len, unsigned int ts){
chunkpack ch;
ch.cs_id = msg_type_id;
ch.timestamp = ts;
ch.len = len;
ch.real_len = len;
ch.len_left = 0;
ch.msg_type_id = msg_type_id;
ch.msg_stream_id = 1;
ch.data = (unsigned char*)malloc(len);
memcpy(ch.data, data, len);
SendChunk(ch);
free(ch.data);
}//SendMedia
//sends a control message
void SendCTL(unsigned char type, unsigned int data){
chunkpack ch;
ch.cs_id = 2;
ch.timestamp = getNowMS();
ch.len = 4;
ch.real_len = 4;
ch.len_left = 0;
ch.msg_type_id = type;
ch.msg_stream_id = 0;
ch.data = (unsigned char*)malloc(4);
data = htonl(data);
memcpy(ch.data, &data, 4);
SendChunk(ch);
free(ch.data);
}//SendCTL
//sends a control message
void SendCTL(unsigned char type, unsigned int data, unsigned char data2){
chunkpack ch;
ch.cs_id = 2;
ch.timestamp = getNowMS();
ch.len = 5;
ch.real_len = 5;
ch.len_left = 0;
ch.msg_type_id = type;
ch.msg_stream_id = 0;
ch.data = (unsigned char*)malloc(5);
data = htonl(data);
memcpy(ch.data, &data, 4);
ch.data[4] = data2;
SendChunk(ch);
free(ch.data);
}//SendCTL
//sends a usr control message
void SendUSR(unsigned char type, unsigned int data){
chunkpack ch;
ch.cs_id = 2;
ch.timestamp = getNowMS();
ch.len = 6;
ch.real_len = 6;
ch.len_left = 0;
ch.msg_type_id = 4;
ch.msg_stream_id = 0;
ch.data = (unsigned char*)malloc(6);
data = htonl(data);
memcpy(ch.data+2, &data, 4);
ch.data[0] = 0;
ch.data[1] = type;
SendChunk(ch);
free(ch.data);
}//SendUSR
//sends a usr control message
void SendUSR(unsigned char type, unsigned int data, unsigned int data2){
chunkpack ch;
ch.cs_id = 2;
ch.timestamp = getNowMS();
ch.len = 10;
ch.real_len = 10;
ch.len_left = 0;
ch.msg_type_id = 4;
ch.msg_stream_id = 0;
ch.data = (unsigned char*)malloc(10);
data = htonl(data);
data2 = htonl(data2);
memcpy(ch.data+2, &data, 4);
memcpy(ch.data+6, &data2, 4);
ch.data[0] = 0;
ch.data[1] = type;
SendChunk(ch);
free(ch.data);
}//SendUSR
//get a chunk from standard input
struct chunkpack getChunk(){
gettimeofday(&lastrec, 0);
struct chunkpack ret;
unsigned char temp;
DDV_read(&(ret.chunktype), 1, 1, CONN_fd);
rec_cnt++;
//read the chunkstream ID properly
switch (ret.chunktype & 0x3F){
case 0:
DDV_read(&temp, 1, 1, CONN_fd);
rec_cnt++;
ret.cs_id = temp + 64;
break;
case 1:
DDV_read(&temp, 1, 1, CONN_fd);
ret.cs_id = temp + 64;
DDV_read(&temp, 1, 1, CONN_fd);
ret.cs_id += temp * 256;
rec_cnt+=2;
break;
default:
ret.cs_id = ret.chunktype & 0x3F;
break;
}
chunkinfo prev = GetPrev(ret.cs_id);
//process the rest of the header, for each chunk type
switch (ret.chunktype & 0xC0){
case 0x00:
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp = temp*256*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp += temp*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp += temp;
DDV_read(&temp, 1, 1, CONN_fd);
ret.len = temp*256*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.len += temp*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.len += temp;
ret.len_left = 0;
DDV_read(&temp, 1, 1, CONN_fd);
ret.msg_type_id = temp;
DDV_read(&temp, 1, 1, CONN_fd);
ret.msg_stream_id = temp;
DDV_read(&temp, 1, 1, CONN_fd);
ret.msg_stream_id += temp*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.msg_stream_id += temp*256*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.msg_stream_id += temp*256*256*256;
rec_cnt+=11;
break;
case 0x40:
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp = temp*256*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp += temp*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp += temp;
ret.timestamp += prev.timestamp;
DDV_read(&temp, 1, 1, CONN_fd);
ret.len = temp*256*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.len += temp*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.len += temp;
ret.len_left = 0;
DDV_read(&temp, 1, 1, CONN_fd);
ret.msg_type_id = temp;
ret.msg_stream_id = prev.msg_stream_id;
rec_cnt+=7;
break;
case 0x80:
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp = temp*256*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp += temp*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp += temp;
ret.timestamp += prev.timestamp;
ret.len = prev.len;
ret.len_left = prev.len_left;
ret.msg_type_id = prev.msg_type_id;
ret.msg_stream_id = prev.msg_stream_id;
rec_cnt+=3;
break;
case 0xC0:
ret.timestamp = prev.timestamp;
ret.len = prev.len;
ret.len_left = prev.len_left;
ret.msg_type_id = prev.msg_type_id;
ret.msg_stream_id = prev.msg_stream_id;
break;
}
//calculate chunk length, real length, and length left till complete
if (ret.len_left > 0){
ret.real_len = ret.len_left;
ret.len_left -= ret.real_len;
}else{
ret.real_len = ret.len;
}
if (ret.real_len > chunk_rec_max){
ret.len_left += ret.real_len - chunk_rec_max;
ret.real_len = chunk_rec_max;
}
//read extended timestamp, if neccesary
if (ret.timestamp == 0x00ffffff){
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp = temp*256*256*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp += temp*256*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp += temp*256;
DDV_read(&temp, 1, 1, CONN_fd);
ret.timestamp += temp;
rec_cnt+=4;
}
//read data if length > 0, and allocate it
if (ret.real_len > 0){
ret.data = (unsigned char*)malloc(ret.real_len);
DDV_read(ret.data, 1, ret.real_len, CONN_fd);
rec_cnt+=ret.real_len;
}else{
ret.data = 0;
}
PutPrev(ret);
return ret;
}//getChunk
//adds newchunk to global list of unfinished chunks, re-assembling them complete
//returns pointer to chunk when a chunk is finished, 0 otherwise
//removes pointed to chunk from internal list if returned, without cleanup
// (cleanup performed in getWholeChunk function)
chunkpack * AddChunkPart(chunkpack newchunk){
chunkpack * p;
unsigned char * tmpdata = 0;
static std::map<unsigned int, chunkpack *> ch_lst;
std::map<unsigned int, chunkpack *>::iterator it;
it = ch_lst.find(newchunk.cs_id);
if (it == ch_lst.end()){
p = (chunkpack*)malloc(sizeof(chunkpack));
*p = newchunk;
p->data = (unsigned char*)malloc(p->real_len);
memcpy(p->data, newchunk.data, p->real_len);
if (p->len_left == 0){return p;}
ch_lst[newchunk.cs_id] = p;
}else{
p = it->second;
tmpdata = (unsigned char*)realloc(p->data, p->real_len + newchunk.real_len);
if (tmpdata == 0){
#if DEBUG >= 1
fprintf(stderr, "Error allocating memory!\n");
#endif
return 0;
}
p->data = tmpdata;
memcpy(p->data+p->real_len, newchunk.data, newchunk.real_len);
p->real_len += newchunk.real_len;
p->len_left -= newchunk.real_len;
if (p->len_left <= 0){
ch_lst.erase(it);
return p;
}else{
ch_lst[newchunk.cs_id] = p;//pointer may have changed
}
}
return 0;
}//AddChunkPart
//grabs chunks until a whole one comes in, then returns that
chunkpack getWholeChunk(){
static chunkpack gwc_next, gwc_complete;
static bool clean = false;
int counter = 0;
if (!clean){gwc_complete.data = 0; clean = true;}//prevent brain damage
chunkpack * ret = 0;
scrubChunk(gwc_complete);
while (counter < 1000){
gwc_next = getChunk();
ret = AddChunkPart(gwc_next);
scrubChunk(gwc_next);
if (ret){
gwc_complete = *ret;
free(ret);//cleanup returned chunk
return gwc_complete;
}
if (socketError || socketBlocking){break;}
counter++;
}
gwc_complete.msg_type_id = 0;
return gwc_complete;
}//getWholeChunk

View file

@ -1,506 +0,0 @@
#define STR(x) (((std::string)(x)).c_str())
#include "crypto.h"
#define P768 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF"
#define P1024 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
"FFFFFFFFFFFFFFFF"
#define Q1024 \
"7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68" \
"948127044533E63A0105DF531D89CD9128A5043CC71A026E" \
"F7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122" \
"F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6" \
"F71C35FDAD44CFD2D74F9208BE258FF324943328F67329C0" \
"FFFFFFFFFFFFFFFF"
#define P1536 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
#define P2048 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
"15728E5A8AACAA68FFFFFFFFFFFFFFFF"
#define P3072 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
"43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"
#define P4096 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" \
"FFFFFFFFFFFFFFFF"
#define P6144 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \
"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \
"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \
"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \
"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \
"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \
"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \
"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \
"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \
"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \
"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \
"12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF"
#define P8192 \
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
"29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
"EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
"E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
"EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \
"C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \
"83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
"670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" \
"E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" \
"DE2BCBF6955817183995497CEA956AE515D2261898FA0510" \
"15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" \
"ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" \
"ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" \
"F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" \
"BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" \
"43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" \
"88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" \
"2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" \
"287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" \
"1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" \
"93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" \
"36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" \
"F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" \
"179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" \
"DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" \
"5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" \
"D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" \
"23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" \
"CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" \
"06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" \
"DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" \
"12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" \
"38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" \
"741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" \
"3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" \
"22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" \
"4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" \
"062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" \
"4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" \
"B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" \
"4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" \
"9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" \
"60C980DD98EDD3DFFFFFFFFFFFFFFFFF"
uint8_t genuineFMSKey[] = {
0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20,
0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c,
0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69,
0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001
0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8,
0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57,
0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab,
0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae
}; // 68
uint8_t genuineFPKey[] = {
0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20,
0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C,
0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79,
0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001
0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8,
0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57,
0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,
0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
}; // 62
void replace(std::string &target, std::string search, std::string replacement) {
if (search == replacement)
return;
if (search == "")
return;
std::string::size_type i = std::string::npos;
while ((i = target.find(search)) != std::string::npos) {
target.replace(i, search.length(), replacement);
}
}
DHWrapper::DHWrapper(int32_t bitsCount) {
_bitsCount = bitsCount;
_pDH = NULL;
_pSharedKey = NULL;
_sharedKeyLength = 0;
_peerPublickey = NULL;
}
DHWrapper::~DHWrapper() {
Cleanup();
}
bool DHWrapper::Initialize() {
Cleanup();
//1. Create the DH
_pDH = DH_new();
if (_pDH == NULL) {
Cleanup();
return false;
}
//2. Create his internal p and g
_pDH->p = BN_new();
if (_pDH->p == NULL) {
Cleanup();
return false;
}
_pDH->g = BN_new();
if (_pDH->g == NULL) {
Cleanup();
return false;
}
//3. initialize p, g and key length
if (BN_hex2bn(&_pDH->p, P1024) == 0) {
Cleanup();
return false;
}
if (BN_set_word(_pDH->g, 2) != 1) {
Cleanup();
return false;
}
//4. Set the key length
_pDH->length = _bitsCount;
//5. Generate private and public key
if (DH_generate_key(_pDH) != 1) {
Cleanup();
return false;
}
return true;
}
bool DHWrapper::CopyPublicKey(uint8_t *pDst, int32_t dstLength) {
if (_pDH == NULL) {
return false;
}
return CopyKey(_pDH->pub_key, pDst, dstLength);
}
bool DHWrapper::CopyPrivateKey(uint8_t *pDst, int32_t dstLength) {
if (_pDH == NULL) {
return false;
}
return CopyKey(_pDH->priv_key, pDst, dstLength);
}
bool DHWrapper::CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length) {
if (_pDH == NULL) {
return false;
}
if (_sharedKeyLength != 0 || _pSharedKey != NULL) {
return false;
}
_sharedKeyLength = DH_size(_pDH);
if (_sharedKeyLength <= 0 || _sharedKeyLength > 1024) {
return false;
}
_pSharedKey = new uint8_t[_sharedKeyLength];
_peerPublickey = BN_bin2bn(pPeerPublicKey, length, 0);
if (_peerPublickey == NULL) {
return false;
}
if (DH_compute_key(_pSharedKey, _peerPublickey, _pDH) != _sharedKeyLength) {
return false;
}
return true;
}
bool DHWrapper::CopySharedKey(uint8_t *pDst, int32_t dstLength) {
if (_pDH == NULL) {
return false;
}
if (dstLength != _sharedKeyLength) {
return false;
}
memcpy(pDst, _pSharedKey, _sharedKeyLength);
return true;
}
void DHWrapper::Cleanup() {
if (_pDH != NULL) {
if (_pDH->p != NULL) {
BN_free(_pDH->p);
_pDH->p = NULL;
}
if (_pDH->g != NULL) {
BN_free(_pDH->g);
_pDH->g = NULL;
}
DH_free(_pDH);
_pDH = NULL;
}
if (_pSharedKey != NULL) {
delete[] _pSharedKey;
_pSharedKey = NULL;
}
_sharedKeyLength = 0;
if (_peerPublickey != NULL) {
BN_free(_peerPublickey);
_peerPublickey = NULL;
}
}
bool DHWrapper::CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength) {
int32_t keySize = BN_num_bytes(pNum);
if ((keySize <= 0) || (dstLength <= 0) || (keySize > dstLength)) {
return false;
}
if (BN_bn2bin(pNum, pDst) != keySize) {
return false;
}
return true;
}
void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut, RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut) {
uint8_t digest[SHA256_DIGEST_LENGTH];
unsigned int digestLen = 0;
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0);
HMAC_Update(&ctx, pubKeyIn, 128);
HMAC_Final(&ctx, digest, &digestLen);
HMAC_CTX_cleanup(&ctx);
RC4_set_key(rc4keyOut, 16, digest);
HMAC_CTX_init(&ctx);
HMAC_Init_ex(&ctx, secretKey, 128, EVP_sha256(), 0);
HMAC_Update(&ctx, pubKeyOut, 128);
HMAC_Final(&ctx, digest, &digestLen);
HMAC_CTX_cleanup(&ctx);
RC4_set_key(rc4keyIn, 16, digest);
}
std::string md5(std::string source, bool textResult) {
EVP_MD_CTX mdctx;
unsigned char md_value[EVP_MAX_MD_SIZE];
unsigned int md_len;
EVP_DigestInit(&mdctx, EVP_md5());
EVP_DigestUpdate(&mdctx, STR(source), source.length());
EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
EVP_MD_CTX_cleanup(&mdctx);
if (textResult) {
std::string result = "";
char tmp[3];
for (uint32_t i = 0; i < md_len; i++) {
sprintf(tmp, "%02x", md_value[i]);
result += tmp;
}
return result;
} else {
return std::string((char *) md_value, md_len);
}
}
std::string b64(std::string source) {
return b64((uint8_t *) STR(source), source.size());
}
std::string b64(uint8_t *pBuffer, uint32_t length) {
BIO *bmem;
BIO *b64;
BUF_MEM *bptr;
b64 = BIO_new(BIO_f_base64());
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, pBuffer, length);
std::string result = "";
if (BIO_flush(b64) == 1) {
BIO_get_mem_ptr(b64, &bptr);
result = std::string(bptr->data, bptr->length);
}
BIO_free_all(b64);
replace(result, "\n", "");
replace(result, "\r", "");
return result;
}
std::string unb64(std::string source) {
return unb64((uint8_t *)STR(source),source.length());
}
std::string unb64(uint8_t *pBuffer, uint32_t length){
// create a memory buffer containing base64 encoded data
//BIO* bmem = BIO_new_mem_buf((void*) STR(source), source.length());
BIO* bmem = BIO_new_mem_buf((void *)pBuffer, length);
// push a Base64 filter so that reading from buffer decodes it
BIO *bioCmd = BIO_new(BIO_f_base64());
// we don't want newlines
BIO_set_flags(bioCmd, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_push(bioCmd, bmem);
char *pOut = new char[length];
int finalLen = BIO_read(bmem, (void*) pOut, length);
BIO_free_all(bmem);
std::string result(pOut, finalLen);
delete[] pOut;
return result;
}
void HMACsha256(const void *pData, uint32_t dataLength, const void *pKey, uint32_t keyLength, void *pResult) {
unsigned int digestLen;
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
HMAC_Init_ex(&ctx, (unsigned char*) pKey, keyLength, EVP_sha256(), NULL);
HMAC_Update(&ctx, (unsigned char *) pData, dataLength);
HMAC_Final(&ctx, (unsigned char *) pResult, &digestLen);
HMAC_CTX_cleanup(&ctx);
}
uint32_t GetDigestOffset0(uint8_t *pBuffer) {
uint32_t offset = pBuffer[8] + pBuffer[9] + pBuffer[10] + pBuffer[11];
return (offset % 728) + 12;
}
uint32_t GetDigestOffset1(uint8_t *pBuffer) {
uint32_t offset = pBuffer[772] + pBuffer[773] + pBuffer[774] + pBuffer[775];
return (offset % 728) + 776;
}
uint32_t GetDigestOffset(uint8_t *pBuffer, uint8_t scheme){
if (scheme == 0){return GetDigestOffset0(pBuffer);}else{return GetDigestOffset1(pBuffer);}
}
uint32_t GetDHOffset0(uint8_t *pBuffer) {
uint32_t offset = pBuffer[1532] + pBuffer[1533] + pBuffer[1534] + pBuffer[1535];
return (offset % 632) + 772;
}
uint32_t GetDHOffset1(uint8_t *pBuffer) {
uint32_t offset = pBuffer[768] + pBuffer[769] + pBuffer[770] + pBuffer[771];
return (offset % 632) + 8;
}
uint32_t GetDHOffset(uint8_t *pBuffer, uint8_t scheme){
if (scheme == 0){return GetDHOffset0(pBuffer);}else{return GetDHOffset1(pBuffer);}
}
bool ValidateClientScheme(uint8_t * pBuffer, uint8_t scheme) {
uint32_t clientDigestOffset = GetDigestOffset(pBuffer, scheme);
uint8_t *pTempBuffer = new uint8_t[1536 - 32];
memcpy(pTempBuffer, pBuffer, clientDigestOffset);
memcpy(pTempBuffer + clientDigestOffset, pBuffer + clientDigestOffset + 32, 1536 - clientDigestOffset - 32);
uint8_t *pTempHash = new uint8_t[512];
HMACsha256(pTempBuffer, 1536 - 32, genuineFPKey, 30, pTempHash);
bool result = (memcmp(pBuffer+clientDigestOffset, pTempHash, 32) == 0);
#if DEBUG >= 4
fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed");
#endif
delete[] pTempBuffer;
delete[] pTempHash;
return result;
}

View file

@ -1,45 +0,0 @@
#ifndef _CRYPTO_H
#define _CRYPTO_H
#define DLLEXP
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/rc4.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/hmac.h>
class DLLEXP DHWrapper {
private:
int32_t _bitsCount;
DH *_pDH;
uint8_t *_pSharedKey;
int32_t _sharedKeyLength;
BIGNUM *_peerPublickey;
public:
DHWrapper(int32_t bitsCount);
virtual ~DHWrapper();
bool Initialize();
bool CopyPublicKey(uint8_t *pDst, int32_t dstLength);
bool CopyPrivateKey(uint8_t *pDst, int32_t dstLength);
bool CreateSharedKey(uint8_t *pPeerPublicKey, int32_t length);
bool CopySharedKey(uint8_t *pDst, int32_t dstLength);
private:
void Cleanup();
bool CopyKey(BIGNUM *pNum, uint8_t *pDst, int32_t dstLength);
};
DLLEXP void InitRC4Encryption(uint8_t *secretKey, uint8_t *pubKeyIn, uint8_t *pubKeyOut,
RC4_KEY *rc4keyIn, RC4_KEY *rc4keyOut);
DLLEXP std::string md5(std::string source, bool textResult);
DLLEXP std::string b64(std::string source);
DLLEXP std::string b64(uint8_t *pBuffer, uint32_t length);
DLLEXP std::string unb64(std::string source);
DLLEXP std::string unb64(uint8_t *pBuffer, uint32_t length);
#endif /* _CRYPTO_H */

View file

@ -1,137 +0,0 @@
#undef OLDHANDSHAKE //change to #define for old handshake method
char versionstring[] = "WWW.DDVTECH.COM ";
#ifdef OLDHANDSHAKE
struct Handshake {
char Time[4];
char Zero[4];
char Random[1528];
};//Handshake
bool doHandshake(){
char Version;
Handshake Client;
Handshake Server;
/** Read C0 **/
DDV_read(&(Version), 1, 1, CONN_fd);
/** Read C1 **/
DDV_read(Client.Time, 1, 4, CONN_fd);
DDV_read(Client.Zero, 1, 4, CONN_fd);
DDV_read(Client.Random, 1, 1528, CONN_fd);
rec_cnt+=1537;
/** Build S1 Packet **/
Server.Time[0] = 0; Server.Time[1] = 0; Server.Time[2] = 0; Server.Time[3] = 0;
Server.Zero[0] = 0; Server.Zero[1] = 0; Server.Zero[2] = 0; Server.Zero[3] = 0;
for (int i = 0; i < 1528; i++){
Server.Random[i] = versionstring[i%sizeof(versionstring)];
}
/** Send S0 **/
DDV_write(&(Version), 1, 1, CONN_fd);
/** Send S1 **/
DDV_write(Server.Time, 1, 4, CONN_fd);
DDV_write(Server.Zero, 1, 4, CONN_fd);
DDV_write(Server.Random, 1, 1528, CONN_fd);
/** Flush output, just for certainty **/
//fflush(CONN_fd);
snd_cnt+=1537;
/** Send S2 **/
DDV_write(Client.Time, 1, 4, CONN_fd);
DDV_write(Client.Time, 1, 4, CONN_fd);
DDV_write(Client.Random, 1, 1528, CONN_fd);
snd_cnt+=1536;
/** Flush, necessary in order to work **/
//fflush(CONN_fd);
/** Read and discard C2 **/
DDV_read(Client.Time, 1, 4, CONN_fd);
DDV_read(Client.Zero, 1, 4, CONN_fd);
DDV_read(Client.Random, 1, 1528, CONN_fd);
rec_cnt+=1536;
return true;
}//doHandshake
#else
#include "crypto.cpp" //cryptography for handshaking
bool doHandshake(){
char Version;
/** Read C0 **/
DDV_read(&Version, 1, 1, CONN_fd);
uint8_t Client[1536];
uint8_t Server[3072];
DDV_read(&Client, 1, 1536, CONN_fd);
rec_cnt+=1537;
/** Build S1 Packet **/
*((uint32_t*)Server) = 0;//time zero
*(((uint32_t*)(Server+4))) = htonl(0x01020304);//version 1 2 3 4
for (int i = 8; i < 3072; ++i){Server[i] = versionstring[i%13];}//"random" data
bool encrypted = (Version == 6);
#if DEBUG >= 4
fprintf(stderr, "Handshake version is %hhi\n", Version);
#endif
uint8_t _validationScheme = 5;
if (ValidateClientScheme(Client, 0)) _validationScheme = 0;
if (ValidateClientScheme(Client, 1)) _validationScheme = 1;
#if DEBUG >= 4
fprintf(stderr, "Handshake type is %hhi, encryption is %s\n", _validationScheme, encrypted?"on":"off");
#endif
//**** FIRST 1536 bytes from server response ****//
//compute DH key position
uint32_t serverDHOffset = GetDHOffset(Server, _validationScheme);
uint32_t clientDHOffset = GetDHOffset(Client, _validationScheme);
//generate DH key
DHWrapper dhWrapper(1024);
if (!dhWrapper.Initialize()) return false;
if (!dhWrapper.CreateSharedKey(Client + clientDHOffset, 128)) return false;
if (!dhWrapper.CopyPublicKey(Server + serverDHOffset, 128)) return false;
if (encrypted) {
uint8_t secretKey[128];
if (!dhWrapper.CopySharedKey(secretKey, sizeof (secretKey))) return false;
RC4_KEY _pKeyIn;
RC4_KEY _pKeyOut;
InitRC4Encryption(secretKey, (uint8_t*) & Client[clientDHOffset], (uint8_t*) & Server[serverDHOffset], &_pKeyIn, &_pKeyOut);
uint8_t data[1536];
RC4(&_pKeyIn, 1536, data, data);
RC4(&_pKeyOut, 1536, data, data);
}
//generate the digest
uint32_t serverDigestOffset = GetDigestOffset(Server, _validationScheme);
uint8_t *pTempBuffer = new uint8_t[1536 - 32];
memcpy(pTempBuffer, Server, serverDigestOffset);
memcpy(pTempBuffer + serverDigestOffset, Server + serverDigestOffset + 32, 1536 - serverDigestOffset - 32);
uint8_t *pTempHash = new uint8_t[512];
HMACsha256(pTempBuffer, 1536 - 32, genuineFMSKey, 36, pTempHash);
memcpy(Server + serverDigestOffset, pTempHash, 32);
delete[] pTempBuffer;
delete[] pTempHash;
//**** SECOND 1536 bytes from server response ****//
uint32_t keyChallengeIndex = GetDigestOffset(Client, _validationScheme);
pTempHash = new uint8_t[512];
HMACsha256(Client + keyChallengeIndex, 32, genuineFMSKey, 68, pTempHash);
uint8_t *pLastHash = new uint8_t[512];
HMACsha256(Server + 1536, 1536 - 32, pTempHash, 32, pLastHash);
memcpy(Server + 1536 * 2 - 32, pLastHash, 32);
delete[] pTempHash;
delete[] pLastHash;
//***** DONE BUILDING THE RESPONSE ***//
/** Send response **/
DDV_write(&Version, 1, 1, CONN_fd);
DDV_write(&Server, 1, 3072, CONN_fd);
snd_cnt+=3073;
/** Flush, necessary in order to work **/
//fflush(CONN_fd);
/** Read and discard C2 **/
DDV_read(Client, 1, 1536, CONN_fd);
rec_cnt+=1536;
return true;
}
#endif

View file

@ -1,9 +1,11 @@
/// \file Connector_RTMP/main.cpp
/// Contains the main code for the RTMP Connector
//debugging level 0 = nothing
//debugging level 1 = critical errors
//debugging level 2 = errors
//debugging level 3 = status information
//debugging level 4 = extremely verbose status information
//debugging level 5 = save all streams to FLV files
#define DEBUG 4
#include <iostream>
@ -18,155 +20,402 @@
#include <getopt.h>
#include "../util/ddv_socket.h"
#include "../util/flv_tag.h"
#include "parsechunks.cpp" //chunkstream parsing
#include "handshake.cpp" //handshaking
#include "../util/amf.h"
#include "../util/rtmpchunks.h"
/// Holds all functions and data unique to the RTMP Connector
namespace Connector_RTMP{
//for connection to server
bool ready4data = false; ///< Set to true when streaming starts.
bool inited = false; ///< Set to true when ready to connect to Buffer.
bool stopparsing = false; ///< Set to true when all parsing needs to be cancelled.
timeval lastrec; ///< Timestamp of last received data.
DDV::Socket Socket; ///< Socket connected to user
/// Main Connector_RTMP function
int Connector_RTMP(DDV::Socket conn){
Socket = conn;
unsigned int ts;
unsigned int fts = 0;
unsigned int ftst;
DDV::Socket SS;
FLV::Tag tag = 0;
//first timestamp set
firsttime = getNowMS();
if (doHandshake()){
#if DEBUG >= 4
fprintf(stderr, "Handshake succcess!\n");
#endif
}else{
#if DEBUG >= 1
fprintf(stderr, "Handshake fail!\n");
#endif
return 0;
}
int retval;
int poller = epoll_create(1);
int sspoller = epoll_create(1);
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = CONN_fd;
epoll_ctl(poller, EPOLL_CTL_ADD, CONN_fd, &ev);
struct epoll_event events[1];
#if DEBUG >= 5
//for writing whole stream to a file
FILE * tmpfile = 0;
char tmpstr[200];
#endif
while (Socket.connected() && !FLV::Parse_Error){
//only parse input if available or not yet init'ed
//rightnow = getNowMS();
retval = epoll_wait(poller, events, 1, 1);
if ((retval > 0) || !ready4data){// || (snd_cnt - snd_window_at >= snd_window_size)
switch (Socket.ready()){
case -1: break; //disconnected
case 0: break; //not ready yet
default: parseChunk(); break; //new data is waiting
}
}
if (ready4data){
if (!inited){
//we are ready, connect the socket!
SS = DDV::Socket(streamname);
if (!SS.connected()){
#if DEBUG >= 1
fprintf(stderr, "Could not connect to server!\n");
#endif
Socket.close();//disconnect user
break;
}
ev.events = EPOLLIN;
ev.data.fd = SS.getSocket();
epoll_ctl(sspoller, EPOLL_CTL_ADD, SS.getSocket(), &ev);
#if DEBUG >= 3
fprintf(stderr, "Everything connected, starting to send video data...\n");
#endif
inited = true;
}
DDV::Socket Socket; ///< Socket connected to user
std::string streamname = "/tmp/shared_socket"; ///< Stream that will be opened
void parseChunk();
int Connector_RTMP(DDV::Socket conn);
};//Connector_RTMP namespace;
retval = epoll_wait(sspoller, events, 1, 1);
switch (SS.ready()){
case -1:
#if DEBUG >= 1
fprintf(stderr, "Source socket is disconnected.\n");
#endif
Socket.close();//disconnect user
break;
case 0: break;//not ready yet
default:
if (tag.SockLoader(SS)){//able to read a full packet?
ts = tag.tagTime();
if (ts != 0){
if (fts == 0){fts = ts;ftst = getNowMS();}
ts -= fts;
tag.tagTime(ts);
ts += ftst;
}else{
ftst = getNowMS();
tag.tagTime(ftst);
}
SendMedia((unsigned char)tag.data[0], (unsigned char *)tag.data+11, tag.len-15, ts);
#if DEBUG >= 5
//write whole stream to a file
if (tmpfile == 0){
sprintf(tmpstr, "./tmpfile_socket_%i.flv", CONN_fd);
tmpfile = fopen(tmpstr, "w");
fwrite(FLVHeader, 13, 1, tmpfile);
}
fwrite(tag->data, tag->len, 1, tmpfile);
#endif
#if DEBUG >= 4
fprintf(stderr, "Sent a tag to %i\n", CONN_fd);
#endif
}
break;
}
}
//send ACK if we received a whole window
if ((rec_cnt - rec_window_at > rec_window_size)){
rec_window_at = rec_cnt;
SendCTL(3, rec_cnt);//send ack (msg 3)
}
}
SS.close();
Socket.close();
#if DEBUG >= 5
fclose(tmpfile);
/// Main Connector_RTMP function
int Connector_RTMP::Connector_RTMP(DDV::Socket conn){
Socket = conn;
unsigned int ts;
unsigned int fts = 0;
unsigned int ftst;
DDV::Socket SS;
FLV::Tag tag;
//first timestamp set
RTMPStream::firsttime = RTMPStream::getNowMS();
while (RTMPStream::handshake_in.size() < 1537){
Socket.read(RTMPStream::handshake_in);
}
if (RTMPStream::doHandshake()){
Socket.write(RTMPStream::handshake_out);
Socket.read((char*)RTMPStream::handshake_in.c_str(), 1536);
RTMPStream::rec_cnt += 1536;
#if DEBUG >= 4
fprintf(stderr, "Handshake succcess!\n");
#endif
}else{
#if DEBUG >= 1
if (FLV::Parse_Error){fprintf(stderr, "FLV Parse Error\n");}
fprintf(stderr, "User %i disconnected.\n", conn.getSocket());
if (inited){
fprintf(stderr, "Status was: inited\n");
}else{
if (ready4data){
fprintf(stderr, "Status was: ready4data\n");
}else{
fprintf(stderr, "Status was: connected\n");
}
}
fprintf(stderr, "Handshake fail!\n");
#endif
return 0;
}//Connector_RTMP
}
int retval;
int poller = epoll_create(1);
int sspoller = epoll_create(1);
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = Socket.getSocket();
epoll_ctl(poller, EPOLL_CTL_ADD, Socket.getSocket(), &ev);
struct epoll_event events[1];
while (Socket.connected() && !FLV::Parse_Error){
//only parse input if available or not yet init'ed
//rightnow = getNowMS();
retval = epoll_wait(poller, events, 1, 1);
if ((retval > 0) || !ready4data){// || (snd_cnt - snd_window_at >= snd_window_size)
switch (Socket.ready()){
case -1: break; //disconnected
case 0: break; //not ready yet
default: parseChunk(); break; //new data is waiting
}
}
if (ready4data){
if (!inited){
//we are ready, connect the socket!
SS = DDV::Socket(streamname);
if (!SS.connected()){
#if DEBUG >= 1
fprintf(stderr, "Could not connect to server!\n");
#endif
Socket.close();//disconnect user
break;
}
ev.events = EPOLLIN;
ev.data.fd = SS.getSocket();
epoll_ctl(sspoller, EPOLL_CTL_ADD, SS.getSocket(), &ev);
#if DEBUG >= 3
fprintf(stderr, "Everything connected, starting to send video data...\n");
#endif
inited = true;
}
retval = epoll_wait(sspoller, events, 1, 1);
switch (SS.ready()){
case -1:
#if DEBUG >= 1
fprintf(stderr, "Source socket is disconnected.\n");
#endif
Socket.close();//disconnect user
break;
case 0: break;//not ready yet
default:
if (tag.SockLoader(SS)){//able to read a full packet?
ts = tag.tagTime();
if (ts != 0){
if (fts == 0){fts = ts;ftst = RTMPStream::getNowMS();}
ts -= fts;
tag.tagTime(ts);
ts += ftst;
}else{
ftst = RTMPStream::getNowMS();
tag.tagTime(ftst);
}
Socket.write(RTMPStream::SendMedia((unsigned char)tag.data[0], (unsigned char *)tag.data+11, tag.len-15, ts));
#if DEBUG >= 4
fprintf(stderr, "Sent a tag to %i\n", Socket.getSocket());
#endif
}
break;
}
}
}
SS.close();
Socket.close();
#if DEBUG >= 1
if (FLV::Parse_Error){fprintf(stderr, "FLV Parse Error: %s\n", FLV::Error_Str.c_str());}
fprintf(stderr, "User %i disconnected.\n", conn.getSocket());
if (inited){
fprintf(stderr, "Status was: inited\n");
}else{
if (ready4data){
fprintf(stderr, "Status was: ready4data\n");
}else{
fprintf(stderr, "Status was: connected\n");
}
}
#endif
return 0;
}//Connector_RTMP
/// Tries to get and parse one RTMP chunk at a time.
void Connector_RTMP::parseChunk(){
static RTMPStream::Chunk next;
static std::string inbuffer;
static AMF::Object amfdata("empty", AMF::AMF0_DDV_CONTAINER);
static AMF::Object amfelem("empty", AMF::AMF0_DDV_CONTAINER);
if (!Connector_RTMP::Socket.read(inbuffer)){return;} //try to get more data
while (next.Parse(inbuffer)){
//send ACK if we received a whole window
if ((RTMPStream::rec_cnt - RTMPStream::rec_window_at > RTMPStream::rec_window_size)){
RTMPStream::rec_window_at = RTMPStream::rec_cnt;
Socket.write(RTMPStream::SendCTL(3, RTMPStream::rec_cnt));//send ack (msg 3)
}
switch (next.msg_type_id){
case 0://does not exist
break;//happens when connection breaks unexpectedly
case 1://set chunk size
RTMPStream::chunk_rec_max = ntohl(*(int*)next.data.c_str());
#if DEBUG >= 4
fprintf(stderr, "CTRL: Set chunk size: %i\n", RTMPStream::chunk_rec_max);
#endif
break;
case 2://abort message - we ignore this one
#if DEBUG >= 4
fprintf(stderr, "CTRL: Abort message\n");
#endif
//4 bytes of stream id to drop
break;
case 3://ack
#if DEBUG >= 4
fprintf(stderr, "CTRL: Acknowledgement\n");
#endif
RTMPStream::snd_window_at = ntohl(*(int*)next.data.c_str());
RTMPStream::snd_window_at = RTMPStream::snd_cnt;
break;
case 4:{
#if DEBUG >= 4
short int ucmtype = ntohs(*(short int*)next.data.c_str());
fprintf(stderr, "CTRL: User control message %hi\n", ucmtype);
#endif
//2 bytes event type, rest = event data
//types:
//0 = stream begin, 4 bytes ID
//1 = stream EOF, 4 bytes ID
//2 = stream dry, 4 bytes ID
//3 = setbufferlen, 4 bytes ID, 4 bytes length
//4 = streamisrecorded, 4 bytes ID
//6 = pingrequest, 4 bytes data
//7 = pingresponse, 4 bytes data
//we don't need to process this
} break;
case 5://window size of other end
#if DEBUG >= 4
fprintf(stderr, "CTRL: Window size\n");
#endif
RTMPStream::rec_window_size = ntohl(*(int*)next.data.c_str());
RTMPStream::rec_window_at = RTMPStream::rec_cnt;
Socket.write(RTMPStream::SendCTL(3, RTMPStream::rec_cnt));//send ack (msg 3)
break;
case 6:
#if DEBUG >= 4
fprintf(stderr, "CTRL: Set peer bandwidth\n");
#endif
//4 bytes window size, 1 byte limit type (ignored)
RTMPStream::snd_window_size = ntohl(*(int*)next.data.c_str());
Socket.write(RTMPStream::SendCTL(5, RTMPStream::snd_window_size));//send window acknowledgement size (msg 5)
break;
case 8:
#if DEBUG >= 4
fprintf(stderr, "Received audio data\n");
#endif
break;
case 9:
#if DEBUG >= 4
fprintf(stderr, "Received video data\n");
#endif
break;
case 15:
#if DEBUG >= 4
fprintf(stderr, "Received AFM3 data message\n");
#endif
break;
case 16:
#if DEBUG >= 4
fprintf(stderr, "Received AFM3 shared object\n");
#endif
break;
case 17:
#if DEBUG >= 4
fprintf(stderr, "Received AFM3 command message\n");
#endif
break;
case 18:
#if DEBUG >= 4
fprintf(stderr, "Received AFM0 data message\n");
#endif
break;
case 19:
#if DEBUG >= 4
fprintf(stderr, "Received AFM0 shared object\n");
#endif
break;
case 20:{//AMF0 command message
bool parsed = false;
amfdata = AMF::parse(next.data);
#if DEBUG >= 4
amfdata.Print();
#endif
if (amfdata.getContentP(0)->StrValue() == "connect"){
#if DEBUG >= 4
int tmpint;
tmpint = amfdata.getContentP(2)->getContentP("videoCodecs")->NumValue();
if (tmpint & 0x04){fprintf(stderr, "Sorensen video support detected\n");}
if (tmpint & 0x80){fprintf(stderr, "H264 video support detected\n");}
tmpint = amfdata.getContentP(2)->getContentP("audioCodecs")->NumValue();
if (tmpint & 0x04){fprintf(stderr, "MP3 audio support detected\n");}
if (tmpint & 0x400){fprintf(stderr, "AAC video support detected\n");}
#endif
Socket.write(RTMPStream::SendCTL(6, RTMPStream::rec_window_size, 0));//send peer bandwidth (msg 6)
Socket.write(RTMPStream::SendCTL(5, RTMPStream::snd_window_size));//send window acknowledgement size (msg 5)
Socket.write(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
//send a _result reply
AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
amfreply.addContent(AMF::Object("", "_result"));//result success
amfreply.addContent(amfdata.getContent(1));//same transaction ID
// amfreply.addContent(AMFType("", (double)0, 0x05));//null - command info
amfreply.addContent(AMF::Object(""));//server properties
amfreply.getContentP(2)->addContent(AMF::Object("fmsVer", "FMS/3,5,2,654"));//stolen from examples
amfreply.getContentP(2)->addContent(AMF::Object("capabilities", (double)31));//stolen from examples
amfreply.getContentP(2)->addContent(AMF::Object("mode", (double)1));//stolen from examples
amfreply.getContentP(2)->addContent(AMF::Object("objectEncoding", (double)0));
amfreply.addContent(AMF::Object(""));//info
amfreply.getContentP(3)->addContent(AMF::Object("level", "status"));
amfreply.getContentP(3)->addContent(AMF::Object("code", "NetConnection.Connect.Success"));
amfreply.getContentP(3)->addContent(AMF::Object("description", "Connection succeeded."));
#if DEBUG >= 4
amfreply.Print();
#endif
Socket.write(RTMPStream::SendChunk(3, 20, next.msg_stream_id, amfreply.Pack()));
//send onBWDone packet
//amfreply = AMFType("container", (unsigned char)0xFF);
//amfreply.addContent(AMFType("", "onBWDone"));//result success
//amfreply.addContent(AMFType("", (double)0));//zero
//amfreply.addContent(AMFType("", (double)0, 0x05));//null
//SendChunk(3, 20, next.msg_stream_id, amfreply.Pack());
parsed = true;
}//connect
if (amfdata.getContentP(0)->StrValue() == "createStream"){
//send a _result reply
AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
amfreply.addContent(AMF::Object("", "_result"));//result success
amfreply.addContent(amfdata.getContent(1));//same transaction ID
amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
amfreply.addContent(AMF::Object("", (double)1));//stream ID - we use 1
#if DEBUG >= 4
amfreply.Print();
#endif
Socket.write(RTMPStream::SendChunk(3, 20, next.msg_stream_id, amfreply.Pack()));
Socket.write(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
parsed = true;
}//createStream
if ((amfdata.getContentP(0)->StrValue() == "getStreamLength") || (amfdata.getContentP(0)->StrValue() == "getMovLen")){
//send a _result reply
AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
amfreply.addContent(AMF::Object("", "_result"));//result success
amfreply.addContent(amfdata.getContent(1));//same transaction ID
amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
amfreply.addContent(AMF::Object("", (double)0));//zero length
#if DEBUG >= 4
amfreply.Print();
#endif
Socket.write(RTMPStream::SendChunk(3, 20, next.msg_stream_id, amfreply.Pack()));
parsed = true;
}//getStreamLength
if (amfdata.getContentP(0)->StrValue() == "checkBandwidth"){
//send a _result reply
AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
amfreply.addContent(AMF::Object("", "_result"));//result success
amfreply.addContent(amfdata.getContent(1));//same transaction ID
amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
#if DEBUG >= 4
amfreply.Print();
#endif
Socket.write(RTMPStream::SendChunk(3, 20, 1, amfreply.Pack()));
parsed = true;
}//checkBandwidth
if ((amfdata.getContentP(0)->StrValue() == "play") || (amfdata.getContentP(0)->StrValue() == "play2")){
//send streambegin
streamname = amfdata.getContentP(3)->StrValue();
for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){
if (!isalpha(*i) && !isdigit(*i)){streamname.erase(i);}else{*i=tolower(*i);}
}
streamname = "/tmp/shared_socket_" + streamname;
Socket.write(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1
//send a status reply
AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER);
amfreply.addContent(AMF::Object("", "onStatus"));//status reply
amfreply.addContent(amfdata.getContent(1));//same transaction ID
amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
amfreply.addContent(AMF::Object(""));//info
amfreply.getContentP(3)->addContent(AMF::Object("level", "status"));
amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Reset"));
amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing and resetting..."));
amfreply.getContentP(3)->addContent(AMF::Object("details", "PLS"));
amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1));
#if DEBUG >= 4
amfreply.Print();
#endif
Socket.write(RTMPStream::SendChunk(4, 20, next.msg_stream_id, amfreply.Pack()));
amfreply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER);
amfreply.addContent(AMF::Object("", "onStatus"));//status reply
amfreply.addContent(amfdata.getContent(1));//same transaction ID
amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info
amfreply.addContent(AMF::Object(""));//info
amfreply.getContentP(3)->addContent(AMF::Object("level", "status"));
amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Start"));
amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing!"));
amfreply.getContentP(3)->addContent(AMF::Object("details", "PLS"));
amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1));
#if DEBUG >= 4
amfreply.Print();
#endif
Socket.write(RTMPStream::SendChunk(4, 20, 1, amfreply.Pack()));
//No clue what this does. Most real servers send it, though...
// amfreply = AMFType("container", (unsigned char)0xFF);
// amfreply.addContent(AMFType("", "|RtmpSampleAccess"));//status reply
// amfreply.addContent(AMFType("", (double)1, 0x01));//bool true - audioaccess
// amfreply.addContent(AMFType("", (double)1, 0x01));//bool true - videoaccess
// SendChunk(4, 20, next.msg_stream_id, amfreply.Pack());
RTMPStream::chunk_snd_max = 1024*1024;
Socket.write(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max));//send chunk size max (msg 1)
Connector_RTMP::ready4data = true;//start sending video data!
parsed = true;
}//createStream
#if DEBUG >= 3
fprintf(stderr, "AMF0 command: %s\n", amfdata.getContentP(0)->StrValue().c_str());
#endif
if (!parsed){
#if DEBUG >= 2
fprintf(stderr, "AMF0 command not processed! :(\n");
#endif
}
} break;
case 22:
#if DEBUG >= 4
fprintf(stderr, "Received aggregate message\n");
#endif
break;
default:
#if DEBUG >= 1
fprintf(stderr, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.\n");
#endif
Connector_RTMP::stopparsing = true;
break;
}
}
}//parseChunk
};//Connector_RTMP namespace
// Load main server setup file, default port 1935, handler is Connector_RTMP::Connector_RTMP
#define DEFAULT_PORT 1935

View file

@ -1,254 +0,0 @@
#include "chunkstream.cpp" //chunkstream decoding
#include "amf.cpp" //simple AMF0 parsing
std::string streamname = "/tmp/shared_socket";
//gets and parses one chunk
void parseChunk(){
static chunkpack next;
static AMFType amfdata("empty", (unsigned char)AMF0_DDV_CONTAINER);
static AMFType amfelem("empty", (unsigned char)AMF0_DDV_CONTAINER);
next = getWholeChunk();
switch (next.msg_type_id){
case 0://does not exist
break;//happens when connection breaks unexpectedly
case 1://set chunk size
chunk_rec_max = ntohl(*(int*)next.data);
#if DEBUG >= 4
fprintf(stderr, "CTRL: Set chunk size: %i\n", chunk_rec_max);
#endif
break;
case 2://abort message - we ignore this one
#if DEBUG >= 4
fprintf(stderr, "CTRL: Abort message\n");
#endif
//4 bytes of stream id to drop
break;
case 3://ack
#if DEBUG >= 4
fprintf(stderr, "CTRL: Acknowledgement\n");
#endif
snd_window_at = ntohl(*(int*)next.data);
snd_window_at = snd_cnt;
break;
case 4:{
#if DEBUG >= 4
short int ucmtype = ntohs(*(short int*)next.data);
fprintf(stderr, "CTRL: User control message %hi\n", ucmtype);
#endif
//2 bytes event type, rest = event data
//types:
//0 = stream begin, 4 bytes ID
//1 = stream EOF, 4 bytes ID
//2 = stream dry, 4 bytes ID
//3 = setbufferlen, 4 bytes ID, 4 bytes length
//4 = streamisrecorded, 4 bytes ID
//6 = pingrequest, 4 bytes data
//7 = pingresponse, 4 bytes data
//we don't need to process this
} break;
case 5://window size of other end
#if DEBUG >= 4
fprintf(stderr, "CTRL: Window size\n");
#endif
rec_window_size = ntohl(*(int*)next.data);
rec_window_at = rec_cnt;
SendCTL(3, rec_cnt);//send ack (msg 3)
break;
case 6:
#if DEBUG >= 4
fprintf(stderr, "CTRL: Set peer bandwidth\n");
#endif
//4 bytes window size, 1 byte limit type (ignored)
snd_window_size = ntohl(*(int*)next.data);
SendCTL(5, snd_window_size);//send window acknowledgement size (msg 5)
break;
case 8:
#if DEBUG >= 4
fprintf(stderr, "Received audio data\n");
#endif
break;
case 9:
#if DEBUG >= 4
fprintf(stderr, "Received video data\n");
#endif
break;
case 15:
#if DEBUG >= 4
fprintf(stderr, "Received AFM3 data message\n");
#endif
break;
case 16:
#if DEBUG >= 4
fprintf(stderr, "Received AFM3 shared object\n");
#endif
break;
case 17:
#if DEBUG >= 4
fprintf(stderr, "Received AFM3 command message\n");
#endif
break;
case 18:
#if DEBUG >= 4
fprintf(stderr, "Received AFM0 data message\n");
#endif
break;
case 19:
#if DEBUG >= 4
fprintf(stderr, "Received AFM0 shared object\n");
#endif
break;
case 20:{//AMF0 command message
bool parsed = false;
amfdata = parseAMF(next.data, next.real_len);
#if DEBUG >= 4
amfdata.Print();
#endif
if (amfdata.getContentP(0)->StrValue() == "connect"){
#if DEBUG >= 4
int tmpint;
tmpint = amfdata.getContentP(2)->getContentP("videoCodecs")->NumValue();
if (tmpint & 0x04){fprintf(stderr, "Sorensen video support detected\n");}
if (tmpint & 0x80){fprintf(stderr, "H264 video support detected\n");}
tmpint = amfdata.getContentP(2)->getContentP("audioCodecs")->NumValue();
if (tmpint & 0x04){fprintf(stderr, "MP3 audio support detected\n");}
if (tmpint & 0x400){fprintf(stderr, "AAC video support detected\n");}
#endif
SendCTL(6, rec_window_size, 0);//send peer bandwidth (msg 6)
SendCTL(5, snd_window_size);//send window acknowledgement size (msg 5)
SendUSR(0, 1);//send UCM StreamBegin (0), stream 1
//send a _result reply
AMFType amfreply("container", (unsigned char)AMF0_DDV_CONTAINER);
amfreply.addContent(AMFType("", "_result"));//result success
amfreply.addContent(amfdata.getContent(1));//same transaction ID
// amfreply.addContent(AMFType("", (double)0, 0x05));//null - command info
amfreply.addContent(AMFType(""));//server properties
amfreply.getContentP(2)->addContent(AMFType("fmsVer", "FMS/3,5,2,654"));//stolen from examples
amfreply.getContentP(2)->addContent(AMFType("capabilities", (double)31));//stolen from examples
amfreply.getContentP(2)->addContent(AMFType("mode", (double)1));//stolen from examples
amfreply.getContentP(2)->addContent(AMFType("objectEncoding", (double)0));
amfreply.addContent(AMFType(""));//info
amfreply.getContentP(3)->addContent(AMFType("level", "status"));
amfreply.getContentP(3)->addContent(AMFType("code", "NetConnection.Connect.Success"));
amfreply.getContentP(3)->addContent(AMFType("description", "Connection succeeded."));
#if DEBUG >= 4
amfreply.Print();
#endif
SendChunk(3, 20, next.msg_stream_id, amfreply.Pack());
//send onBWDone packet
//amfreply = AMFType("container", (unsigned char)0xFF);
//amfreply.addContent(AMFType("", "onBWDone"));//result success
//amfreply.addContent(AMFType("", (double)0));//zero
//amfreply.addContent(AMFType("", (double)0, 0x05));//null
//SendChunk(3, 20, next.msg_stream_id, amfreply.Pack());
parsed = true;
}//connect
if (amfdata.getContentP(0)->StrValue() == "createStream"){
//send a _result reply
AMFType amfreply("container", (unsigned char)AMF0_DDV_CONTAINER);
amfreply.addContent(AMFType("", "_result"));//result success
amfreply.addContent(amfdata.getContent(1));//same transaction ID
amfreply.addContent(AMFType("", (double)0, AMF0_NULL));//null - command info
amfreply.addContent(AMFType("", (double)1));//stream ID - we use 1
#if DEBUG >= 4
amfreply.Print();
#endif
SendChunk(3, 20, next.msg_stream_id, amfreply.Pack());
SendUSR(0, 1);//send UCM StreamBegin (0), stream 1
parsed = true;
}//createStream
if ((amfdata.getContentP(0)->StrValue() == "getStreamLength") || (amfdata.getContentP(0)->StrValue() == "getMovLen")){
//send a _result reply
AMFType amfreply("container", (unsigned char)AMF0_DDV_CONTAINER);
amfreply.addContent(AMFType("", "_result"));//result success
amfreply.addContent(amfdata.getContent(1));//same transaction ID
amfreply.addContent(AMFType("", (double)0, AMF0_NULL));//null - command info
amfreply.addContent(AMFType("", (double)0));//zero length
#if DEBUG >= 4
amfreply.Print();
#endif
SendChunk(3, 20, next.msg_stream_id, amfreply.Pack());
parsed = true;
}//getStreamLength
if (amfdata.getContentP(0)->StrValue() == "checkBandwidth"){
//send a _result reply
AMFType amfreply("container", (unsigned char)AMF0_DDV_CONTAINER);
amfreply.addContent(AMFType("", "_result"));//result success
amfreply.addContent(amfdata.getContent(1));//same transaction ID
amfreply.addContent(AMFType("", (double)0, AMF0_NULL));//null - command info
amfreply.addContent(AMFType("", (double)0, AMF0_NULL));//null - command info
#if DEBUG >= 4
amfreply.Print();
#endif
SendChunk(3, 20, 1, amfreply.Pack());
parsed = true;
}//checkBandwidth
if ((amfdata.getContentP(0)->StrValue() == "play") || (amfdata.getContentP(0)->StrValue() == "play2")){
//send streambegin
streamname = amfdata.getContentP(3)->StrValue();
for (std::string::iterator i=streamname.end()-1; i>=streamname.begin(); --i){
if (!isalpha(*i) && !isdigit(*i)){streamname.erase(i);}else{*i=tolower(*i);}
}
streamname = "/tmp/shared_socket_" + streamname;
SendUSR(0, 1);//send UCM StreamBegin (0), stream 1
//send a status reply
AMFType amfreply("container", (unsigned char)AMF0_DDV_CONTAINER);
amfreply.addContent(AMFType("", "onStatus"));//status reply
amfreply.addContent(amfdata.getContent(1));//same transaction ID
amfreply.addContent(AMFType("", (double)0, AMF0_NULL));//null - command info
amfreply.addContent(AMFType(""));//info
amfreply.getContentP(3)->addContent(AMFType("level", "status"));
amfreply.getContentP(3)->addContent(AMFType("code", "NetStream.Play.Reset"));
amfreply.getContentP(3)->addContent(AMFType("description", "Playing and resetting..."));
amfreply.getContentP(3)->addContent(AMFType("details", "PLS"));
amfreply.getContentP(3)->addContent(AMFType("clientid", (double)1));
#if DEBUG >= 4
amfreply.Print();
#endif
SendChunk(4, 20, next.msg_stream_id, amfreply.Pack());
amfreply = AMFType("container", (unsigned char)AMF0_DDV_CONTAINER);
amfreply.addContent(AMFType("", "onStatus"));//status reply
amfreply.addContent(amfdata.getContent(1));//same transaction ID
amfreply.addContent(AMFType("", (double)0, AMF0_NULL));//null - command info
amfreply.addContent(AMFType(""));//info
amfreply.getContentP(3)->addContent(AMFType("level", "status"));
amfreply.getContentP(3)->addContent(AMFType("code", "NetStream.Play.Start"));
amfreply.getContentP(3)->addContent(AMFType("description", "Playing!"));
amfreply.getContentP(3)->addContent(AMFType("details", "PLS"));
amfreply.getContentP(3)->addContent(AMFType("clientid", (double)1));
#if DEBUG >= 4
amfreply.Print();
#endif
SendChunk(4, 20, 1, amfreply.Pack());
//No clue what this does. Most real servers send it, though...
// amfreply = AMFType("container", (unsigned char)0xFF);
// amfreply.addContent(AMFType("", "|RtmpSampleAccess"));//status reply
// amfreply.addContent(AMFType("", (double)1, 0x01));//bool true - audioaccess
// amfreply.addContent(AMFType("", (double)1, 0x01));//bool true - videoaccess
// SendChunk(4, 20, next.msg_stream_id, amfreply.Pack());
chunk_snd_max = 1024*1024;
SendCTL(1, chunk_snd_max);//send chunk size max (msg 1)
ready4data = true;//start sending video data!
parsed = true;
}//createStream
#if DEBUG >= 3
fprintf(stderr, "AMF0 command: %s\n", amfdata.getContentP(0)->StrValue().c_str());
#endif
if (!parsed){
#if DEBUG >= 2
fprintf(stderr, "AMF0 command not processed! :(\n");
#endif
}
} break;
case 22:
#if DEBUG >= 4
fprintf(stderr, "Received aggregate message\n");
#endif
break;
default:
#if DEBUG >= 1
fprintf(stderr, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.\n");
#endif
stopparsing = true;
break;
}
}//parseChunk