/**************************************************************************/ // resolver.cpp // can manually compile with: g++ -o resolver resolver.cpp // The resolver binary should be in same directory as the dawn binary for // dawn to take advantage of it. /**************************************************************************/ // Please note: this should really be rewritten from scratch, but due to // timelines to release the codebase, is being released as is. /**************************************************************************/ // some parameters #define DATE_IN_LOGS // if the date is in the log entires #define RESOLVER_VERSION "1.5" #define VERBOSE_LEVEL (0) #define IDENT_CONNECT_TIMEOUT (15) #define MSL (2048) #define HSL (16384) //#define RESOLVER_DEBUG_BRACKETS /**************************************************************************/ #define __IN_RESOLVER_CPP__ // so resolver.h doesn't complain #include "resolver.h" /**************************************************************************/ // caching stats int iHostLookups=0; int iHostLookupsFoundInCache=0; // prototypes void sleep_seconds(int seconds); hnode *find_in_cache(sockaddr_type *socket_address, size_t address_length); time_t current_time(void); bool resolve_text_ip_into_address(sockaddr_type *socket_address, size_t *address_length, char*pszAddress, int *error_value, char *error_message); hnode *lookup_host(sockaddr_type *socket_address, size_t address_length); /**************************************************************************/ #ifdef WIN32 #define redirect_socket_error_text(r) get_socket_error_text(r) #else #define redirect_socket_error_text(r) gai_strerror(r) #endif /**************************************************************************/ typedef const char *resolver_function(char *argument); struct command_type{ char *name; char *syntax; resolver_function *func; }; /**************************************************************************/ const char *resolver_close(char *argument) { resolver_logf( 0,"close command received - ending resolver"); exit( 0 ); return ""; } /**************************************************************************/ const char *resolver_stats(char *argument) { static char result[MSL]; snprintf(result, sizeof(result), "HostLookups: %d, HostLookupsResolvedFromCache: %d (%0.2f%%)", iHostLookups, iHostLookupsFoundInCache, (iHostLookups? ((float)iHostLookupsFoundInCache/(float)iHostLookups)*100 :0.0) ); return result; } /**************************************************************************/ const char *resolver_version(char *argument) { static char result[MSL]; snprintf(result, sizeof(result), "%s", RESOLVER_VERSION); return result; } /**************************************************************************/ // look up an ip address or hostname const char *resolver_resolve(char *argument) { bool system_result=false; bool log_resolve=true; hnode * pHost; static char result[HSL]; char initial_request[HSL]; // process requests in the format: 'resolve <requesting_name> <domain_name>' // get the name of the person we are resolving for char requesting_name[200]; char *n=argument; while(!IS_NULLSTR(n) && isspace(*n)){ // trim off any leading whitespace n++; } // *n is either the end of the string or a non space character char *name_start=n; for( ;!IS_NULLSTR(n) && !isspace(*n); n++){ // loop till we find the next space or end of string } if(IS_NULLSTR(n)){ // premature end of string -> error // we return the message with a destination of "system" since no name was specified snprintf( result, sizeof(result), "system invalid request - requesting name not specified."); return result; } // *n contains a space after one or more non space characters *n='\0'; // terminate the name_start if(strlen(name_start)>sizeof(requesting_name)-1){ // we return the message with a destination of "system" since no name was specified snprintf( result, sizeof(result), "system invalid request - requesting name too long."); return result; } // copy name_start into requesting_name strcpy(requesting_name, name_start); n++; // advance over the terminating we just did // fast forward to find the next piece of text while(!IS_NULLSTR(n) && isspace(*n)){ n++; } if(IS_NULLSTR(n)){// premature end of string -> error // we return the message with a destination of "system" since no name was specified snprintf( result, sizeof(result), "%s invalid request - nothing specified to resolve.", requesting_name); return result; } log_resolve=(requesting_name[0]!='*' || requesting_name[1]); if(!strcmp(requesting_name,"system")){ log_resolve=false; system_result=true; } if(log_resolve){ resolver_logf( 5,"resolving '%s' for '%s'", n, requesting_name); } // check that we have a valid ipv4 or ipv6 address in szIPAddress // by attempting to resolve it, if we don't the below function will // return false sockaddr_type socket_address; size_t address_length; initial_request[0]='\0'; bool valid_ip=resolve_text_ip_into_address(&socket_address, &address_length, n, NULL, NULL); if(valid_ip){ // we have an ip... look that up, get the dns name then resolve that pHost=lookup_host(&socket_address, address_length); if(strcmp(pHost->hostname,n)){ sprintf(initial_request, "Initial request for %s resolves to '%s'`1", n, pHost->hostname); } n=pHost->hostname; } // at this stage, n points to the string we want to resolve on the following basis: // - if the request was originally an ip address, that has been looked up, // and we are now resolving the hostname if it could be resolved (otherwise the ip) // - if the request was a hostname all along, we are resolving that. #ifdef IPV6_SUPPORT_ENABLED { // ipv4/ipv6 lookup code struct addrinfo hints, *reshead, *res; int r; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags=AI_CANONNAME; r=getaddrinfo(n, "80", &hints, &res); if(r){ // if resolution failed, try resolving once more incase it is due to slow DNS if(res){ freeaddrinfo(res); } r=getaddrinfo(n, "80", &hints, &res); } if(r){ // failed to resolve two times in a row, report the error and give up sprintf(result,"%s resolver_resolve(): getaddrinfo() error %d (%s) occurred while attempting to resolve '%s'.", requesting_name, r, redirect_socket_error_text(r), n); if(res){ freeaddrinfo(res); } return (log_resolve||system_result)?result:""; } if(!res){ // if it did resolve, but didn't give us a usable result report that sprintf(result,"%s resolver_resolve(): getaddrinfo() returned an empty res value when resolving '%s' - effectively no result.", requesting_name, n); return (log_resolve||system_result)?result:""; } // successful resolution, format the results if(IS_NULLSTR(res->ai_canonname)){ sprintf(result, "%s %sResolve request for:`1 %s", initial_request, requesting_name, n); }else{ // canonical name of the specified node (offical name) sprintf(result, "%s %sResolve request for:`1 %s`1Official Name:`1 %s", requesting_name, initial_request, n, res->ai_canonname); } reshead=res; // save the head of the res list for freeaddrinfo for(; res; res= res->ai_next){ char temp[MSL]; char sz_host[NI_MAXHOST+1]; int hostlen = NI_MAXHOST; r= getnameinfo( res->ai_addr, res->ai_addrlen, sz_host, hostlen, NULL, 0,NI_NUMERICHOST); if(r){ snprintf(temp, MSL-2, "`1unexpected error converting binary ai_addr into text version!"); }else{ char family[MSL]; if(res->ai_family==PF_INET){ strcpy(family, "ipv4"); }else if(res->ai_family==PF_INET6){ strcpy(family, "ipv6"); }else{ sprintf(family, "%d", res->ai_family); } snprintf(temp, MSL-2, "`1%s-'%s'", family, sz_host); } strcat(result,temp); if(strlen(result)>HSL-MSL){ break; } } } #else { // ipv4 lookup only code struct hostent *h; h=gethostbyname(n); if(h && (log_resolve || system_result)){ // we have a match with one or more addresses char temp[8196]; if(strlen(h->h_name)<8000){ // offical name sprintf(result,"%s %sResolve request for:`1 %s`1`1Official Name:`1 %s", requesting_name, initial_request, n, h->h_name); if(h->h_addrtype!=AF_INET){ sprintf(temp, "`1Unrecognised address type %d result, can't display actual address/addresses.", h->h_addrtype); strcat(result,temp); }else{ // ip addresses if(h->h_addr_list[0]){ int ai=0; for( ; h->h_addr_list[ai]; ai++){ in_addr address; memcpy(&address.s_addr, h->h_addr_list[ai], h->h_length); sprintf(temp, "`1ipv4-'%s'", inet_ntoa(address)); if(strlen(result)+ strlen(temp)<8000){ strcat(result, temp); } } } // aliases if(h->h_aliases && *(h->h_aliases)){ strcat(result, "`1`1Aliases include:"); char **an=h->h_aliases; while(*an){ sprintf(temp, "`1 '%s'", *an); if(strlen(result)+ strlen(temp)< 8000){ strcat(result, temp); } an++; } } } return (log_resolve||system_result)?result:""; } snprintf( result, sizeof(result), "%s '%s' resolved, but was too long (%d characters).", requesting_name, n, strlen(h->h_name)); }else{ snprintf( result, sizeof(result), "%s Failed to resolve: '%s'", requesting_name, n); } } #endif // not IPV6_SUPPORT_ENABLED return (log_resolve||system_result)?result:"";; } /**************************************************************************/ // return what the text ip resolves to // fill in the socket_address details based on the szAddress const char *resolver_resolveip(char *argument) { hnode * pHost; static char result[HSL]; int errval; char errmsg[MSL]; sockaddr_type socket_address; size_t address_length; // check that we have a valid ipv4 or ipv6 address in szIPAddress // by attempting to resolve it, if we don't the below function will // return false if(!resolve_text_ip_into_address(&socket_address, &address_length, argument, &errval, errmsg)){ resolver_logf(errval, "%s", errmsg); sprintf(result, "unrecognised address '%s'", argument); return result; } // report what we are about to do resolver_logf(3, "dns lookup: %s", argument); // order to do things in is first dns (more important), then ident pHost= lookup_host(&socket_address, address_length); // looks up the host, and handles caching // display the dns results // resolver_responsef("ip %s %s", pHost->szAddr, pHost->hostname); // resolver_logf(2, "IP RESULT SENT: %s %s", pHost->szAddr, pHost->hostname); sprintf(result, "ip=%s lookup=%s reverse=%s", pHost->szAddr, pHost->hostname, pHost->szAddr); return result; } /**************************************************************************/ const char *resolver_commands(char *); command_type command_table[]={ {"close", "", resolver_close}, {"commands", "", resolver_commands}, {"resolve", "<requesting_user> <ip|dns name>", resolver_resolve}, {"resolveip", "<ip>", resolver_resolveip}, {"stats", "", resolver_stats}, {"version", "", resolver_version}, {"", NULL} }; /**************************************************************************/ const char *resolver_commands(char *argument) { static char result[MSL]; result[0]='\0'; for(int i=0; !IS_NULLSTR(command_table[i].name);){ strcat(result,command_table[i].name); i++; if(!IS_NULLSTR(command_table[i].name)){ strcat(result," "); } } return result; } /**************************************************************************/ // process a command (all commands are on a single line) // return true if a match was found bool interpret_line(const char *line) { char entire_line[MSL]; char command[MSL]; char *argstart; strncpy(entire_line, line, sizeof(entire_line)-1); entire_line[sizeof(entire_line)-1]='\0'; // find the first space in the table argstart=strstr(entire_line, " "); if(argstart){ *argstart='\0'; // terminate the command argstart++; // args are the first word after the command }else{ argstart=""; } strcpy(command, entire_line); // find the "command" in the command_table // resolver_logf(5, "'%s' command received: argstart='%s'", command, argstart); for(int i=0; !IS_NULLSTR(command_table[i].name); i++){ if(!strcmp(command_table[i].name, command)){ const char *response=(*(command_table[i].func)) (argstart); if(!IS_NULLSTR(response)){ resolver_responsef("%s_response %s", command, response); } return true; } } resolver_responsef("%s_response unrecognised command", command); return false; } /**************************************************************************/ void resolve_ident_failed(hnode *host, char *reason) { if(!IS_NULLSTR(reason)){ resolver_logf(5, "%s",reason); } host->ident_failure_count++; host->retry_ident_after=current_time()+ (host->ident_failure_count*60); } /**************************************************************************/ char *read_from_socket(SOCKET Socket, int timeout) { static char data[1024]; fd_set fdRead; fd_set fdWrite; fd_set fdException; FD_ZERO(&fdRead); FD_ZERO(&fdWrite); FD_ZERO(&fdException); FD_SET(Socket, &fdRead); FD_SET(Socket, &fdWrite); FD_SET(Socket, &fdException); struct timeval select_timeout; select_timeout.tv_usec=0; select_timeout.tv_sec=timeout; if(select(Socket+1, &fdRead, NULL, NULL, &select_timeout)>0){ if(recv(Socket, data, 1023, 0)<0){ return ""; } data[1023]='\0'; return data; } return ""; } /**************************************************************************/ int connect_with_timeout(SOCKET s, const struct sockaddr * addr, int timeout) { #ifdef WIN32 // set the socket to non-blocking mode unsigned long blockmode = 1; if(ioctlsocket(s, FIONBIO, &blockmode)== SOCKET_ERROR) { resolver_logf(8, "ioctlsocket: error setting new socket to nonblocking, " "WSAGetLastError=%d", WSAGetLastError()); return SOCKET_ERROR; } // start the connection int nRet= connect(s, addr, sizeof(SOCKADDR_IN)); if(nRet==0){ return 0; }else{ if(WSAGetLastError()!=WSAEWOULDBLOCK){ socket_error("connect_with_timeout: connect()"); } } // no use a select call to wait for the socket to be ready/timeout // set our select_timeout, to timeout seconds struct timeval select_timeout; select_timeout.tv_sec=timeout; select_timeout.tv_usec=0; // prepare our filedescriptor set fd_set fdWrite; FD_ZERO(&fdWrite); FD_SET(s, &fdWrite); // select and wait nRet=select(s+1, NULL, &fdWrite, NULL, &select_timeout); if(nRet<1){ if(nRet==0){ resolver_logf(5, "connect timeout."); }else{ resolver_logf(5, "select() an error occured."); } return SOCKET_ERROR; } return 0; // successful connect #else // unix version of connect_with_timeout() // set the socket to non-blocking mode if ( fcntl(s, F_SETFL, O_NONBLOCK)< 0 ){ resolver_logf(8,"fcntl: error setting new socket to nonblocking"); return SOCKET_ERROR; } resolver_logf(9,"connecting to %d", s); // set our select_timeout, to sleep for 0.5 seconds struct timeval select_timeout; // start the connect int nRet; time_t end_time=current_time()+timeout; while(end_time>current_time()){ // we keep looping till our time is done, // or a positive connect or an unexpected error nRet= connect(s, addr, sizeof(SOCKADDR_IN)); resolver_logf(9,"errno=%d, nRet=%d", errno, nRet); if(nRet==0){ return 0; } if(nRet<0){ #ifndef EISCONN #define EISCONN 56 // socket is already connected #endif if(errno==EISCONN){ return 0; } if (errno != EINPROGRESS && errno != EALREADY) { socket_error("connect_with_timeout(): connect()"); return SOCKET_ERROR; } } // sleep for half a second select_timeout.tv_sec=0; select_timeout.tv_usec=500000; if(select(0, NULL, NULL, NULL, &select_timeout)<0){ socket_error("select()"); } } resolver_logf(5, "connect timeout."); return SOCKET_ERROR; #endif } /**************************************************************************/ // return true if successful bool resolve_ident(hnode *host, int LocalPort, int RemotePort) { SOCKET Socket; Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (Socket == INVALID_SOCKET) { resolve_ident_failed(host, "Error creating socket for ident!"); return false; } resolve_ident_failed(host, "Ident support disabled for now."); return false; // not supported for now } /**************************************************************************/ #ifdef IPV6_SUPPORT_ENABLED // if the host is an ipv4 inside an ipv6 address e.g. "::ffff:a.b.c.d" // then strip off the front prefix and resolve as an ipv4 address // return NULL if the address isn't of this type, // otherwise if it resolves sucessfully, return the dns name char *resolve_ipv4_inside_ipv6(char *szAddress) { static char result[MSL]; int r; // check if the host is an ipv4 inside an ipv6 address // e.g. "::ffff:a.b.c.d" if(strncmp("::ffff:", szAddress, strlen("::ffff:"))){ return NULL; } strcpy(result, &szAddress[strlen("::ffff:")]); sockaddr_type socket_address; size_t address_length; bool valid_ip=resolve_text_ip_into_address(&socket_address, &address_length, result, NULL, NULL); if(!valid_ip){ return NULL; } const struct sockaddr* sa=(const struct sockaddr*)&socket_address; if(sa->sa_family!=AF_INET){ return NULL; } // we have what appears to be a valid ipv4 address, // look it up in the dns to see if we get a result char sz_host[NI_MAXHOST], sz_serv[NI_MAXSERV]; int hostlen = NI_MAXHOST, servlen = NI_MAXSERV; size_t salen=address_length; r= getnameinfo( sa, salen, sz_host, hostlen, sz_serv, servlen, NI_NUMERICSERV); if(r!=0){ // have 2 attempts at resolving address if first attempt failed sleep_seconds(5); r= getnameinfo( sa, salen, sz_host, hostlen, sz_serv, servlen, NI_NUMERICSERV); } if(r!=0){ return NULL; } // success, return the result prefixed to make it clear how it was resolved sprintf(result, "::ffff:%s", sz_host); return result; } #endif /**************************************************************************/ // attempt to do a dns lookup on a host // the host structure already has its socket_address, and address_length fields filled in // this process will fill in the host->hostname field with either the resolved // name or a copy of the ip which is already in host->szAddr void resolve_host(hnode *host) { #ifdef IPV6_SUPPORT_ENABLED const struct sockaddr* sa=(const struct sockaddr*)&host->socket_address; char sz_host[NI_MAXHOST], sz_serv[NI_MAXSERV]; int hostlen = NI_MAXHOST, servlen = NI_MAXSERV, result; size_t salen=host->address_length; char *embedded_address; result = getnameinfo( sa, salen, sz_host, hostlen, sz_serv, servlen, NI_NUMERICSERV); if(result!=0){ // have 2 attempts at resolving address if first attempt failed sleep_seconds(5); result = getnameinfo( sa, salen, sz_host, hostlen, sz_serv, servlen, NI_NUMERICSERV); } if(result==0){ // success, strdup the result - replacing the old result if necessary if(host->hostname){ free(host->hostname); } host->hostname=strdup(sz_host); host->reresolve_after=current_time()+ (24*60*60); // cache for up to 1 day // if the hostname is actually the same as the ip address, // check if it is an ipv4 address embeeded in an ipv6 address if(!strcmp(host->szAddr,host->hostname)){ embedded_address=resolve_ipv4_inside_ipv6(host->szAddr); if(embedded_address){ if(host->hostname){ free(host->hostname); } host->hostname=strdup(embedded_address); } } }else{ // failed to resolve... // check if it is an embeeded address char *embedded_address=resolve_ipv4_inside_ipv6(host->szAddr); if(embedded_address){ if(host->hostname){ free(host->hostname); } host->hostname=strdup(embedded_address); host->reresolve_after=current_time()+ (24*60*60); // cache for up to 1 day }else{ // put in ip address if we dont already have one if(!host->hostname){ host->hostname=strdup(host->szAddr); } host->reresolve_after=current_time()+ (30*60); // cache for up to 30 minutes } } #else struct hostent *hostent; hostent= gethostbyaddr( (const char *) &host->socket_address.sin_addr.s_addr, sizeof(host->socket_address.sin_addr.s_addr), host->socket_address.sin_family); if ( !hostent){ // have 2 attempts at resolving address if first attempt failed sleep_seconds(5); hostent= gethostbyaddr( (const char *) &host->socket_address.sin_addr.s_addr, sizeof(host->socket_address.sin_addr.s_addr), host->socket_address.sin_family); } if (hostent){ // success, strdup the result - replacing the old result if necessary if(host->hostname){ free(host->hostname); } host->hostname=strdup(hostent->h_name); host->reresolve_after=current_time()+ (24*60*60); // 1 day }else{ // failed to resolve... put in ip address if we dont already have one if(!host->hostname){ host->hostname=strdup(host->szAddr); } host->reresolve_after=current_time()+ (30*60); // 30minutes } #endif return; } /**************************************************************************/ hnode *lookup_host(sockaddr_type *socket_address, size_t address_length) { iHostLookups++; hnode *result=find_in_cache(socket_address, address_length); if(!result){ // details not found in the cache, allocate a new host node // in which the constructor sets its reresolve_after value to 0 // which in turn will trigger a resolve_host() call below result=new hnode(socket_address, address_length); // insert at the top of the correct hash table bucket unsigned int key=get_hash_key(socket_address, address_length); result->next=hosts_table[key]; hosts_table[key]=result; } // check if the results are old or non existant, if so resolve now if(result->reresolve_after<current_time()){ resolve_host(result); } return result; } /**************************************************************************/ // return true if text ip was resolved successfully // fill in the socket_address details based on the szAddress bool resolve_text_ip_into_address(sockaddr_type *socket_address, size_t *address_length, char*pszAddress, int *error_value, char *error_message) { if(IS_NULLSTR(pszAddress)){ resolver_logf(2, "Empty query: '%s' is not a valid ipv4 or ipv6 address.",pszAddress); return false; // can't have an empty string } #ifdef IPV6_SUPPORT_ENABLED struct addrinfo hints, *res; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags=AI_NUMERICHOST; // we don't want any hostnames here hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; res=NULL; int r = getaddrinfo(pszAddress, // ip address in text form "0", // we don't care about the service &hints, &res); if (r) { char errmsgbuf[2048]; if(error_value){ *error_value=2; } #ifdef WIN32 if(r==WSAHOST_NOT_FOUND){ if(error_value){ *error_value=2; } sprintf(errmsgbuf,"resolve_text_ip_into_address error %d (%s)- couldn't convert '%s' to a valid ip address.", r, get_socket_error_text(r), pszAddress); } /* else if(r==WSAEAFNOSUPPORT && node->protocol==PROTOCOL_IPV6){ resolver_logf("\n" "getaddrinfo error %d - couldn't convert '%s' to a valid ip address\n" " - getaddrinfo reported this system has no support for the AF_INET6 address family.\n" " This error message is normal if this system doesn't support ipv6, as it\n" " was an ipv6 address getaddrinfo() was asked to convert.", r, node->psz_bind_address); }else */ else{ sprintf(errmsgbuf,"resolve_text_ip_into_address(): getaddrinfo() error %d (%s)- couldn't convert '%s' to a valid ip address.", r, get_socket_error_text(r), pszAddress); } #else { sprintf(errmsgbuf,"resolve_text_ip_into_address(): getaddrinfo() error %d (%s)- couldn't convert '%s' to a valid ip address.", r, gai_strerror(r), pszAddress); } #endif if(error_message){ strcpy(error_message, errmsgbuf); } if(res){ freeaddrinfo(res); } return false; } // we had success memcpy(socket_address, res->ai_addr, res->ai_addrlen); *address_length=res->ai_addrlen; freeaddrinfo(res); #else // !IPV6_SUPPORT_ENABLED memset(socket_address, 0, sizeof(sockaddr_type)); socket_address->sin_addr.s_addr = inet_addr( pszAddress); socket_address->sin_family = AF_INET; *address_length=sizeof(sockaddr_type); if(socket_address->sin_addr.s_addr==INADDR_NONE){ // we had an invalid address supplied // technically INADDR_NONE can be returned by the valid ip 255.255.255.255 // but 255.255.255.255 isn't useful as a bind address, so we assume the // input node->psz_bind_address was invalid. if(error_value){ *error_value=2; } if(error_message){ sprintf(error_message,"Invalid query: '%s' is not a valid ipv4 address.",pszAddress); } return false; } #endif // ifdef IPV6_SUPPORT_ENABLED return true; } /**************************************************************************/ char *get_enabled_commands() { static char result[MSL]; char buf[1024]; result[0]='\0'; strcpy(result, "\n"); for(int i=0; !IS_NULLSTR(command_table[i].name);){ sprintf(buf, " %s %s\n", command_table[i].name, command_table[i].syntax); strcat(result, buf); i++; } return result; } /**************************************************************************/ int main(int argc, char *argv[]) { char buf[16384]; char *p; resolver_logf(0, "\n" "============================================================================\n" " Resolver v%s - Caching Host Name and Ident resolving tool\n" #ifdef IPV6_SUPPORT_ENABLED " Purpose: Allow dns hostname resolving of ipv4 and ipv6 \n" " addresses and ident lookups for a mud process.\n" #else " Purpose: Allow dns hostname resolving of ipv4 addresses\n" " and ident lookups for a mud process.\n" #endif " http://www.dawnoftime.org/\n" " (c) Michael Garratt 1997-2004\n" "\n" " Syntax: aaa.bbb.ccc.ddd (dns query only)\n" " aaa.bbb.ccc.ddd,localport,remoteport (dns and ident)\n" " resolve <username> <dns name> (lookup a dns name)\n" " version (what version are we running)\n" " stats (show some stats about resolver performance)\n" "\n" " Enabled commands: %s\n" "============================================================================", RESOLVER_VERSION, get_enabled_commands()); resolver_logf(1, "Verbose level: %d", VERBOSE_LEVEL); resolver_logf(1, "Ident connect timeout: %d second%s", IDENT_CONNECT_TIMEOUT, IDENT_CONNECT_TIMEOUT==1?"":"s"); if(init_network()==false){ resolver_logf(0, "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" "! %-10s COULD NOT INITIALISE WINSOCK SUPPORT !\n" "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", argv[0]); exit(1); }; // initialise the hash table to empty for(int i=0; i<HHK; i++){ hosts_table[i]=NULL; } // main IO loop while ( fgets( buf, 16383, stdin ) ) { // resolver_logf(0, "STDIN(((%s)))", buf); p = strchr( buf, '\n' ); // all commands are terminated with \n if ( IS_NULLSTR(p)){ resolver_logf(5, "^^^^^invalid input - '%s'^^^^^", buf); continue; } *p = '\0'; // chop off the \n if(IS_NULLSTR(buf)){ continue; } interpret_line(buf); } return 0; }; /**************************************************************************/ hnode *find_in_cache(sockaddr_type *socket_address, size_t address_length) { hnode *host; unsigned int key=get_hash_key(socket_address, address_length); for (host= hosts_table[key]; host; host= host->next) { if ( !memcmp(&host->socket_address, socket_address, address_length) ){ iHostLookupsFoundInCache++; return host; } } return NULL; } /**************************************************************************/ /* * resolver - caching dns and ident lookup, by Michael Garratt (c)1997-2001 * * USAGE: * accepts input from stdin in the form * aaa.bbb.ccc.ddd\n (dns query) * or * aaa.bbb.ccc.ddd,localport,remoteport\n (dns and ident query) * results are sent to stdout * <localport> = port mud is running on locally * * maximum input is: * 15(ip) + 1(,) + 5(biggest short) + 1(,) + 5(biggest short) + 1(\n) = 28 * * DNS result: * aaa.bbb.ccc.ddd dns.host.name\n * IDENT result: * aaa.bbb.ccc.ddd ,localport,remoteport ident_result\n * * an ident response always starts with a , * (because , is not valid in a domain name it is easy to detect idents) * * NOTES: * - Resolver does not bother to ditch old cached results, just relooks up * after they have aged a certain amount (1 day by default) * - Resolver caches negative dns hits - if it cant resolve an ip address * it wont attempt to reresolve the ip for 30 minutes. * - Resolver caches failed ident attempts, if it cant contact an ident * server, it wont retry an ident for 1minute*number of failures for the * specific ip. * - The resolver will spend up to IDENT_CONNECT_TIMEOUT waiting for a * connect to a ident server to establish. * - The resolver gives ident servers 20 seconds to respond once connected. * - The port ident lookups are directed to is take from /etc/services */ /**************************************************************************/ // look at top of file for instructions on manual compilation /**************************************************************************/