// //nodeserv.c // //Noderef server. Functions for managing noderefs. // // //-UserX 2001/11/13 #include "misc/compat.h" #include #include #include #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