diff --git a/Connector_RTMP/main.cpp b/Connector_RTMP/main.cpp index d18eb917..fd0d2ae0 100644 --- a/Connector_RTMP/main.cpp +++ b/Connector_RTMP/main.cpp @@ -41,7 +41,7 @@ int main(int argc, char ** argv){ sigaction (SIGHUP, &new_action, NULL); sigaction (SIGTERM, &new_action, NULL); - server_socket = DDV_Listen(1935); + server_socket = DDV_Listen(1936); if ((argc < 2) || (argv[1] == "nd")){ if (server_socket > 0){daemon(1, 0);}else{return 1;} } diff --git a/Connector_RTMPf/Makefile b/Connector_RTMPf/Makefile new file mode 100644 index 00000000..113d8023 --- /dev/null +++ b/Connector_RTMPf/Makefile @@ -0,0 +1,23 @@ +SRC = main.cpp ../sockets/sw_base.cpp ../sockets/sw_inet.cpp ../sockets/sw_unix.cpp +OBJ = $(SRC:.cpp=.o) +OUT = Connector_RTMPf +INCLUDES = +CCFLAGS = -Wall -Wextra -funsigned-char -g +CC = $(CROSS)g++ +LD = $(CROSS)ld +AR = $(CROSS)ar +LIBS = -lssl -lcrypto +.SUFFIXES: .cpp +.PHONY: clean default +default: $(OUT) +.cpp.o: + $(CC) $(INCLUDES) $(CCFLAGS) $(LIBS) -c $< -o $@ +$(OUT): $(OBJ) chunkstream.cpp parsechunks.cpp handshake.cpp crypto.cpp amf.cpp + $(CC) $(LIBS) -o $(OUT) $(OBJ) +clean: + rm -rf $(OBJ) $(OUT) Makefile.bak *~ +run-test: $(OUT) + rm -rf ./meh + mkfifo ./meh + cat ./meh & + nc -l -p 1935 -e './Connector_RTMPf 2>./meh' diff --git a/Connector_RTMPf/amf.cpp b/Connector_RTMPf/amf.cpp new file mode 100644 index 00000000..70aab059 --- /dev/null +++ b/Connector_RTMPf/amf.cpp @@ -0,0 +1,285 @@ +#include +#include +#include + +class AMFType { + public: + std::string Indice(){return myIndice;}; + unsigned char GetType(){return myType;}; + double NumValue(){return numval;}; + std::string StrValue(){return strval;}; + const char * Str(){return strval.c_str();}; + int hasContent(){ + if (!contents){return 0;} + return contents->size(); + }; + void addContent(AMFType c){if (contents != 0){contents->push_back(c);}}; + AMFType* getContentP(int i){if (contents != 0){return &contents->at(i);}else{return 0;}}; + AMFType getContent(int i){if (contents != 0){return contents->at(i);}else{return AMFType("error");}}; + AMFType* getContentP(std::string s){ + if (contents != 0){ + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ + if (it->Indice() == s){ + return &(*it); + } + } + } + return this; + }; + AMFType getContent(std::string s){ + if (contents != 0){ + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ + if (it->Indice() == s){ + return *it; + } + } + } + return AMFType("error"); + }; + AMFType(std::string indice, double val, unsigned char setType = 0x00){//num type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = val; + contents = 0; + }; + AMFType(std::string indice, std::string val, unsigned char setType = 0x02){//str type initializer + myIndice = indice; + myType = setType; + strval = val; + numval = 0; + contents = 0; + }; + AMFType(std::string indice, unsigned char setType = 0x03){//object type initializer + myIndice = indice; + myType = setType; + strval = ""; + numval = 0; + contents = new std::vector; + }; + ~AMFType(){if (contents != 0){delete contents;contents=0;}}; + AMFType& operator=(const AMFType &a) { + myIndice = a.myIndice; + myType = a.myType; + strval = a.strval; + numval = a.numval; + if (contents){ + if (a.contents != contents){ + delete contents; + if (a.contents){ + contents = new std::vector; + for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ + contents->push_back(*it); + } + }else{ + contents = 0; + } + } + }else{ + if (a.contents){ + contents = new std::vector; + for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ + contents->push_back(*it); + } + } + } + return *this; + };//= operator + AMFType(const AMFType &a){ + myIndice = a.myIndice; + myType = a.myType; + strval = a.strval; + numval = a.numval; + if (a.contents){ + contents = new std::vector; + for (std::vector::iterator it = a.contents->begin(); it < a.contents->end(); it++){ + contents->push_back(*it); + } + }else{contents = 0;} + };//copy constructor + void Print(std::string indent = ""){ + std::cerr << indent; + switch (myType){ + case 0x00: std::cerr << "Number"; break; + case 0x01: std::cerr << "Bool"; break; + case 0x02://short string + case 0x0C: std::cerr << "String"; break; + case 0x03: std::cerr << "Object"; break; + case 0x08: std::cerr << "ECMA Array"; break; + case 0x05: std::cerr << "Null"; break; + case 0x06: std::cerr << "Undefined"; break; + case 0x0D: std::cerr << "Unsupported"; break; + case 0xFF: std::cerr << "Container"; break; + } + std::cerr << " " << myIndice << " "; + switch (myType){ + case 0x00: case 0x01: std::cerr << numval; break; + case 0x02: case 0x0C: std::cerr << strval; break; + } + std::cerr << std::endl; + if (contents){ + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){it->Print(indent+" ");} + } + };//print + std::string Pack(){ + std::string r = ""; + if ((myType == 0x02) && (strval.size() > 0xFFFF)){myType = 0x0C;} + if (myType != 0xFF){r += myType;} + switch (myType){ + case 0x00://number + r += *(((char*)&numval)+7); r += *(((char*)&numval)+6); + r += *(((char*)&numval)+5); r += *(((char*)&numval)+4); + r += *(((char*)&numval)+3); r += *(((char*)&numval)+2); + r += *(((char*)&numval)+1); r += *(((char*)&numval)); + break; + case 0x01://bool + r += (char)numval; + break; + case 0x02://short string + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case 0x0C://long string + r += strval.size() / (256*256*256); + r += strval.size() / (256*256); + r += strval.size() / 256; + r += strval.size() % 256; + r += strval; + break; + case 0x03://object + if (contents){ + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); + } + } + r += (char)0; r += (char)0; r += (char)9; + break; + case 0x08:{//array + int arrlen = 0; + if (contents){ + arrlen = getContentP("length")->NumValue(); + r += arrlen / (256*256*256); r += arrlen / (256*256); r += arrlen / 256; r += arrlen % 256; + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ + r += it->Indice().size() / 256; + r += it->Indice().size() % 256; + r += it->Indice(); + r += it->Pack(); + } + }else{ + r += (char)0; r += (char)0; r += (char)0; r += (char)0; + } + r += (char)0; r += (char)0; r += (char)9; + } break; + case 0xFF://container - our own type - do not send, only send contents + if (contents){ + for (std::vector::iterator it = contents->begin(); it != contents->end(); it++){ + r += it->Pack(); + } + } + break; + } + return r; + };//pack + protected: + std::string myIndice; + unsigned char myType; + std::string strval; + double numval; + std::vector * contents; +};//AMFType + +AMFType parseOneAMF(const unsigned char *& data, unsigned int &len, unsigned int &i, std::string name){ + char * helperchar = 0; + std::string tmpstr; + unsigned int tmpi = 0; + unsigned char tmpdbl[8]; + switch (data[i]){ + case 0x00://number + tmpdbl[7] = data[i+1]; + tmpdbl[6] = data[i+2]; + tmpdbl[5] = data[i+3]; + tmpdbl[4] = data[i+4]; + tmpdbl[3] = data[i+5]; + tmpdbl[2] = data[i+6]; + tmpdbl[1] = data[i+7]; + tmpdbl[0] = data[i+8]; + i+=9; + return AMFType(name, *(double*)tmpdbl, 0x00); + break; + case 0x01://bool + i+=2; + if (data[i-1] == 0){ + return AMFType(name, (double)0, 0x01); + }else{ + return AMFType(name, (double)1, 0x01); + } + break; + case 0x0C://long string + tmpi = data[i+1]*256*256*256+data[i+2]*256*256+data[i+3]*256+data[i+4]; + helperchar = (char*)malloc(tmpi+1); + memcpy(helperchar, data+i+5, tmpi); + helperchar[tmpi] = 0; + tmpstr = helperchar; + free(helperchar); + i += tmpi + 5; + return AMFType(name, tmpstr, 0x0C); + break; + case 0x02://string + tmpi = data[i+1]*256+data[i+2]; + helperchar = (char*)malloc(tmpi+1); + memcpy(helperchar, data+i+3, tmpi); + helperchar[tmpi] = 0; + tmpstr = helperchar; + free(helperchar); + i += tmpi + 3; + return AMFType(name, tmpstr, 0x02); + break; + case 0x05://null + case 0x06://undefined + case 0x0D://unsupported + ++i; + return AMFType(name, (double)0, data[i-1]); + break; + case 0x03:{//object + ++i; + AMFType ret = AMFType(name, data[i-1]); + while (data[i] + data[i+1] != 0){ + tmpi = data[i]*256+data[i+1]; + tmpstr = (char*)(data+i+2); + i += tmpi + 2; + ret.addContent(parseOneAMF(data, len, i, tmpstr)); + } + i += 3; + return ret; + } break; + case 0x08:{//ECMA array + ++i; + AMFType ret = AMFType(name, data[i-1]); + i += 4; + while (data[i] + data[i+1] != 0){ + tmpi = data[i]*256+data[i+1]; + tmpstr = (char*)(data+i+2); + i += tmpi + 2; + ret.addContent(parseOneAMF(data, len, i, tmpstr)); + } + i += 3; + return ret; + } break; + } + #ifdef DEBUG + fprintf(stderr, "Error: Unimplemented AMF type %hhx - returning.\n", data[i]); + #endif + return AMFType("error", (unsigned char)0xFF); +}//parseOneAMF + +AMFType parseAMF(const unsigned char * data, unsigned int len){ + AMFType ret("returned", (unsigned char)0xFF);//container type + unsigned int i = 0; + while (i < len){ret.addContent(parseOneAMF(data, len, i, ""));} + return ret; +}//parseAMF +AMFType parseAMF(std::string data){return parseAMF((const unsigned char*)data.c_str(), data.size());} diff --git a/Connector_RTMPf/chunkstream.cpp b/Connector_RTMPf/chunkstream.cpp new file mode 100644 index 00000000..9d9247aa --- /dev/null +++ b/Connector_RTMPf/chunkstream.cpp @@ -0,0 +1,501 @@ +#include +#include +#include +#include +#include + +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 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 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; fwrite(&tmp, 1, 1, stdout); + snd_cnt+=1; + }else{ + if (ch.cs_id <= 255+64){ + tmp = chtype | 0; fwrite(&tmp, 1, 1, stdout); + tmp = ch.cs_id - 64; fwrite(&tmp, 1, 1, stdout); + snd_cnt+=2; + }else{ + tmp = chtype | 1; fwrite(&tmp, 1, 1, stdout); + tmpi = ch.cs_id - 64; + tmp = tmpi % 256; fwrite(&tmp, 1, 1, stdout); + tmp = tmpi / 256; fwrite(&tmp, 1, 1, stdout); + 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); fwrite(&tmp, 1, 1, stdout); + tmp = tmpi / 256; fwrite(&tmp, 1, 1, stdout); + tmp = tmpi % 256; fwrite(&tmp, 1, 1, stdout); + snd_cnt+=3; + }else{ + tmpi = ch.timestamp - prev.timestamp; + if (tmpi >= 0x00ffffff){ntime = tmpi; tmpi = 0x00ffffff;} + tmp = tmpi / (256*256); fwrite(&tmp, 1, 1, stdout); + tmp = tmpi / 256; fwrite(&tmp, 1, 1, stdout); + tmp = tmpi % 256; fwrite(&tmp, 1, 1, stdout); + snd_cnt+=3; + } + if (chtype != 0x80){ + //len + tmpi = ch.len; + tmp = tmpi / (256*256); fwrite(&tmp, 1, 1, stdout); + tmp = tmpi / 256; fwrite(&tmp, 1, 1, stdout); + tmp = tmpi % 256; fwrite(&tmp, 1, 1, stdout); + snd_cnt+=3; + //msg type id + tmp = ch.msg_type_id; fwrite(&tmp, 1, 1, stdout); + snd_cnt+=1; + if (chtype != 0x40){ + //msg stream id + tmp = ch.msg_stream_id % 256; fwrite(&tmp, 1, 1, stdout); + tmp = ch.msg_stream_id / 256; fwrite(&tmp, 1, 1, stdout); + tmp = ch.msg_stream_id / (256*256); fwrite(&tmp, 1, 1, stdout); + tmp = ch.msg_stream_id / (256*256*256); fwrite(&tmp, 1, 1, stdout); + snd_cnt+=4; + } + } + } + //support for 0x00ffffff timestamps + if (ntime){ + tmp = ntime / (256*256*256); fwrite(&tmp, 1, 1, stdout); + tmp = ntime / (256*256); fwrite(&tmp, 1, 1, stdout); + tmp = ntime / 256; fwrite(&tmp, 1, 1, stdout); + tmp = ntime % 256; fwrite(&tmp, 1, 1, stdout); + 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;} + fwrite((ch.data + ch.len_left), 1, tmpi, stdout); + snd_cnt+=tmpi; + ch.len_left += tmpi; + if (ch.len_left < ch.len){ + if (ch.cs_id <= 63){ + tmp = 0xC0 + ch.cs_id; fwrite(&tmp, 1, 1, stdout); + snd_cnt+=1; + }else{ + if (ch.cs_id <= 255+64){ + tmp = 0xC0; fwrite(&tmp, 1, 1, stdout); + tmp = ch.cs_id - 64; fwrite(&tmp, 1, 1, stdout); + snd_cnt+=2; + }else{ + tmp = 0xC1; fwrite(&tmp, 1, 1, stdout); + tmpi = ch.cs_id - 64; + tmp = tmpi % 256; fwrite(&tmp, 1, 1, stdout); + tmp = tmpi / 256; fwrite(&tmp, 1, 1, stdout); + 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; + fread(&(ret.chunktype), 1, 1, stdin); + rec_cnt++; + //read the chunkstream ID properly + switch (ret.chunktype & 0x3F){ + case 0: + fread(&temp, 1, 1, stdin); + rec_cnt++; + ret.cs_id = temp + 64; + break; + case 1: + fread(&temp, 1, 1, stdin); + ret.cs_id = temp + 64; + fread(&temp, 1, 1, stdin); + 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: + fread(&temp, 1, 1, stdin); + ret.timestamp = temp*256*256; + fread(&temp, 1, 1, stdin); + ret.timestamp += temp*256; + fread(&temp, 1, 1, stdin); + ret.timestamp += temp; + fread(&temp, 1, 1, stdin); + ret.len = temp*256*256; + fread(&temp, 1, 1, stdin); + ret.len += temp*256; + fread(&temp, 1, 1, stdin); + ret.len += temp; + ret.len_left = 0; + fread(&temp, 1, 1, stdin); + ret.msg_type_id = temp; + fread(&temp, 1, 1, stdin); + ret.msg_stream_id = temp; + fread(&temp, 1, 1, stdin); + ret.msg_stream_id += temp*256; + fread(&temp, 1, 1, stdin); + ret.msg_stream_id += temp*256*256; + fread(&temp, 1, 1, stdin); + ret.msg_stream_id += temp*256*256*256; + rec_cnt+=11; + break; + case 0x40: + fread(&temp, 1, 1, stdin); + ret.timestamp = temp*256*256; + fread(&temp, 1, 1, stdin); + ret.timestamp += temp*256; + fread(&temp, 1, 1, stdin); + ret.timestamp += temp; + ret.timestamp += prev.timestamp; + fread(&temp, 1, 1, stdin); + ret.len = temp*256*256; + fread(&temp, 1, 1, stdin); + ret.len += temp*256; + fread(&temp, 1, 1, stdin); + ret.len += temp; + ret.len_left = 0; + fread(&temp, 1, 1, stdin); + ret.msg_type_id = temp; + ret.msg_stream_id = prev.msg_stream_id; + rec_cnt+=7; + break; + case 0x80: + fread(&temp, 1, 1, stdin); + ret.timestamp = temp*256*256; + fread(&temp, 1, 1, stdin); + ret.timestamp += temp*256; + fread(&temp, 1, 1, stdin); + 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){ + fread(&temp, 1, 1, stdin); + ret.timestamp = temp*256*256*256; + fread(&temp, 1, 1, stdin); + ret.timestamp += temp*256*256; + fread(&temp, 1, 1, stdin); + ret.timestamp += temp*256; + fread(&temp, 1, 1, stdin); + 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); + fread(ret.data, 1, ret.real_len, stdin); + 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 ch_lst; + std::map::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){ + #ifdef DEBUG + 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 < 10000){ + gwc_next = getChunk(); + ret = AddChunkPart(gwc_next); + scrubChunk(gwc_next); + if (ret){ + gwc_complete = *ret; + free(ret);//cleanup returned chunk + return gwc_complete; + } + if (feof(stdin) != 0){break;} + counter++; + } + gwc_complete.msg_type_id = 0; + return gwc_complete; +}//getWholeChunk diff --git a/Connector_RTMPf/crypto.cpp b/Connector_RTMPf/crypto.cpp new file mode 100644 index 00000000..9f62e5ce --- /dev/null +++ b/Connector_RTMPf/crypto.cpp @@ -0,0 +1,506 @@ +#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); + #ifdef DEBUG + fprintf(stderr, "Client scheme validation %hhi %s\n", scheme, result?"success":"failed"); + #endif + delete[] pTempBuffer; + delete[] pTempHash; + return result; +} diff --git a/Connector_RTMPf/crypto.h b/Connector_RTMPf/crypto.h new file mode 100644 index 00000000..8d36188b --- /dev/null +++ b/Connector_RTMPf/crypto.h @@ -0,0 +1,45 @@ +#ifndef _CRYPTO_H +#define _CRYPTO_H +#define DLLEXP + +#include +#include +#include +#include +#include +#include +#include +#include + +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 */ + diff --git a/Connector_RTMPf/handshake.cpp b/Connector_RTMPf/handshake.cpp new file mode 100644 index 00000000..43143dc8 --- /dev/null +++ b/Connector_RTMPf/handshake.cpp @@ -0,0 +1,137 @@ +#undef OLDHANDSHAKE //change to #define for old handshake method + +char versionstring[] = "PLSRTMPServer"; + +#ifdef OLDHANDSHAKE +struct Handshake { + char Time[4]; + char Zero[4]; + char Random[1528]; +};//Handshake + +bool doHandshake(){ + char Version; + Handshake Client; + Handshake Server; + /** Read C0 **/ + fread(&(Version), 1, 1, stdin); + /** Read C1 **/ + fread(Client.Time, 1, 4, stdin); + fread(Client.Zero, 1, 4, stdin); + fread(Client.Random, 1, 1528, stdin); + 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%13]; + } + /** Send S0 **/ + fwrite(&(Version), 1, 1, stdout); + /** Send S1 **/ + fwrite(Server.Time, 1, 4, stdout); + fwrite(Server.Zero, 1, 4, stdout); + fwrite(Server.Random, 1, 1528, stdout); + /** Flush output, just for certainty **/ + fflush(stdout); + snd_cnt+=1537; + /** Send S2 **/ + fwrite(Client.Time, 1, 4, stdout); + fwrite(Client.Time, 1, 4, stdout); + fwrite(Client.Random, 1, 1528, stdout); + snd_cnt+=1536; + /** Flush, necessary in order to work **/ + fflush(stdout); + /** Read and discard C2 **/ + fread(Client.Time, 1, 4, stdin); + fread(Client.Zero, 1, 4, stdin); + fread(Client.Random, 1, 1528, stdin); + rec_cnt+=1536; + return true; +}//doHandshake + +#else + +#include "crypto.cpp" //cryptography for handshaking + +bool doHandshake(){ + char Version; + /** Read C0 **/ + fread(&Version, 1, 1, stdin); + uint8_t Client[1536]; + uint8_t Server[3072]; + fread(&Client, 1, 1536, stdin); + 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); + #ifdef DEBUG + 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; + + #ifdef DEBUG + 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 **/ + fwrite(&Version, 1, 1, stdout); + fwrite(&Server, 1, 3072, stdout); + snd_cnt+=3073; + /** Flush, necessary in order to work **/ + fflush(stdout); + /** Read and discard C2 **/ + fread(Client, 1, 1536, stdin); + rec_cnt+=1536; + return true; +} + +#endif diff --git a/Connector_RTMPf/main.cpp b/Connector_RTMPf/main.cpp new file mode 100644 index 00000000..14cfea45 --- /dev/null +++ b/Connector_RTMPf/main.cpp @@ -0,0 +1,119 @@ +#undef DEBUG +#include +#include +#include +#include + +//needed for select +#include +#include +#include +#include +#include + +//for connection to server +#include "../sockets/SocketW.h" +bool ready4data = false;//set to true when streaming starts +bool inited = false; +bool stopparsing = false; +timeval lastrec; + +#include "parsechunks.cpp" //chunkstream parsing +#include "handshake.cpp" //handshaking +#include "../util/flv_sock.cpp" //FLV parsing with SocketW + +int main(){ + unsigned int ts; + unsigned int fts = 0; + unsigned int ftst; + SWUnixSocket ss; + fd_set pollset; + struct timeval timeout; + //0 timeout - return immediately after select call + timeout.tv_sec = 1; timeout.tv_usec = 0; + FD_ZERO(&pollset);//clear the polling set + FD_SET(0, &pollset);//add stdin to polling set + + //first timestamp set + firsttime = getNowMS(); + + #ifdef DEBUG + fprintf(stderr, "Doing handshake...\n"); + #endif + if (doHandshake()){ + #ifdef DEBUG + fprintf(stderr, "Handshake succcess!\n"); + #endif + }else{ + #ifdef DEBUG + fprintf(stderr, "Handshake fail!\n"); + #endif + return 0; + } + #ifdef DEBUG + fprintf(stderr, "Starting processing...\n"); + #endif + while (std::cin.good() && std::cout.good()){ + //select(1, &pollset, 0, 0, &timeout); + //only parse input from stdin if available or not yet init'ed + //FD_ISSET(0, &pollset) || //NOTE: Polling does not work? WHY?!? WHY DAMN IT?!? + if ((!ready4data || (snd_cnt - snd_window_at >= snd_window_size)) && !stopparsing){parseChunk();fflush(stdout);} + if (ready4data){ + if (!inited){ + //we are ready, connect the socket! + if (!ss.connect(streamname.c_str())){ + #ifdef DEBUG + fprintf(stderr, "Could not connect to server!\n"); + #endif + return 0; + } + FLV_Readheader(ss);//read the header, we don't want it + #ifdef DEBUG + fprintf(stderr, "Header read, starting to send video data...\n"); + #endif + inited = true; + } + //only send data if previous data has been ACK'ed... + if (snd_cnt - snd_window_at < snd_window_size){ + if (FLV_GetPacket(ss)){//able to read a full packet? + ts = FLVbuffer[7] * 256*256*256; + ts += FLVbuffer[4] * 256*256; + ts += FLVbuffer[5] * 256; + ts += FLVbuffer[6]; + if (ts != 0){ + if (fts == 0){fts = ts;ftst = getNowMS();} + ts -= fts; + FLVbuffer[7] = ts / (256*256*256); + FLVbuffer[4] = ts / (256*256); + FLVbuffer[5] = ts / 256; + FLVbuffer[6] = ts % 256; + ts += ftst; + }else{ + ftst = getNowMS(); + FLVbuffer[7] = ftst / (256*256*256); + FLVbuffer[4] = ftst / (256*256); + FLVbuffer[5] = ftst / 256; + FLVbuffer[6] = ftst % 256; + } + SendMedia((unsigned char)FLVbuffer[0], (unsigned char *)FLVbuffer+11, FLV_len-15, ts); + FLV_Dump();//dump packet and get ready for next + } + if ((SWBerr != SWBaseSocket::ok) && (SWBerr != SWBaseSocket::notReady)){ + #ifdef DEBUG + fprintf(stderr, "No more data! :-( (%s)\n", SWBerr.get_error().c_str()); + #endif + return 0;//no more input possible! Fail immediately. + } + } + } + //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) + } + } + #ifdef DEBUG + fprintf(stderr, "User disconnected.\n"); + #endif + return 0; +}//main diff --git a/Connector_RTMPf/parsechunks.cpp b/Connector_RTMPf/parsechunks.cpp new file mode 100644 index 00000000..5860b65f --- /dev/null +++ b/Connector_RTMPf/parsechunks.cpp @@ -0,0 +1,246 @@ +#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)0xFF); + static AMFType amfelem("empty", (unsigned char)0xFF); + 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); + #ifdef DEBUG + fprintf(stderr, "CTRL: Set chunk size: %i\n", chunk_rec_max); + #endif + break; + case 2://abort message - we ignore this one + #ifdef DEBUG + fprintf(stderr, "CTRL: Abort message\n"); + #endif + //4 bytes of stream id to drop + break; + case 3://ack + #ifdef DEBUG + fprintf(stderr, "CTRL: Acknowledgement\n"); + #endif + snd_window_at = ntohl(*(int*)next.data); + snd_window_at = snd_cnt; + break; + case 4:{ + #ifdef DEBUG + 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 + #ifdef DEBUG + 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: + #ifdef DEBUG + 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: + #ifdef DEBUG + fprintf(stderr, "Received audio data\n"); + #endif + break; + case 9: + #ifdef DEBUG + fprintf(stderr, "Received video data\n"); + #endif + break; + case 15: + #ifdef DEBUG + fprintf(stderr, "Received AFM3 data message\n"); + #endif + break; + case 16: + #ifdef DEBUG + fprintf(stderr, "Received AFM3 shared object\n"); + #endif + break; + case 17: + #ifdef DEBUG + fprintf(stderr, "Received AFM3 command message\n"); + #endif + break; + case 18: + #ifdef DEBUG + fprintf(stderr, "Received AFM0 data message\n"); + #endif + break; + case 19: + #ifdef DEBUG + fprintf(stderr, "Received AFM0 shared object\n"); + #endif + break; + case 20:{//AMF0 command message + bool parsed = false; + amfdata = parseAMF(next.data, next.real_len); + #ifdef DEBUG + fprintf(stderr, "Received AFM0 command message:\n"); + amfdata.Print(); + #endif + if (amfdata.getContentP(0)->StrValue() == "connect"){ + #ifdef DEBUG + 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, 0);//send UCM StreamBegin (0), stream 0 + //send a _result reply + AMFType amfreply("container", (unsigned char)0xFF); + 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,0,1,123"));//stolen from examples + amfreply.getContentP(2)->addContent(AMFType("capabilities", (double)31));//stolen from examples + 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.")); + amfreply.getContentP(3)->addContent(AMFType("capabilities", (double)33));//from red5 server + amfreply.getContentP(3)->addContent(AMFType("fmsVer", "PLS/1,0,0,0"));//from red5 server + 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)0xFF); + 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("", (double)1));//stream ID - we use 1 + SendChunk(3, 20, next.msg_stream_id, amfreply.Pack()); + SendUSR(0, 0);//send UCM StreamBegin (0), stream 0 + #ifdef DEBUG + fprintf(stderr, "AMF0 command: createStream result\n"); + #endif + parsed = true; + }//createStream + if ((amfdata.getContentP(0)->StrValue() == "getStreamLength") || (amfdata.getContentP(0)->StrValue() == "getMovLen")){ + //send a _result reply + AMFType amfreply("container", (unsigned char)0xFF); + 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("", (double)0));//zero length + SendChunk(3, 20, next.msg_stream_id, amfreply.Pack()); + #ifdef DEBUG + fprintf(stderr, "AMF0 command: getStreamLength result\n"); + #endif + parsed = true; + }//getStreamLength + if (amfdata.getContentP(0)->StrValue() == "checkBandwidth"){ + //send a _result reply + AMFType amfreply("container", (unsigned char)0xFF); + 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("", (double)0, 0x05));//null - command info + SendChunk(3, 20, 1, amfreply.Pack()); + #ifdef DEBUG + fprintf(stderr, "AMF0 command: checkBandwidth result\n"); + #endif + 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)0xFF); + amfreply.addContent(AMFType("", "onStatus"));//status reply + amfreply.addContent(amfdata.getContent(1));//same transaction ID + amfreply.addContent(AMFType("", (double)0, 0x05));//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)); + SendChunk(4, 20, next.msg_stream_id, amfreply.Pack()); + amfreply = AMFType("container", (unsigned char)0xFF); + amfreply.addContent(AMFType("", "onStatus"));//status reply + amfreply.addContent(amfdata.getContent(1));//same transaction ID + amfreply.addContent(AMFType("", (double)0, 0x05));//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)); + 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! + #ifdef DEBUG + fprintf(stderr, "AMF0 command: play result (%s)\n", streamname.c_str()); + #endif + parsed = true; + }//createStream + if (!parsed){ + #ifdef DEBUG + fprintf(stderr, "AMF0 command not processed! :(\n"); + #endif + } + } break; + case 22: + #ifdef DEBUG + fprintf(stderr, "Received aggregate message\n"); + #endif + break; + default: + #ifdef DEBUG + fprintf(stderr, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.\n"); + #endif + stopparsing = true; + break; + } +}//parseChunk diff --git a/Makefile b/Makefile index 42ead03d..9170c77c 100644 --- a/Makefile +++ b/Makefile @@ -3,12 +3,14 @@ default: client-install client: cd Connector_HTTP; $(MAKE) cd Connector_RTMP; $(MAKE) + cd Connector_RTMPf; $(MAKE) cd Connector_RAW; $(MAKE) #cd Connector_RTSP; $(MAKE) cd Buffer; $(MAKE) client-clean: cd Connector_HTTP; $(MAKE) clean cd Connector_RTMP; $(MAKE) clean + cd Connector_RTMPf; $(MAKE) clean cd Connector_RAW; $(MAKE) clean #cd Connector_RTSP; $(MAKE) clean cd Buffer; $(MAKE) clean @@ -18,6 +20,7 @@ client-install: client-clean client cp -f ./Connector_HTTP/Connector_HTTP /usr/bin/ cd Connector_RTMP; $(MAKE) install cp -f ./Connector_RAW/Connector_RAW /usr/bin/ + cp -f ./Connector_RTMPf/Connector_RTMPf /usr/bin/ #cp -f ./Connector_RTSP/Connector_RTSP /usr/bin/ cp -f ./Buffer/Buffer /usr/bin/ cp -f ./PLS /etc/xinetd.d/ diff --git a/PLS b/PLS index e6eda2ea..e1656837 100644 --- a/PLS +++ b/PLS @@ -26,3 +26,17 @@ service ddvtechraw cps = 100 5 } +service ddvtechrtmp +{ + disable = no + type = UNLISTED + protocol = tcp + socket_type = stream + user = root + server = /usr/bin/Connector_RTMPf + port = 1935 + wait = no + per_source = 10 + cps = 100 5 +} + diff --git a/util/ddv_socket.cpp b/util/ddv_socket.cpp index e81e7445..d8babf7a 100644 --- a/util/ddv_socket.cpp +++ b/util/ddv_socket.cpp @@ -97,20 +97,6 @@ bool DDV_ready(int sock){ return (r == 1); } -int DDV_readycount(int sock){ - static char tmp[1048576]; - int preflags = fcntl(sock, F_GETFL, 0); - int postflags = preflags | O_NONBLOCK; - fcntl(sock, F_SETFL, postflags); - int r = recv(sock, tmp, 1048576, MSG_PEEK); - fcntl(sock, F_SETFL, preflags); - if (r > 0){ - return r; - }else{ - return 0; - } -} - bool DDV_read(void * buffer, int todo, int sock){ int sofar = 0; socketBlocking = false;