parent
ad232359ac
commit
e57ffdab1a
@ -0,0 +1,25 @@
|
||||
|
||||
CXX=g++
|
||||
# CXXFLAGS= -Wall -ggdb -g -std=c++11 -pg -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600
|
||||
CXXFLAGS= -Wall -ggdb -g -std=c++11 -pg -I/usr/include/libxml2
|
||||
LDFLAGS= -lm -L/usr/local/lib -g -ggdb -pg -lxml2 -lcrypto
|
||||
|
||||
APPS = fbsh-cli
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .cc .C .cpp .oo
|
||||
|
||||
all: $(APPS)
|
||||
|
||||
.cc.oo : $(INCLUDES)
|
||||
$(CXX) -o $@ -c $(CXXFLAGS) $<
|
||||
|
||||
# $(CXX) -o $@ $^ $(LDFLAGS) -L./ -I./ -ltinfo -lncursesw
|
||||
fbsh-cli: fbsh.oo cfg.oo fbsh-cli.oo tcp.oo
|
||||
$(CXX) -o $@ $^ $(LDFLAGS) -L./ -I./
|
||||
|
||||
clean:
|
||||
rm -rf *.oo
|
||||
rm -rf *.cc~
|
||||
rm -rf fbsh-cli
|
||||
rm -rf gmon.out
|
@ -1,3 +1,15 @@
|
||||
# FBSmartHome
|
||||
|
||||
Fritzbox SmartHome C++ Interface
|
||||
**fbsh-cli** - FritzBox SmartHome Console Client
|
||||
|
||||
## Configuration File
|
||||
To set some default settings and create the configuration file, just run `./fbsh-cli -host *HOSTNAME* -user *USERNAME* -pass *PASSWORD* saveconfig`
|
||||
|
||||
The default configuration will be saved under $HOME/.fbsh.config.
|
||||
|
||||
## Supported Commands
|
||||
+ saveconfig
|
||||
+ connect
|
||||
+ list
|
||||
+ help
|
||||
|
||||
|
@ -0,0 +1,161 @@
|
||||
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "cfg.h"
|
||||
|
||||
std::string Config::GetFilenameInHome () {
|
||||
std::string res = "";
|
||||
char *env = NULL;
|
||||
|
||||
env = secure_getenv("HOME");
|
||||
if (env != NULL)
|
||||
res = env;
|
||||
|
||||
res = res + "/" + FBSH_CFGFILE;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::string Config::GetDefaultSIDFile () {
|
||||
std::string res = "";
|
||||
char *env = NULL;
|
||||
|
||||
env = secure_getenv("HOME");
|
||||
if (env != NULL)
|
||||
res = env;
|
||||
|
||||
res = res + "/" + FBSH_SIDFILE;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* if saving password is disabled the password will be deleted.
|
||||
* The configuration file must be saved manually..
|
||||
*/
|
||||
void Config::SetSavePassword(bool enable) {
|
||||
savepassword = enable;
|
||||
if (!savepassword) {
|
||||
password = "";
|
||||
username = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* the constructor will need to set all parameters to default,
|
||||
* and read the configuration file.
|
||||
*
|
||||
* Since this class/object is defined as global object, it will
|
||||
* do this before the command line parameters are read in the
|
||||
* main function.
|
||||
*/
|
||||
Config::Config() {
|
||||
filename = GetFilenameInHome();
|
||||
sidfile = GetDefaultSIDFile();
|
||||
username = "";
|
||||
password = "";
|
||||
savepassword = false;
|
||||
silent = false;
|
||||
LoadConfig();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* nothing to do here, we have no autosave of the cofiguration file.
|
||||
*/
|
||||
Config::~Config() {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* just load all parameters found in the configuration file into the configuration
|
||||
*/
|
||||
#define BUFSIZE 1024
|
||||
int Config::LoadConfig() {
|
||||
FILE *f = NULL;
|
||||
char buffer[BUFSIZE];
|
||||
char *val = NULL;
|
||||
int i;
|
||||
|
||||
if ((f = fopen (filename.c_str(), "r")) == NULL) {
|
||||
fprintf (stderr, "error on writing config '%s' : %s\n", filename.c_str(),
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(buffer, BUFSIZE, f) != NULL) {
|
||||
//
|
||||
// check for lenght of buffer
|
||||
// find parametername and value
|
||||
buffer[BUFSIZE] = '\0';
|
||||
i = strlen (buffer);
|
||||
if (i > 0) buffer[i-1] = '\0';
|
||||
val = strchr(buffer, '=');
|
||||
if (val != NULL) {
|
||||
*val = '\0';
|
||||
val++;
|
||||
}
|
||||
else continue;
|
||||
|
||||
//
|
||||
// copy found value into the Config variables
|
||||
if (strncmp (buffer, "FBSH_SAVEPASSWORD", BUFSIZE) == 0)
|
||||
savepassword = atoi(val);
|
||||
else if (strncmp (buffer, "FBSH_SIDFILE", BUFSIZE) == 0)
|
||||
sidfile = val;
|
||||
else if (strncmp (buffer, "FBSH_USER", BUFSIZE) == 0)
|
||||
username = val;
|
||||
else if (strncmp (buffer, "FBSH_PASS", BUFSIZE) == 0)
|
||||
password = val;
|
||||
else if (strncmp (buffer, "FBSH_HOSTNAME", BUFSIZE) == 0)
|
||||
fbhostname = val;
|
||||
if (strncmp (buffer, "FBSH_SILENTMODE", BUFSIZE) == 0)
|
||||
silent = atoi(val);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* save configuration file, it will contains all default parameters and changed
|
||||
* parameters given as command line parameter.
|
||||
*/
|
||||
int Config::SaveConfig() {
|
||||
FILE *f = NULL;
|
||||
|
||||
if ((f = fopen (filename.c_str(), "w")) == NULL) {
|
||||
fprintf (stderr, "error on writing config '%s' : %s\n", filename.c_str(),
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(f, "#\n");
|
||||
fprintf(f, "# configfile for fbsh-cli\n");
|
||||
fprintf(f, "# PARAMETERNAME=VALUE no space allowed except for the value.\n");
|
||||
fprintf(f, "# but not between parametername, = and value.\n");
|
||||
fprintf(f, "#\n");
|
||||
fprintf(f, "#\n");
|
||||
|
||||
fprintf(f, "FBSH_SAVEPASSWORD=%d\n", savepassword);
|
||||
if (savepassword) {
|
||||
fprintf(f, "FBSH_USER=%s\n", username.c_str());
|
||||
fprintf(f, "FBSH_PASS=%s\n", password.c_str());
|
||||
}
|
||||
fprintf(f, "FBSH_SIDFILE=%s\n", sidfile.c_str());
|
||||
fprintf(f, "FBSH_HOSTNAME=%s\n", fbhostname.c_str());
|
||||
fprintf(f, "FBSH_SILENTMODE=%d\n", silent);
|
||||
|
||||
fclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,47 @@
|
||||
|
||||
#ifndef _CFG_H_
|
||||
#define _CFG_H_
|
||||
|
||||
#include <string>
|
||||
#define FBSH_CFGFILE ".fbsh.config"
|
||||
#define FBSH_SIDFILE ".fbsh.sid"
|
||||
|
||||
class Config {
|
||||
private:
|
||||
std::string username;
|
||||
std::string password;
|
||||
std::string sidfile;
|
||||
bool silent;
|
||||
bool savepassword;
|
||||
std::string fbhostname;
|
||||
|
||||
std::string filename; // config filename ($HOME/.fbsh.config)
|
||||
std::string GetFilenameInHome();
|
||||
std::string GetDefaultSIDFile();
|
||||
|
||||
public:
|
||||
Config();
|
||||
~Config();
|
||||
|
||||
void SetPassword(std::string psw) { if (savepassword) password = psw; };
|
||||
void SetUsername(std::string user) { if (savepassword) username = user; };
|
||||
void SetFBHostname(std::string fn) { fbhostname = fn; };
|
||||
void SetSIDFile(std::string fn) { sidfile = fn; };
|
||||
void SetFilename(std::string fn) { filename = fn; };
|
||||
void SetSavePassword(bool enable);
|
||||
void SetSilent(bool enable) { silent = enable; };
|
||||
|
||||
std::string GetPassword() { return password; };
|
||||
std::string GetFBHostname() { return fbhostname; };
|
||||
std::string GetUsername() { return username; };
|
||||
std::string GetFilename() { return filename; };
|
||||
std::string GetSIDFile() { return sidfile; };
|
||||
bool GetSavePassword() { return savepassword; };
|
||||
bool GetSilent() { return silent; };
|
||||
|
||||
int SaveConfig();
|
||||
int LoadConfig();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,116 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
|
||||
#include "cfg.h"
|
||||
#include "fbsh.h"
|
||||
|
||||
Config config;
|
||||
FBSmartHome fbsh;
|
||||
|
||||
|
||||
void help() {
|
||||
printf ("fbsh-cli: parameters command options\n");
|
||||
printf (" Client for accessing the fritzbox smart home devices.\n");
|
||||
printf (" Most parameters can be set in a seperate config file.\n");
|
||||
printf ("Commands:\n");
|
||||
printf ("\thelp displays this helptext.\n");
|
||||
printf ("\tconnect connect to the device, it will save the SID file.\n");
|
||||
printf ("\tlist lists all devices, connection will be recovered from SID.\n");
|
||||
printf ("\t If silent parameter is set, we will not be asked for credentials\n");
|
||||
printf ("\tsaveconfig save configfile.\n");
|
||||
printf ("\n");
|
||||
printf ("Parameters:\n");
|
||||
printf ("\t-config <CONFIGGILE> configfile to use\n");
|
||||
printf ("\t-host <HOSTNAME> the fritzbox to connect to (http://fritz.box)\n");
|
||||
printf ("\t-user <USERNAME> username to connect with\n");
|
||||
printf ("\t-pass <PASSWORD> password to use\n");
|
||||
printf ("\t-sidfile <SIDFILE> file which contains the session id\n");
|
||||
printf ("\t-savepassword <0|1> should the password be saved in the configfile\n");
|
||||
printf ("\n");
|
||||
printf ("\t-silent <0|1> enable/disable: no questions about username or password\n");
|
||||
printf ("\t-S enable silent mode\n");
|
||||
printf ("\n");
|
||||
|
||||
exit (0);
|
||||
}
|
||||
|
||||
|
||||
void connect() {
|
||||
fbsh.UseSIDFile(config.GetSIDFile());
|
||||
if (fbsh.Connect(config.GetFBHostname()) != 0) {
|
||||
if (fbsh.Connect(config.GetFBHostname(), config.GetUsername(), config.GetPassword()) != 0) {
|
||||
fprintf (stderr, "could not connect to: %s Error:%s\n", config.GetFBHostname().c_str(), strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void list(int argpos, int argc, char **argv) {
|
||||
connect ();
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int i;
|
||||
|
||||
if (argc < 2) help();
|
||||
else {
|
||||
for (i = 1; i < argc; i++) {
|
||||
//
|
||||
// commands to run, all parameters must be set by here.
|
||||
//
|
||||
if (strcmp(argv[i], "help") == 0) {
|
||||
help();
|
||||
break;
|
||||
}
|
||||
else if (strcmp(argv[i], "connect") == 0) {
|
||||
connect();
|
||||
break;
|
||||
}
|
||||
else if (strcmp(argv[i], "list") == 0) {
|
||||
list(i, argc, argv);
|
||||
break;
|
||||
}
|
||||
else if (strcmp(argv[i], "saveconfig") == 0) {
|
||||
config.SaveConfig();
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// parameters will follow here
|
||||
//
|
||||
else if (strcmp(argv[i], "-host") == 0) {
|
||||
config.SetFBHostname(argv[++i]);
|
||||
}
|
||||
else if (strcmp(argv[i], "-user") == 0) {
|
||||
config.SetUsername(argv[++i]);
|
||||
}
|
||||
else if (strcmp(argv[i], "-pass") == 0) {
|
||||
config.SetPassword(argv[++i]);
|
||||
}
|
||||
else if (strcmp(argv[i], "-silent") == 0) {
|
||||
config.SetSilent(atoi(argv[++i]));
|
||||
}
|
||||
else if (strcmp(argv[i], "-S") == 0) {
|
||||
config.SetSilent(true);
|
||||
}
|
||||
else if (strcmp(argv[i], "-sidfile") == 0) {
|
||||
config.SetSIDFile(argv[++i]);
|
||||
}
|
||||
else if (strcmp(argv[i], "-savepassword") == 0) {
|
||||
config.SetSavePassword(atoi(argv[++i]));
|
||||
}
|
||||
else if (strcmp(argv[i], "-config") == 0) {
|
||||
config.SetFilename(argv[++i]);
|
||||
config.LoadConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,264 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <unistd.h>
|
||||
#include <iconv.h>
|
||||
#include <libxml/xmlmemory.h>
|
||||
#include <libxml/parser.h>
|
||||
|
||||
#include "fbsh.h"
|
||||
|
||||
std::string generateMD5 (char* input, int size) {
|
||||
int i;
|
||||
MD5_CTX md5;
|
||||
unsigned char out[MD5_DIGEST_LENGTH];
|
||||
char txt[32];
|
||||
std::string md5sum = "";
|
||||
|
||||
MD5_Init(&md5);
|
||||
MD5_Update(&md5, input, size);
|
||||
MD5_Final(out, &md5);
|
||||
|
||||
for ( i = 0; i < MD5_DIGEST_LENGTH; i++) {
|
||||
snprintf (txt, 32, "%02x", out[i]);
|
||||
md5sum += txt;
|
||||
}
|
||||
|
||||
return md5sum;
|
||||
}
|
||||
|
||||
|
||||
FBSmartHome::FBSmartHome() {
|
||||
hostname = "";
|
||||
SID = "00 not yet set 00";
|
||||
fsidfile = NULL;
|
||||
}
|
||||
|
||||
|
||||
FBSmartHome::~FBSmartHome() {
|
||||
if (fsidfile != NULL) CloseSIDFile();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* try to connect with the last known SID.
|
||||
*/
|
||||
int FBSmartHome::Connect(std::string host) {
|
||||
int len;
|
||||
xmlDocPtr xmldoc;
|
||||
xmlNodePtr xmlnode;
|
||||
|
||||
hostname = host;
|
||||
|
||||
// is sid working?
|
||||
len = tcp.WebGetFile(hostname+"/login_sid.lua?sid="+SID, inbuffer, FB_BUFFER, NULL);
|
||||
if (len > FB_BUFFER) len = FB_BUFFER;
|
||||
else if (len < 0) {
|
||||
fprintf (stderr, "%s:%d Error getting challenge from Fritzbox\n", __FILE__, __LINE__);
|
||||
errno = EAI_FAIL;
|
||||
return -1;
|
||||
}
|
||||
inbuffer[len] = '\0';
|
||||
XMLPrepare(inbuffer, len, &xmldoc, &xmlnode);
|
||||
|
||||
//
|
||||
// parse for SID and Challange
|
||||
while (xmlnode) {
|
||||
if ((!xmlStrcmp(xmlnode->name, (const xmlChar *)"SessionInfo"))){
|
||||
xmlNodePtr xmlchild = xmlnode->xmlChildrenNode;
|
||||
while (xmlchild) {
|
||||
if ((!xmlStrcmp(xmlchild->name, (const xmlChar *)"SID")))
|
||||
SID = (char *)xmlNodeListGetString(xmldoc, xmlchild->children, 1);
|
||||
xmlchild = xmlchild->next;
|
||||
}
|
||||
}
|
||||
xmlnode = xmlnode->next;
|
||||
}
|
||||
xmlFreeDoc(xmldoc);
|
||||
|
||||
if (SID.compare("0000000000000000") == 0) {
|
||||
hostname = "";
|
||||
errno = ENOTCONN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int FBSmartHome::XMLPrepare(char *buffer, int len, xmlDocPtr *xmldoc, xmlNodePtr *xmlnode) {
|
||||
//
|
||||
// read the xml data from memory and select root node.
|
||||
if (xmldoc == NULL || xmlnode == NULL) return -1;
|
||||
|
||||
*xmldoc = xmlReadMemory(inbuffer, len, "hostname", NULL, 0);
|
||||
if (xmldoc == NULL) {
|
||||
fprintf (stderr, "%s: Could not read XML Document\n", __FUNCTION__);
|
||||
errno = EAI_FAIL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*xmlnode = xmlDocGetRootElement(*xmldoc);
|
||||
if (*xmlnode == NULL) {
|
||||
fprintf (stderr, "%s: Empty XML Document\n", __FUNCTION__);
|
||||
xmlFreeDoc(*xmldoc);
|
||||
errno = EOF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* connect with username and password, if connect is successfull the
|
||||
* SID will be returned.
|
||||
*/
|
||||
#define PWSSIZE 512
|
||||
int FBSmartHome::Connect (std::string host, std::string username, std::string password) {
|
||||
int len;
|
||||
xmlDocPtr xmldoc;
|
||||
xmlNodePtr xmlnode;
|
||||
std::string challenge;
|
||||
std::string formdata;
|
||||
|
||||
hostname = host;
|
||||
SID = "";
|
||||
tcp.Close();
|
||||
|
||||
//
|
||||
// get challenge from FritzBox
|
||||
len = tcp.WebGetFile(hostname+"/login_sid.lua", inbuffer, FB_BUFFER, NULL);
|
||||
if (len > FB_BUFFER) len = FB_BUFFER;
|
||||
else if (len < 0) {
|
||||
fprintf (stderr, "%s:%d Error getting challenge from Fritzbox\n", __FILE__, __LINE__);
|
||||
errno = EAI_FAIL;
|
||||
return -1;
|
||||
}
|
||||
inbuffer[len] = '\0';
|
||||
XMLPrepare(inbuffer, len, &xmldoc, &xmlnode);
|
||||
|
||||
//
|
||||
// parse for SID and Challange
|
||||
while (xmlnode) {
|
||||
if ((!xmlStrcmp(xmlnode->name, (const xmlChar *)"SessionInfo"))){
|
||||
xmlNodePtr xmlchild = xmlnode->xmlChildrenNode;
|
||||
while (xmlchild) {
|
||||
if ((!xmlStrcmp(xmlchild->name, (const xmlChar *)"SID")))
|
||||
SID = (char *)xmlNodeListGetString(xmldoc, xmlchild->children, 1);
|
||||
else if ((!xmlStrcmp(xmlchild->name, (const xmlChar *)"Challenge")))
|
||||
challenge = (char*) xmlNodeListGetString(xmldoc, xmlchild->children, 1);
|
||||
xmlchild = xmlchild->next;
|
||||
}
|
||||
}
|
||||
xmlnode = xmlnode->next;
|
||||
}
|
||||
xmlFreeDoc(xmldoc);
|
||||
|
||||
//
|
||||
// create Challenge-PSW -> MD5 sum
|
||||
//
|
||||
|
||||
//
|
||||
// convert to UTF16_LE
|
||||
std::string convertfrom = challenge+"-"+password;
|
||||
char *conv_input = (char *)convertfrom.c_str();
|
||||
size_t conv_inbytes = strlen (conv_input);
|
||||
size_t conv_size;
|
||||
char convertedpsw[PWSSIZE];
|
||||
char *conv_output = (char*)convertedpsw;
|
||||
size_t conv_outbytes = PWSSIZE;
|
||||
|
||||
iconv_t ic = iconv_open("UTF-16LE", "UTF-8");
|
||||
if ((long int)ic == -1) {
|
||||
fprintf (stderr, "iconv_open error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
conv_size = iconv(ic, &conv_input, &conv_inbytes, (char**)&conv_output, &conv_outbytes);
|
||||
if ((long int)conv_size == -1) {
|
||||
fprintf (stderr, "iconv error: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
iconv_close(ic);
|
||||
|
||||
//
|
||||
// create password
|
||||
formdata = "response="+challenge+"-"+generateMD5(convertedpsw, conv_output-convertedpsw)+"&username="+username;
|
||||
len = tcp.WebGetFile(hostname+"/login_sid.lua", inbuffer, FB_BUFFER, (char*)formdata.c_str());
|
||||
if (len > FB_BUFFER) len = FB_BUFFER;
|
||||
else if (len < 0) {
|
||||
errno = EAI_FAIL;
|
||||
return -1;
|
||||
}
|
||||
inbuffer[len] = '\0';
|
||||
|
||||
//
|
||||
// parse for SID and Challange
|
||||
XMLPrepare(inbuffer, len, &xmldoc, &xmlnode);
|
||||
while (xmlnode) {
|
||||
if ((!xmlStrcmp(xmlnode->name, (const xmlChar *)"SessionInfo"))){
|
||||
xmlNodePtr xmlchild = xmlnode->xmlChildrenNode;
|
||||
while (xmlchild) {
|
||||
if ((!xmlStrcmp(xmlchild->name, (const xmlChar *)"SID")))
|
||||
SID = (char *)xmlNodeListGetString(xmldoc, xmlchild->children, 1);
|
||||
else if ((!xmlStrcmp(xmlchild->name, (const xmlChar *)"Challenge")))
|
||||
challenge = (char*) xmlNodeListGetString(xmldoc, xmlchild->children, 1);
|
||||
xmlchild = xmlchild->next;
|
||||
}
|
||||
}
|
||||
xmlnode = xmlnode->next;
|
||||
}
|
||||
xmlFreeDoc(xmldoc);
|
||||
|
||||
errno = 0;
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* read sidfile, if it does not exist create file.
|
||||
*/
|
||||
#define BUFSIZE 1024
|
||||
int FBSmartHome::UseSIDFile(std::string fn) {
|
||||
char buffer[BUFSIZE];
|
||||
int i;
|
||||
|
||||
if ((fsidfile = fopen (fn.c_str(), "r+")) == NULL) {
|
||||
// file not found create file
|
||||
if ((fsidfile = fopen (fn.c_str(), "w")) == NULL) {
|
||||
fprintf (stderr, "error on writing sidfile '%s' : %s\n", fn.c_str(),
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// sidfile found and open
|
||||
fseek (fsidfile, 0, SEEK_SET);
|
||||
if (fgets(buffer, BUFSIZE, fsidfile) != NULL) {
|
||||
buffer[BUFSIZE] = '\0';
|
||||
i = strlen (buffer);
|
||||
if (i > 0 && buffer[i-1] == '\n') buffer[i-1] = '\0';
|
||||
SID = buffer;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* if the sid file is open save current sid.
|
||||
*/
|
||||
int FBSmartHome::CloseSIDFile() {
|
||||
if (fsidfile != NULL) {
|
||||
fseek (fsidfile, 0, SEEK_SET);
|
||||
fprintf (fsidfile, "%s", SID.c_str());
|
||||
fclose(fsidfile);
|
||||
fsidfile = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* FritzBox Smart Home - C++ interface class
|
||||
*/
|
||||
|
||||
#ifndef _FBSH_H_
|
||||
#define _FBSH_H_
|
||||
|
||||
#include <string>
|
||||
#include <libxml/xmlmemory.h>
|
||||
#include <libxml/parser.h>
|
||||
|
||||
#include "tcp.h"
|
||||
|
||||
std::string generateMD5 (char* input, int size);
|
||||
|
||||
#define FB_BUFFER (4*1024*1024)
|
||||
|
||||
class FBSmartHome {
|
||||
private:
|
||||
char inbuffer[FB_BUFFER];
|
||||
char outbuffer[FB_BUFFER];
|
||||
|
||||
std::string SID;
|
||||
FILE *fsidfile;
|
||||
std::string hostname;
|
||||
TCP tcp;
|
||||
|
||||
int XMLPrepare(char *buffer, int len, xmlDocPtr *xmldoc, xmlNodePtr *xmlnode);
|
||||
public:
|
||||
FBSmartHome ();
|
||||
~FBSmartHome ();
|
||||
|
||||
std::string GetSID() { return SID; };
|
||||
|
||||
int UseSIDFile (std::string fn);
|
||||
int CloseSIDFile ();
|
||||
|
||||
int Connect(std::string host); // return 0 on success
|
||||
int Connect(std::string host, std::string username, std::string password);
|
||||
};
|
||||
|
||||
#endif
|
@ -0,0 +1,679 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// tcp.cc is part of TestModbus-Server.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "tcp.h"
|
||||
#include <stdio.h>
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
|
||||
#else
|
||||
#include <unistd.h> /* close() */
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h> /* memset() */
|
||||
#include <errno.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
int tcpinit;
|
||||
//
|
||||
// convert host and port to sockaddr_in6
|
||||
//
|
||||
int dns_filladdr (string host, string port, int ai_family, struct sockaddr_storage *sAddr) {
|
||||
struct addrinfo hints, *res;
|
||||
int err;
|
||||
|
||||
bzero (&hints, sizeof (struct addrinfo));
|
||||
hints.ai_family = ai_family;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
if ((err = getaddrinfo (host.c_str(), port.c_str(), &hints, &res)) < 0) {
|
||||
fprintf (stdout, "dns_filladdr (getaddrinfo):%s\n", gai_strerror (err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy (sAddr, res->ai_addr, res->ai_addrlen);
|
||||
freeaddrinfo (res);
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// convert int to char*
|
||||
//
|
||||
char* itoa(char* buffer, int number, int size) {
|
||||
snprintf (buffer, size, "%d", number);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
TCP::~TCP() {
|
||||
Close();
|
||||
}
|
||||
|
||||
TCP::TCP() {
|
||||
if (tcpinit == 0) {
|
||||
#ifdef BUILD_WINDOWS
|
||||
|
||||
WORD wVersionRequested;
|
||||
WSADATA wsaData;
|
||||
int err;
|
||||
|
||||
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
|
||||
wVersionRequested = MAKEWORD(2, 2);
|
||||
|
||||
err = WSAStartup(wVersionRequested, &wsaData);
|
||||
if (err != 0) {
|
||||
/* Tell the user that we could not find a usable */
|
||||
/* Winsock DLL. */
|
||||
printf("WSAStartup failed with error: %d\n", err);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
tcpinit = 1;
|
||||
}
|
||||
sock = 0;
|
||||
writecnt = 0;
|
||||
readcnt = 0;
|
||||
islisten = 0;
|
||||
};
|
||||
|
||||
TCP::TCP(int s) {
|
||||
TCP();
|
||||
sock = s;
|
||||
// memset (&localaddr, 0x0, sizeof(localaddr));
|
||||
// memset (&remoteaddr, 0x0, sizeof(remoteaddr));
|
||||
writecnt = 0;
|
||||
readcnt = 0;
|
||||
islisten = 0;
|
||||
};
|
||||
|
||||
TCP::TCP(string h, string p) {
|
||||
TCP();
|
||||
Connect (h,p);
|
||||
};
|
||||
|
||||
|
||||
int TCP::Listen(int port) {
|
||||
char buffer[NET_BUFFERSIZE];
|
||||
int err, i;
|
||||
struct addrinfo hints, *res, *rp;
|
||||
|
||||
if (sock > 0) Close();
|
||||
// FIXME: solution to get both (IPV4 and IPV6) to work at the same time?
|
||||
bzero (&hints, sizeof (struct addrinfo));
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
|
||||
hints.ai_family = AF_INET;
|
||||
#else
|
||||
hints.ai_family = AF_INET6;
|
||||
#endif
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
if ((err = getaddrinfo (NULL, itoa(buffer, port, 32), &hints, &res)) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// walk through all results until we could connect
|
||||
//
|
||||
for (rp = res; rp != NULL; rp = rp->ai_next) {
|
||||
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (sock == -1) continue;
|
||||
|
||||
i = 1;
|
||||
if ((err = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char*)&i, sizeof (i))) != 0) {
|
||||
printf ("%s:%d setsockopt error\n", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
if ((err = bind (sock, rp->ai_addr, rp->ai_addrlen)) < 0) {
|
||||
close (sock);
|
||||
sock = -1;
|
||||
continue;
|
||||
}
|
||||
if (listen (sock, 8) < 0) { // maximum of 8 connections at the time
|
||||
close (sock);
|
||||
sock = -1;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo (res);
|
||||
|
||||
if (rp == NULL) {
|
||||
sock = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
islisten = 1;
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
|
||||
TCP* TCP::Accept() {
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
int retval;
|
||||
SOCKET newsock;
|
||||
struct sockaddr_storage cliaddr;
|
||||
socklen_t cliaddr_len = sizeof(struct sockaddr_storage);
|
||||
TCP *tcp = NULL;
|
||||
|
||||
if (sock <= 0) return NULL;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(sock, &rfds);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 1000;
|
||||
|
||||
retval = select (sock+1, &rfds, NULL, NULL, &tv);
|
||||
if (retval == -1 && errno == EINTR) {
|
||||
retval = 0;
|
||||
}
|
||||
else if (retval == -1) {
|
||||
return NULL;
|
||||
}
|
||||
else if (retval) {
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
|
||||
newsock = accept (sock, (struct sockaddr *) &cliaddr, (int*) &cliaddr_len);
|
||||
#else
|
||||
newsock = accept (sock, (struct sockaddr *) &cliaddr, &cliaddr_len);
|
||||
#endif
|
||||
if (newsock < 0) return NULL;
|
||||
tcp = new TCP();
|
||||
tcp->SetSocket(newsock, &cliaddr, cliaddr_len);
|
||||
|
||||
return tcp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int TCP::Connect(string h, string p) {
|
||||
remote_host = h;
|
||||
remote_port = p;
|
||||
|
||||
return Connect();
|
||||
};
|
||||
|
||||
|
||||
int TCP::Connect(string hostport, int defaultport) {
|
||||
char buffer[32];
|
||||
int pos = hostport.rfind(':', hostport.length());
|
||||
if (pos == -1) {
|
||||
remote_port = itoa (buffer, defaultport, 32);
|
||||
remote_host = hostport;
|
||||
}
|
||||
else {
|
||||
remote_port = hostport.substr (pos+1, hostport.length() - pos);
|
||||
remote_host = hostport.substr (0, pos);
|
||||
}
|
||||
|
||||
return Connect();
|
||||
};
|
||||
|
||||
|
||||
int TCP::Connect() {
|
||||
int err, s = 0;
|
||||
struct addrinfo hints, *res, *rp;
|
||||
struct timeval timeout;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
err = getaddrinfo(remote_host.c_str(), remote_port.c_str(), &hints, &res);
|
||||
if (err != 0) {
|
||||
fprintf(stderr, "getaddrinfo: Host:'%s' Port:'%s' Error:%s\n", remote_host.c_str(), remote_port.c_str(), gai_strerror(err));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// walk through all results until we could connect
|
||||
//
|
||||
for (rp = res; rp != NULL; rp = rp->ai_next) {
|
||||
s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
|
||||
// setup timeout to max 2 secs
|
||||
timeout.tv_sec = 2;
|
||||
timeout.tv_usec = 0;
|
||||
setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
|
||||
|
||||
if (s == -1) continue;
|
||||
if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) {
|
||||
sock = s;
|
||||
break;
|
||||
}
|
||||
close(s);
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
//
|
||||
// connect not successfull
|
||||
//
|
||||
if (rp == NULL) return 0;
|
||||
|
||||
writecnt = 0;
|
||||
readcnt = 0;
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
|
||||
long int TCP::ReadPop (char *buffer, long int pktlen, long int bufferlen) {
|
||||
memmove (buffer, buffer+pktlen, bufferlen-pktlen);
|
||||
return bufferlen-pktlen;
|
||||
}
|
||||
|
||||
|
||||
long int TCP::Read(char *buffer, long int len) {
|
||||
long int len_ = len;
|
||||
|
||||
if (sock <= 0) return -2;
|
||||
len_ = recv (sock, buffer, len, 0);
|
||||
|
||||
if (len_ < 0 && errno == EAGAIN) len_ = 0;
|
||||
else if (len_ < 0 && errno != EAGAIN) {
|
||||
#ifdef BUILD_WINDOWS
|
||||
printf ("%s ERROR:%s\n", __FUNCTION__, strerror (errno));
|
||||
#else
|
||||
fprintf (stderr, "%s ERROR:%s\n", __FUNCTION__, strerror (errno));
|
||||
#endif
|
||||
len_ = -2;
|
||||
}
|
||||
else if (len_ == 0) len_ = -1;
|
||||
if (len_ < 0) Close();
|
||||
|
||||
readcnt += len_;
|
||||
|
||||
return len_;
|
||||
};
|
||||
|
||||
|
||||
long int TCP::ReadTimeout(char *buffer, long int len, int timeout) {
|
||||
int data = IsData (timeout);
|
||||
|
||||
if (data > 0) return Read (buffer, len);
|
||||
else if (data < 0) return -1;
|
||||
else return 0;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
//
|
||||
// write data, generate no signal if some error occures
|
||||
long int TCP::Write(char *buffer, long int len) {
|
||||
int i;
|
||||
int to = NET_MAX_RETRY;
|
||||
|
||||
if (sock <= 0) return -1;
|
||||
|
||||
do {
|
||||
i = send (sock, buffer, len, MSG_NOSIGNAL);
|
||||
} while (i == -1 && (to--) > 0 && errno == EINTR);
|
||||
|
||||
if (i < 0) Close ();
|
||||
writecnt += i;
|
||||
|
||||
return i;
|
||||
};
|
||||
|
||||
|
||||
void TCP::Close() {
|
||||
#ifdef BUILD_WINDOWS
|
||||
closesocket(sock);
|
||||
#else
|
||||
if (sock > 0) close (sock);
|
||||
#endif
|
||||
sock = -1;
|
||||
islisten = false;
|
||||
};
|
||||
|
||||
|
||||
void TCP::SetSocket(SOCKET s, struct sockaddr_storage *saddr, socklen_t saddrlen) {
|
||||
char host[NET_HOSTLEN];
|
||||
char port[NET_PORTLEN];
|
||||
int err;
|
||||
|
||||
if (sock > 0) Close();
|
||||
if (s > 0) {
|
||||
sock = s;
|
||||
|
||||
if (saddr != NULL) {
|
||||
memcpy (&peeraddr, saddr, sizeof(peeraddr));
|
||||
if ((err = getnameinfo ((struct sockaddr*) saddr, saddrlen, host, NET_HOSTLEN,
|
||||
port, NET_PORTLEN, NI_NUMERICHOST | NI_NUMERICSERV)) == 0) {
|
||||
remote_host = host;
|
||||
remote_port = port;
|
||||
} else {
|
||||
printf ("error: getnameinfo");
|
||||
/* if (err == EAI_AGAIN) printf ("EAI_AGAIN\n");
|
||||
if (err == EAI_BADFLAGS) printf ("EAI_BADFLAGS\n");
|
||||
if (err == EAI_FAIL) printf ("EAI_FAIL\n");
|
||||
if (err == EAI_FAMILY) printf ("EAI_FAMILY\n");
|
||||
if (err == EAI_MEMORY) printf ("EAI_MEMORY\n");
|
||||
if (err == EAI_NONAME) printf ("EAI_NONAME\n");
|
||||
if (err == EAI_OVERFLOW) printf ("EAI_OVERFLOW\n");
|
||||
if (err == EAI_SYSTEM) printf ("EAI_SYSTEM\n"); */ // windows seem to have different error codes
|
||||
}
|
||||
}
|
||||
}
|
||||
else sock = -1;
|
||||
};
|
||||
|
||||
|
||||
int TCP::IsConnected() {
|
||||
return (sock > 0);
|
||||
};
|
||||
|
||||
|
||||
int TCP::IsData(int timeout) {
|
||||
fd_set sockset;
|
||||
struct timeval tval;
|
||||
|
||||
if (sock <= 0) return -1;
|
||||
|
||||
FD_ZERO (&sockset);
|
||||
FD_SET (sock, &sockset);
|
||||
tval.tv_sec = timeout / 1000;
|
||||
tval.tv_usec = (timeout % 1000);
|
||||
if ((select (sock + 1, &sockset, NULL, NULL, &tval)) != -1) {
|
||||
if (FD_ISSET (sock, &sockset)) return 1;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if (errno == EBADF) sock = -1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Read a single line and split in parameter and value
|
||||
* if value and parm is set to NULL this field will not be set
|
||||
*/
|
||||
int TCP::WebHeaderGetParamValue(std::string line, std::string *parm, std::string *value) {
|
||||
size_t pos = line.find(" ");
|
||||
if (pos == std::string::npos) {
|
||||
fprintf (stderr, "%s error with protocol. HTTP/ without space? Line:'%s'\n",
|
||||
__FUNCTION__, line.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (parm != NULL) *parm = line.substr (0, pos);
|
||||
if (value != NULL) *value = line.substr (pos+1, std::string::npos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* read any data from server
|
||||
* Return Value:
|
||||
* on error: -1
|
||||
* success : size of buffer
|
||||
*/
|
||||
int TCP::WebGetFile (string url, char *buffer, int maxsize, char *formdata) {
|
||||
char outdata[NET_BUFFERSIZE];
|
||||
char indata[NET_BUFFERSIZE];
|
||||
string host, port, file;
|
||||
int len, line_cnt, i, buffpos = 0, c;
|
||||
string protocol = "";
|
||||
string type = "";
|
||||
string line = "";
|
||||
string lineUP = "";
|
||||
string status = "";
|
||||
int chunked_transfer = 0;
|
||||
int chunked_bytes = 0;
|
||||
|
||||
//
|
||||
// clear input buffer
|
||||
if (buffer == NULL) return -1;
|
||||
buffer[0] = '\0';
|
||||
|
||||
WebGetURLElements(url, &host, &port, &file);
|
||||
if (!IsConnected()) {
|
||||
Connect (host, port);
|
||||
}
|
||||
|
||||
if (!IsConnected()) return -1;
|
||||
|
||||
//
|
||||
// send http request
|
||||
//
|
||||
if (formdata == NULL)
|
||||
snprintf (outdata, NET_BUFFERSIZE, "GET %s HTTP/1.0\r\nUser-Agent: unknown\r\nHost: %s\r\nAccept:*.*\r\n\r\n", file.c_str(), host.c_str());
|
||||
else
|
||||
snprintf (outdata, NET_BUFFERSIZE, "POST %s HTTP/1.1\r\nUser-Agent: unknown\r\nHost: %s\r\nContent-Length: %ld\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept:*.*\r\n\r\n%s\r\n",
|
||||
file.c_str(), host.c_str(), strlen (formdata), formdata);
|
||||
Write (outdata, strlen (outdata));
|
||||
|
||||
//
|
||||
// read header
|
||||
//
|
||||
indata[0] = '\0';
|
||||
len = ReadTimeout (indata, NET_BUFFERSIZE-1, 2000);
|
||||
if (len <= 0) {
|
||||
fprintf (stderr, "%s:%d reading header Error:%s\n", __FILE__, __LINE__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
indata[len] = 0;
|
||||
|
||||
//
|
||||
// read all lines until an empty line is reached
|
||||
for (line = "", lineUP = "", line_cnt = 0, i = 0; i < len; i++) {
|
||||
if (indata[i] == '\n' || indata[i] == '\r') {
|
||||
if (i < len && (indata[i+1] == '\n' || indata[i+1] == '\r')) i++;
|
||||
|
||||
if (line.length() == 0) {
|
||||
i++;
|
||||
break; // break loop, read data
|
||||
}
|
||||
|
||||
//
|
||||
// HTTP Header
|
||||
else if (lineUP.find ("HTTP/") == 0) {
|
||||
if (WebHeaderGetParamValue(line, &protocol, &status) < 0) {
|
||||
fprintf (stderr, "%s:%d reading header Error:%s\n", __FILE__, __LINE__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Transfer Encoding
|
||||
else if (lineUP.find ("TRANSFER-ENCODING:") == 0) {
|
||||
std::string temp;
|
||||
if (WebHeaderGetParamValue(line, NULL, &temp) < 0) {
|
||||
fprintf (stderr, "%s:%d reading header Error:%s\n", __FILE__, __LINE__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
for (int j = 0; (long int)j < (long int) temp.length(); j++)
|
||||
temp[i] = toupper(temp[i]);
|
||||
if (temp.find("chunked") != std::string::npos) chunked_transfer = 1;
|
||||
}
|
||||
|
||||
//
|
||||
// type of file
|
||||
else if (lineUP.find ("CONTENT-TYPE:") == 0) {
|
||||
if (WebHeaderGetParamValue(line, NULL, &type) < 0) {
|
||||
fprintf (stderr, "%s:%d reading header Error:%s\n", __FILE__, __LINE__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
lineUP = line = "";
|
||||
line_cnt++;
|
||||
}
|
||||
|
||||
else {
|
||||
line += indata[i];
|
||||
lineUP += toupper(indata[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// chunked data?
|
||||
// size we need to load
|
||||
if (chunked_transfer) {
|
||||
int v;
|
||||
for (v = 0, line_cnt = 0; i < len; i++) {
|
||||
if (indata[i] == '\r' || indata[i] == '\n') {
|
||||
i++;
|
||||
if (i < len && (indata[i] == '\n' || indata[i] == '\r')) i++;
|
||||
chunked_bytes = v;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
v = v << 4;
|
||||
indata[i] = tolower(indata[i]);
|
||||
if (indata[i] >= '0' && indata[i] <= '9') v += (indata[i] - '0');
|
||||
else if (indata[i] >= 'a' && indata[i] <= 'f') v += (10 + indata[i] - 'a');
|
||||
else {
|
||||
fprintf (stderr, "%s:%d reading chunk bytes, invalid HEX code indata:'%s'\n", __FILE__, __LINE__, indata);
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// check bytes to read, and remove these from chunked_bytes
|
||||
if (chunked_bytes < len-i) c = chunked_bytes;
|
||||
else c = len-i;
|
||||
chunked_bytes -= c;
|
||||
}
|
||||
else c = len-i;
|
||||
|
||||
//
|
||||
// read data, but first copy the left over bytes into the output buffer
|
||||
if (c > 0) memcpy (buffer, indata+i, c);
|
||||
buffpos = c;
|
||||
|
||||
while ((len = ReadTimeout (indata, NET_BUFFERSIZE-1, 2000)) > 0) {
|
||||
i = len;
|
||||
if (i > maxsize-buffpos) i = maxsize-buffpos;
|
||||
if (i > 0) {
|
||||
if (chunked_transfer) {
|
||||
if (i > chunked_bytes) i = chunked_bytes;
|
||||
chunked_bytes -= i;
|
||||
}
|
||||
memcpy (buffer+buffpos, indata, i);
|
||||
buffpos += i;
|
||||
}
|
||||
}
|
||||
|
||||
return buffpos;
|
||||
};
|
||||
|
||||
|
||||
// FIXME: everything below here is wrong...
|
||||
/*
|
||||
* fills the elements and returns 0 on success. Returns set bits if value was found
|
||||
* 1 ... hostname
|
||||
* 2 ... port
|
||||
* 3 ... file
|
||||
*/
|
||||
enum _webgeturl_step_ {
|
||||
_WEBGET_URL_PROTO = 0,
|
||||
_WEBGET_URL_SERVER,
|
||||
_WEBGET_URL_PORT,
|
||||
_WEBGET_URL_FILE
|
||||
};
|
||||
int TCP::WebGetURLElements (string url, string *host, string *port, string *file) {
|
||||
string h = "";
|
||||
string p = "";
|
||||
string f = "/";
|
||||
const string prefix1 = "HTTP://";
|
||||
const string prefix2 = "HTTPS://";
|
||||
int retval = 0;
|
||||
int i;
|
||||
int curpos = 0;
|
||||
int step = _WEBGET_URL_PROTO;
|
||||
|
||||
for (i = 0; i < 5; i++) url[i] = toupper(url[i]);
|
||||
|
||||
//
|
||||
// try to find the protocol and //
|
||||
if (url.find(prefix1) == 0) {
|
||||
retval |= 2;
|
||||
p = "80";
|
||||
curpos = prefix1.length();
|
||||
}
|
||||
else if (url.find(prefix2) == 0) {
|
||||
retval |= 2;
|
||||
p = "443";
|
||||
curpos = prefix2.length();
|
||||
// FIXME: SSL needs to be implemented
|
||||
}
|
||||
else {
|
||||
if (url.find ("//") != std::string::npos) {
|
||||
errno = EPROTONOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
else curpos = 0;
|
||||
}
|
||||
|
||||
for (step = _WEBGET_URL_SERVER;curpos < (int)url.length(); curpos++) {
|
||||
if (step == _WEBGET_URL_SERVER) {
|
||||
if (url[curpos] == '/') step = _WEBGET_URL_FILE;
|
||||
else if (url[curpos] == ':') {
|
||||
step = _WEBGET_URL_PORT;
|
||||
p = "";
|
||||
}
|
||||
else {
|
||||
h += url[curpos];
|
||||
retval |= 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if (step == _WEBGET_URL_PORT) {
|
||||
if (url[curpos] == '/') step = _WEBGET_URL_FILE;
|
||||
else {
|
||||
p += url[curpos];
|
||||
retval |= 2;
|
||||
}
|
||||
}
|
||||
|
||||
else if (step == _WEBGET_URL_FILE) {
|
||||
f += url[curpos];
|
||||
retval |= 3;
|
||||
}
|
||||
}
|
||||
|
||||
if (host != NULL) *host = h;
|
||||
if (file != NULL) *file = f;
|
||||
if (port != NULL) *port = p;
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
|
||||
const string TCP::GetRemoteAddr() {
|
||||
string ret = "";
|
||||
socklen_t addrlen = sizeof (peeraddr);
|
||||
char host[256] = "";
|
||||
char port[256] = "";
|
||||
|
||||
if (getnameinfo ((struct sockaddr*)&peeraddr, addrlen, host, 255, port, 255, NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
|
||||
printf ("getnameinfo error: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
ret = (string)host + ":" + (string)port;
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
const string TCP::GetLocalAddr() {
|
||||
string ret;
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,123 @@
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// tcp.h is part of TestModbus-Server.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef _TCP_H_
|
||||
#define _TCP_H_
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__)
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#define _NTDDI_VERSION_FROM_WIN32_WINNT2(ver) ver##0000
|
||||
#define _NTDDI_VERSION_FROM_WIN32_WINNT(ver) _NTDDI_VERSION_FROM_WIN32_WINNT2(ver)
|
||||
|
||||
#ifndef _WIN32_WINNT
|
||||
# define _WIN32_WINNT 0x501
|
||||
#endif
|
||||
#ifndef NTDDI_VERSION
|
||||
# define NTDDI_VERSION _NTDDI_VERSION_FROM_WIN32_WINNT(_WIN32_WINNT)
|
||||
#endif
|
||||
|
||||
// #include <winsock.h>
|
||||
#include <winsock2.h>
|
||||
#include <io.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <string.h>
|
||||
|
||||
#define socklen_t size_t
|
||||
|
||||
#ifndef bzero
|
||||
#define bzero(__z__, __x__) memset (__z__, 0x0, __x__)
|
||||
#endif
|
||||
|
||||
#ifndef MSG_NOSIGNAL
|
||||
# define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define SOCKET int
|
||||
|
||||
#define NET_HOSTLEN 256
|
||||
#define NET_PORTLEN 6
|
||||
#define NET_BUFFERSIZE 4096
|
||||
#define NET_MAX_RETRY 5 // retry to send data EINTR
|
||||
#define NET_MAX_TIMEOUT 30000 // timeout in ms
|
||||
|
||||
|
||||
/************************************************************************
|
||||
*
|
||||
* global functions needed for networking
|
||||
*
|
||||
*/
|
||||
int dns_filladdr (string host, string port, int ai_family,
|
||||
struct sockaddr_storage *sAddr);
|
||||
char *itoa(char* buffer, int number, int size);
|
||||
void UDPTCPNetwork_Startup();
|
||||
extern int UDPTCPNetwork_init;
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* tcp related functions
|
||||
*/
|
||||
class TCP {
|
||||
private:
|
||||
SOCKET sock;
|
||||
struct sockaddr_storage peeraddr;
|
||||
string remote_host;
|
||||
string remote_port;
|
||||
int readcnt;
|
||||
int writecnt;
|
||||
int islisten;
|
||||
|
||||
int WebHeaderGetParamValue(std::string line, std::string *parm, std::string *value);
|
||||
public:
|
||||
TCP();
|
||||
TCP(SOCKET s);
|
||||
TCP(string h, string p);
|
||||
TCP(string hostport, int defaultport);
|
||||
~TCP();
|
||||
|
||||
int Connect();
|
||||
int Connect(string h, string p);
|
||||
int Connect(string hostport, int defaultport);
|
||||
long int ReadPop (char *buffer, long int pktlen, long int bufferlen);
|
||||
long int ReadTimeout(char *buffer, long int len, int timeout);
|
||||
long int Read(char *buffer, long int len);
|
||||
long int Write(char *buffer, long int len);
|
||||
void Close();
|
||||
int IsConnected();
|
||||
int IsData(int timeout); // timeout in ms;
|
||||
int IsListen() { return islisten; };
|
||||
|
||||
int Listen(int port);
|
||||
TCP* Accept();
|
||||
|
||||
SOCKET GetSocket() { return sock; };
|
||||
void SetSocket(SOCKET s, struct sockaddr_storage *saddr, socklen_t saddrlen);
|
||||
|
||||
const string GetRemoteAddr();
|
||||
const string GetLocalAddr();
|
||||
|
||||
int WebGetURLElements (string url, string *host, string *port, string *file);
|
||||
int WebGetFile (string url, char *buffer, int maxsize, char *formdata);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in new issue