Oude connector naast de nieuwe... poort nummer nieuwe is nu 1936!
This commit is contained in:
parent
cc1fdf7138
commit
27279d31c8
12 changed files with 1880 additions and 15 deletions
|
@ -41,7 +41,7 @@ int main(int argc, char ** argv){
|
||||||
sigaction (SIGHUP, &new_action, NULL);
|
sigaction (SIGHUP, &new_action, NULL);
|
||||||
sigaction (SIGTERM, &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 ((argc < 2) || (argv[1] == "nd")){
|
||||||
if (server_socket > 0){daemon(1, 0);}else{return 1;}
|
if (server_socket > 0){daemon(1, 0);}else{return 1;}
|
||||||
}
|
}
|
||||||
|
|
23
Connector_RTMPf/Makefile
Normal file
23
Connector_RTMPf/Makefile
Normal file
|
@ -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'
|
285
Connector_RTMPf/amf.cpp
Normal file
285
Connector_RTMPf/amf.cpp
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
#include <vector>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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<AMFType>::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<AMFType>::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>;
|
||||||
|
};
|
||||||
|
~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<AMFType>;
|
||||||
|
for (std::vector<AMFType>::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<AMFType>;
|
||||||
|
for (std::vector<AMFType>::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<AMFType>;
|
||||||
|
for (std::vector<AMFType>::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<AMFType>::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<AMFType>::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<AMFType>::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<AMFType>::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<AMFType> * 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());}
|
501
Connector_RTMPf/chunkstream.cpp
Normal file
501
Connector_RTMPf/chunkstream.cpp
Normal file
|
@ -0,0 +1,501 @@
|
||||||
|
#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; 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<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){
|
||||||
|
#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
|
506
Connector_RTMPf/crypto.cpp
Normal file
506
Connector_RTMPf/crypto.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
45
Connector_RTMPf/crypto.h
Normal file
45
Connector_RTMPf/crypto.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#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 */
|
||||||
|
|
137
Connector_RTMPf/handshake.cpp
Normal file
137
Connector_RTMPf/handshake.cpp
Normal file
|
@ -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
|
119
Connector_RTMPf/main.cpp
Normal file
119
Connector_RTMPf/main.cpp
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#undef DEBUG
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
//needed for select
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
//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
|
246
Connector_RTMPf/parsechunks.cpp
Normal file
246
Connector_RTMPf/parsechunks.cpp
Normal file
|
@ -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
|
3
Makefile
3
Makefile
|
@ -3,12 +3,14 @@ default: client-install
|
||||||
client:
|
client:
|
||||||
cd Connector_HTTP; $(MAKE)
|
cd Connector_HTTP; $(MAKE)
|
||||||
cd Connector_RTMP; $(MAKE)
|
cd Connector_RTMP; $(MAKE)
|
||||||
|
cd Connector_RTMPf; $(MAKE)
|
||||||
cd Connector_RAW; $(MAKE)
|
cd Connector_RAW; $(MAKE)
|
||||||
#cd Connector_RTSP; $(MAKE)
|
#cd Connector_RTSP; $(MAKE)
|
||||||
cd Buffer; $(MAKE)
|
cd Buffer; $(MAKE)
|
||||||
client-clean:
|
client-clean:
|
||||||
cd Connector_HTTP; $(MAKE) clean
|
cd Connector_HTTP; $(MAKE) clean
|
||||||
cd Connector_RTMP; $(MAKE) clean
|
cd Connector_RTMP; $(MAKE) clean
|
||||||
|
cd Connector_RTMPf; $(MAKE) clean
|
||||||
cd Connector_RAW; $(MAKE) clean
|
cd Connector_RAW; $(MAKE) clean
|
||||||
#cd Connector_RTSP; $(MAKE) clean
|
#cd Connector_RTSP; $(MAKE) clean
|
||||||
cd Buffer; $(MAKE) clean
|
cd Buffer; $(MAKE) clean
|
||||||
|
@ -18,6 +20,7 @@ client-install: client-clean client
|
||||||
cp -f ./Connector_HTTP/Connector_HTTP /usr/bin/
|
cp -f ./Connector_HTTP/Connector_HTTP /usr/bin/
|
||||||
cd Connector_RTMP; $(MAKE) install
|
cd Connector_RTMP; $(MAKE) install
|
||||||
cp -f ./Connector_RAW/Connector_RAW /usr/bin/
|
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 ./Connector_RTSP/Connector_RTSP /usr/bin/
|
||||||
cp -f ./Buffer/Buffer /usr/bin/
|
cp -f ./Buffer/Buffer /usr/bin/
|
||||||
cp -f ./PLS /etc/xinetd.d/
|
cp -f ./PLS /etc/xinetd.d/
|
||||||
|
|
14
PLS
14
PLS
|
@ -26,3 +26,17 @@ service ddvtechraw
|
||||||
cps = 100 5
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,20 +97,6 @@ bool DDV_ready(int sock){
|
||||||
return (r == 1);
|
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){
|
bool DDV_read(void * buffer, int todo, int sock){
|
||||||
int sofar = 0;
|
int sofar = 0;
|
||||||
socketBlocking = false;
|
socketBlocking = false;
|
||||||
|
|
Loading…
Add table
Reference in a new issue