//
//nodeserv.c
//
//Noderef server. Functions for managing noderefs.
//
//
//-UserX 2001/11/13
#include "misc/compat.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "net/nodeserv.h"
#include "net/noderef.h"
#include "crypt/random.h"
#include "base/logger.h"
#include "base/str.h"
#include "base/strarray.h"
#include "base/parse.h"
#include "misc/global.h"
#include "net/sock.h"
#include "base/array.h"
enum NR_ {
NR_HOST,
NR_PORT,
NR_PROTOCOL,
NR_TRANSPORT,
NR_PUBLICKEY,
NR_PRIVATEKEY,
NR_SPECIAL,
NR_NETNAME,
NR_VERSION,
NR_NETWORK_KEY,
NR_NETWORK_SECRET_KEY,
NR_NETWORK_PROTOCOL,
NR_NETWORK_NAME,
NR_NETWORK_VERSION
};
StringIntPair noderefkeys[] = {
{"host", NR_HOST},
{"port", NR_PORT},
{"protocol", NR_PROTOCOL},
{"transport", NR_TRANSPORT},
{"publickey", NR_PUBLICKEY},
{"privatekey", NR_PRIVATEKEY},
{"special", NR_SPECIAL},
{"netname", NR_NETNAME},
{"version", NR_VERSION},
{"networkid", NR_NETWORK_KEY} ,
{"networkkey", NR_NETWORK_KEY} ,
{"networksecret", NR_NETWORK_SECRET_KEY} ,
{"networkprivatekey", NR_NETWORK_SECRET_KEY} ,
{"networkentry", NR_NETWORK_PROTOCOL},
{"networkprotocol", NR_NETWORK_PROTOCOL} ,
{"networkname", NR_NETWORK_NAME} ,
{"networkversion", NR_NETWORK_VERSION} ,
{NULL, -1}
};
StringIntPair listenrefkeys[] = {
{"host", NR_HOST},
{"port", NR_PORT},
{"protocol", NR_PROTOCOL},
{"transport", NR_TRANSPORT},
{"publickey", NR_PUBLICKEY},
{"privatekey", NR_PRIVATEKEY},
{"special", NR_SPECIAL},
{NULL, -1}
};
char defaultnodereffilename[] = DEFAULT_NODEREFFILENAME;
char *nodereffilename = NULL;
char defaultlistenreffilename[] = "listen.ref";
char *listenreffilename = NULL;
char *networkname = NULL;
char *networkversion = NULL;
int noderefreload = 1;
int nodeservCheckNetworkNameVersion(char *netname, char * netversion) {
StringArrayHandle *sanew;
StringArrayHandle *sacurrent;
int i;
if(isStringBlank(netname) || isStringBlank(networkname)) {
i = 1;
} else {
sanew = saMakeFromSplitString(netname, ",");
sacurrent = saMakeFromSplitString(networkname, ",");
saTrim(sanew, " \t");
saTrim(sacurrent, " \t");
i = saMatchAny(sanew, sacurrent);
saFree(sanew);
saFree(sacurrent);
}
if(i == 0) {
return 0;
}
if(isStringBlank(netversion) || isStringBlank(networkversion)) {
i = 1;
} else {
sanew = saMakeFromSplitString(netversion, ",");
sacurrent = saMakeFromSplitString(networkversion, ",");
saTrim(sanew, " \t");
saTrim(sacurrent, " \t");
i = saMatchAny(sanew, sacurrent);
saFree(sanew);
saFree(sacurrent);
}
return i;
}
void nodeservAddNodeRefFile(char *filename) {
int i;
char *s;
char *netname = NULL;
char *netversion = NULL;
int netok = 1;
StringPairArrayHandle *spa;
ENodeRef *nr = NULL;
nraCheckMake();
spa = spaReadFile(filename);
if(spa == NULL) {
LOGERROR(stringCopyMany("nodeservAddNodeRefFile: no noderef file, filename(", filename, "): No 'node.ref' file found.", NULL));
return;
}
LOGDEBUG(stringJoin("nodeservAddNodeRefFile: spa->size:", intToString(spa->size)));
for(i = 0; i < spa->size; i++) {
s = spa->data[i].val;
switch(stringIntPairSearch(noderefkeys, spa->data[i].key)) {
case NR_NETWORK_NAME:
netname = stringReplace(netname, s);
netok = nodeservCheckNetworkNameVersion(netname, netversion);
break;
case NR_NETWORK_VERSION:
netversion = stringReplace(netversion, s);
netok = nodeservCheckNetworkNameVersion(netname, netversion);
break;
}
if(netok != 0) {
switch(stringIntPairSearch(noderefkeys, spa->data[i].key)) {
case NR_HOST:
if(s != NULL) {
//todo: format checking on hostname
nr = nraAdd(s);
}
break;
case NR_PORT:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: numeric format checking
nr->noderef->PortNum = atoi(s);
}
break;
case NR_PROTOCOL:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: format checking
nr->noderef->Protocol = stringReplace(nr->noderef->Protocol, s);
}
break;
case NR_TRANSPORT:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: format checking
nr->noderef->Transport = stringReplace(nr->noderef->Transport, s);
}
break;
case NR_PUBLICKEY:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: format checking
nr->noderef->PublicKey = stringReplace(nr->noderef->PublicKey, s);
}
break;
case NR_PRIVATEKEY:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: format checking
nr->noderef->PrivateKey = stringReplace(nr->noderef->PrivateKey, s);
}
break;
case NR_SPECIAL:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: format checking
nr->noderef->Special = stringReplace(nr->noderef->Special, s);
switch(stringIntPairSearch(noderefspecials, nr->noderef->Special)) {
case NRS_ENTRY:
if(nr->noderef->PublicKey == NULL && networkKey != NULL) {
nr->noderef->PublicKey = stringCopy(networkKey);
}
if(nr->noderef->Protocol == NULL && networkProtocol != NULL) {
nr->noderef->Protocol = stringCopy(networkProtocol);
}
break;
case NRS_EXIT:
if(nr->noderef->PrivateKey == NULL && networkKey != NULL) {
nr->noderef->PrivateKey = stringCopy(networkSecretKey);
}
if(nr->noderef->Protocol == NULL && networkProtocol != NULL) {
nr->noderef->Protocol = stringCopy(networkProtocol);
}
break;
case -1:
default:
if(isStringBlank(nr->noderef->Special)) {
LOGERROR("Unknown/unhandled special type.");
}
}
}
break;
case NR_NETNAME:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
nr->noderef->netname = stringReplace(nr->noderef->netname, s);
}
break;
case NR_VERSION:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
nr->noderef->version = stringReplace(nr->noderef->version, s);
}
break;
case NR_NETWORK_KEY:
networkKey = stringReplace(networkKey, s);
break;
case NR_NETWORK_SECRET_KEY:
networkSecretKey = stringReplace(networkSecretKey, s);
break;
case NR_NETWORK_PROTOCOL:
networkProtocol = stringReplace(networkProtocol, s);
break;
case -1:
LOGERROR(stringCopyMany(
"File: \"",
filename,
"\", unknown key \"",
spa->data[i].key,
"\", with value \"",
spa->data[i].val,
"\"",
NULL));
break;
case NR_NETWORK_NAME:
case NR_NETWORK_VERSION:
break;
default:
LOGERROR(stringCopyMany(
"Internal error: Key \"",
spa->data[i].key,
"\" is known but is unhandled, with value \"",
spa->data[i].val,
"\"",
NULL));
}
}
}
spaFree(spa);
stringFree(netname);
stringFree(netversion);
}
//Sets the name of the node ref file. or reset to the default if filename is blank or NULL.
void nodeservSetNodeRefFile(char *filename) {
stringFree(nodereffilename);
if(isStringBlank(filename)) {
nodereffilename = NULL;
} else {
nodereffilename = stringCopy(filename);
}
}
void nodeservReadNodeRefFile() {
if(nodereffilename == NULL) {
nodeservAddNodeRefFile(defaultnodereffilename);
} else {
nodeservAddNodeRefFile(nodereffilename);
}
LOGMINOR(stringJoinMany("nodeservReadNodeRefFile: noderef total(", intToString(NRA->size),")", NULL));
}
void nodeservClearNodeRefs(void) {
if(NRA == NULL) {
nraCheckMake();
}
LOGMINOR("nodeservClearNodeRef: Cleared noderefs");
arrayDelete((ArrayHandle *)NRA, 0, NRA->size);
}
//todo: make node selection more aware of statistics
//picks a node
NodeRef *nodeservPickNode() {
int i;
if(noderefreload != 0) {
nodeservClearNodeRefs();
nodeservReadNodeRefFile();
}
if(NRA == NULL || NRA->size == 0) {
return NULL;
}
i = randomint(NRA->size);
LOGMINOR(stringJoinMany("nodeservPickNode: Node index(", intToString(i),")", NULL));
return noderefCopy(NRA->data[i].noderef);
}
//picks the next NodeRef
//The input NodeRef will be released.
NodeRef *nodeservPickNextNode(NodeRef *nr) {
int i = nraFindNodeByITag(nr->iTag);
if(i == -1) {
return nodeservPickNode();
}
noderefFree(nr);
return noderefCopy(NRA->data[(++i) % NRA->size].noderef);
}
int nodeservTotalNodeRefs(){
return NRA->size;
}
void nodeservAddListenRefFile(char *filename) {
int i;
char *s;
StringPairArrayHandle *spa;
NodeRef *nr = NULL;
nraCheckMake();
spa = spaReadFile(filename);
if(spa == NULL) {
LOGERROR(stringCopyMany("nodeservAddListenRefFile: no listenref file, filename(", filename, "): No 'listen.ref' file found.", NULL));
return;
}
LOGDEBUG(stringJoin("nodeservAddListenRefFile: spa->size:", intToString(spa->size)));
for(i = 0; i < spa->size; i++) {
s = spa->data[i].val;
switch(stringIntPairSearch(listenrefkeys, spa->data[i].key)) {
case NR_HOST:
if(s != NULL) {
//todo: format checking on hostname
nr = lnraAdd(s);
if(nr != NULL) {
nr->PortNum = localPort;
}
}
break;
case NR_PORT:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: numeric format checking
nr->PortNum = atoi(s);
}
break;
case NR_PROTOCOL:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: format checking
nr->Protocol = stringReplace(nr->Protocol, s);
}
break;
case NR_TRANSPORT:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: format checking
nr->Transport = stringReplace(nr->Transport, s);
}
break;
case NR_PUBLICKEY:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: format checking
nr->PublicKey = stringReplace(nr->PublicKey, s);
}
break;
case NR_PRIVATEKEY:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: format checking
nr->PrivateKey = stringReplace(nr->PrivateKey, s);
}
break;
case NR_SPECIAL:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
//todo: format checking
nr->Special = stringReplace(nr->Special, s);
switch(stringIntPairSearch(noderefspecials, nr->Special)) {
case NRS_ENTRY:
if(nr->PublicKey == NULL && networkKey != NULL) {
nr->PublicKey = stringCopy(networkKey);
}
if(nr->Protocol == NULL && networkProtocol != NULL) {
nr->Protocol = stringCopy(networkProtocol);
}
break;
case NRS_EXIT:
if(nr->PrivateKey == NULL && networkKey != NULL) {
nr->PrivateKey = stringCopy(networkSecretKey);
}
if(nr->Protocol == NULL && networkProtocol != NULL) {
nr->Protocol = stringCopy(networkProtocol);
}
break;
case -1:
default:
if(!isStringBlank(nr->Special)) {
LOGERROR("Unknown/unhandled special type.");
}
}
}
break;
case NR_NETNAME:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
nr->netname = stringReplace(nr->netname, s);
}
break;
case NR_VERSION:
if(nr == NULL) {
LOGERROR("\"host\" must be first in a node reference file.");
} else {
nr->version = stringReplace(nr->version, s);
}
break;
case -1:
LOGERROR(stringCopyMany(
"File: \"",
filename,
"\", unknown key \"",
spa->data[i].key,
"\", with value \"",
spa->data[i].val,
"\"",
NULL));
break;
default:
LOGERROR(stringCopyMany(
"Internal error: Key \"",
spa->data[i].key,
"\" is know but is unhandled, with value \"",
spa->data[i].val,
"\"",
NULL));
}
}
spaFree(spa);
}
//Sets the name of the node ref file. or reset to the default if filename is blank or NULL.
void nodeservSetListenRefFile(char *filename) {
stringFree(listenreffilename);
if(isStringBlank(filename)) {
listenreffilename = NULL;
} else {
listenreffilename = stringCopy(filename);
}
}
void nodeservReadListenRefFile(void) {
if(listenreffilename == NULL) {
nodeservAddListenRefFile(defaultlistenreffilename);
} else {
nodeservAddListenRefFile(listenreffilename);
}
}
void nodeservClearListenRefs(void) {
if(LNRA == NULL) {
nraCheckMake();
}
arrayDelete((ArrayHandle *)LNRA, 0, LNRA->size);
}
void ndoeservWriteListenRefFile(char *filename) {
//FileHandle *fh;
StringPairArrayHandle *spa;
int i;
NodeRef *nr = NULL;
if(isStringBlank(filename)) {
return;
}
//fh = fileOpen(filename, "w");
spa = spaMake();
for(i = 0; i < LNRA->size; i++) {
nr = &LNRA->data[i];
spaAppend(spa, stringPairMakeCopy("host", nr->HostName));
//fprintf(fh, "host = %s\n", nr->HostName);
if(nr->PortNum != localPort) {
//fprintf(fh, "port = %d\n", nr->PortNum);
spaAppend(spa, stringPairMake("port", intToString(nr->PortNum)));
}
if(!isStringBlank(nr->Transport)) {
//fprintf(fh, "transport = %s\n", nr->Transport);
spaAppend(spa, stringPairMakeCopy("transport", nr->Transport));
}
if(stringCaseCompare(nr->Special, "entry") == 0) {
//fprintf(fh, "special = %s\n", nr->Special);
spaAppend(spa, stringPairMakeCopy("special", nr->Special));
if(!isStringBlank(nr->PublicKey)) {
if(stringCaseCompare(nr->PublicKey, networkKey) != 0) {
//fprintf(fh, "publickey = %s\n", nr->PublicKey);
spaAppend(spa, stringPairMakeCopy("publickey", nr->PublicKey));
}
}
if(!isStringBlank(nr->Protocol)) {
if(stringCaseCompare(nr->Protocol, networkProtocol) != 0) {
//fprintf(fh, "protocol = %s\n", nr->Protocol);
spaAppend(spa, stringPairMakeCopy("protocol", nr->Protocol));
}
}
} else {
if(!isStringBlank(nr->PublicKey)) {
//fprintf(fh, "publickey = %s\n", nr->PublicKey);
spaAppend(spa, stringPairMakeCopy("publickey", nr->PublicKey));
}
if(!isStringBlank(nr->Protocol)) {
//fprintf(fh, "protocol = %s\n", nr->Protocol);
spaAppend(spa, stringPairMakeCopy("protocol", nr->Protocol));
}
}
if(!isStringBlank(nr->PrivateKey)) {
//fprintf(fh, "privatekey = %s\n", nr->PrivateKey);
spaAppend(spa, stringPairMakeCopy("privatekey", nr->PrivateKey));
}
}
//fclose(fh);
spaWriteFile(spa, filename);
spaFree(spa);
}
void nodeservWriteListenRef(void) {
if(isStringBlank(listenreffilename)) {
ndoeservWriteListenRefFile(defaultlistenreffilename);
} else {
ndoeservWriteListenRefFile(listenreffilename);
}
}
//submits a noderef to an email address
//somewhat quick and dirty. will appear to "lock up" if the server doesn't
//reply to SMTP commands.
//returns non-zero if failed.
int nodeservSubmitNode(NodeRef *nr, char *page, char *server, int port) {
NodeRef *enr;
SockHandle *sh;
char *s;
char *l, *r;
char *code;
if(server == NULL || page == NULL) {
return 1;
}
if(port == 0) {
port = 80;
}
enr = noderefMake();
noderefInit(enr, stringCopy(server), NULL, "tcp", NULL, NULL, NULL, NULL, NULL, port);
sh = sockMake();
if(sockSetNodeRef(sh, enr) != SOCK_ERR_NONE) {
sockFree(sh);
noderefFree(enr);
return 1;
}
if(sockOpen(sh) != SOCK_ERR_NONE) {
sockFree(sh);
noderefFree(enr);
return 1;
}
s = stringCopyMany(
page, "?",
"clientversion=110&", // this should be read from a global constant
"host=", nr->HostName, "&",
"port=", intToString(nr->PortNum), "&",
"protocol=", nr->Protocol, "&",
"publickey=", nr->PublicKey, "&",
"networkname=", networkname, "&",
"networkversion=", networkversion, "&",
NULL);
pipeWriteString(&sh->IOPipe, stringCopyMany(
"GET ", s, " HTTP/1.0\x0d\x0a",
"User-Agent: iip/1.1\x0d\x0a",
"Accept: */*\x0d\x0a",
"\x0d\x0a",
NULL
));
//stringFree(s);
while((sh->IOPipe.outBuffer->size != 0) && ((sh->status & SOCK_STATUS_CLOSING) == 0)) {
sockWrite(sh);
}
while((sh->status & SOCK_STATUS_CLOSING) == 0) {
sockRead(sh);
}
s = dblockToString(sh->IOPipe.inBuffer);
sockClose(sh);
sockFree(sh);
noderefFree(enr);
r = stringSplit(s, "\x0d\x0a");
l = stringSplit(r, " ");
code = stringSplit(r, " ");
code = stringTrim(code, " ");
stringFree(l);
stringFree(r);
l = stringSplit(s, "\x0d\x0a\x0d\x0a");
stringFree(l);
if(stringCompare(code, "200") == 0) {
stringFree(s);
return 0;
} else {
stringFree(s);
return 1;
}
}
//Qucik and drity HTTP page request
//todo: maybe: add proxy support to this
int nodeservGetNodeRefs(char *page, char *server, int port) {
FileHandle *fh;
NodeRef *enr;
SockHandle *sh;
char *s;
char *l, *r;
char *code;
if(server == NULL || page == NULL) {
return 1;
}
if(port == 0) {
port = 80;
}
enr = noderefMake();
noderefInit(enr, stringCopy(server), NULL, "tcp", NULL, NULL, NULL, NULL, NULL, port);
sh = sockMake();
if(sockSetNodeRef(sh, enr) != SOCK_ERR_NONE) {
sockFree(sh);
noderefFree(enr);
return 1;
}
if(sockOpen(sh) != SOCK_ERR_NONE) {
sockFree(sh);
noderefFree(enr);
return 1;
}
s = stringAppend2(server, stringAppend2(":", intToString(port)));
pipeWriteString(&sh->IOPipe, stringCopyMany(
"GET ", page, " HTTP/1.0\x0d\x0a",
"User-Agent: iip/1.1\x0d\x0a",
"Host:", s, "\x0d\x0a",
"Accept: */*\x0d\x0a",
"\x0d\x0a",
// "\x0d\x0a",
NULL
));
stringFree(s);
while((sh->IOPipe.outBuffer->size != 0) && ((sh->status & SOCK_STATUS_CLOSING) == 0)) {
sockWrite(sh);
}
while((sh->status & SOCK_STATUS_CLOSING) == 0) {
sockRead(sh);
}
s = dblockToString(sh->IOPipe.inBuffer);
sockClose(sh);
sockFree(sh);
noderefFree(enr);
r = stringSplit(s, "\x0d\x0a");
l = stringSplit(r, " ");
code = stringSplit(r, " ");
code = stringTrim(code, " ");
stringFree(l);
stringFree(r);
l = stringSplit(s, "\x0d\x0a\x0d\x0a");
stringFree(l);
if(stringCompare(code, "200") == 0 && nodeservNodeRefListValid(s)) {
if(nodereffilename == NULL) {
fh = fileOpen(defaultnodereffilename, "w");
} else {
fh = fileOpen(nodereffilename, "w");
}
if(fh != NULL) {
//todo: error checking
fileWriteStringKeep(fh, s);
fileClose(fh);
}
nodeservClearNodeRefs();
nodeservReadNodeRefFile();
stringFree(s);
return 0;
} else {
stringFree(s);
return 1;
}
}
int nodeservNodeRefListValid(char *nodereflist) {
/*todo: make this smarter*/
if(
strstr(nodereflist, "host") != NULL ||
strstr(nodereflist, "Host") != NULL ||
strstr(nodereflist, "HOST") != NULL ||
strstr(nodereflist, "hOST") != NULL
) {
return 1;
} else {
return 0;
}
}
//todo: node statistic functions
syntax highlighted by Code2HTML, v. 0.9.1