/* ************************************************************* RWhois Software Copyright (c) 1994 Scott Williamson and Mark Kosters Copyright (c) 1996-2000 Network Solutions, Inc. See the file LICENSE for conditions of use and distribution. **************************************************************** */ #include "rwhois.h" #include "rwhois_internal.h" #include "strutils.h" #include "whoisquery.h"/* for the minimal whois support */ /* note that SIG_IGN doesn't work on non-SysV systems. */ static void set_sig_ignore(int signum) { signal(signum, SIG_IGN); } static char * connect_state_to_string(rw_connect_state_enum state) { char *msg; switch (state) { case RW_CONN_BADADDR: msg = "Cannot resolve host"; break; case RW_CONN_BADSOCK: msg = "Could not create socket"; break; case RW_CONN_BADCON: msg = "Could not connect to host"; break; case RW_CONN_BADMODE: msg = "Incorrect fcntl mode specified"; break; case RW_CONN_NOTRW: msg = "Socket not read/writable"; break; case RW_CONN_GOOD: msg = ""; break; default: msg = "Unknown contact error."; break; } return msg; } static rw_connect_state_enum contact(rw_server_struct *server) { struct servent *service = NULL; rw_connect_state_enum status = RW_CONN_GOOD; #ifdef HAVE_GETADDRINFO struct addrinfo hints, *res, *res2; char port[50]; int error; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (! server->port) { service = getservbyname(SERVICE, PROTOCOL); /* Connect to that port */ if ( service == NULL ) { server->port = htons(NORMPORT); } else { server->port = service->s_port; } } sprintf(port, "%d", server->port ); error = getaddrinfo(server->host, port, &hints, &res ); if (server->debug_level > 0) { printf("Connecting to %s:%d\n", server->host, server->port); } if (error) return RW_CONN_BADADDR; res2 = res; for ( ; res; res = res->ai_next ) { server->sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (server->sock < 0) { status = RW_CONN_BADSOCK; continue; } if (connect(server->sock, res->ai_addr, res->ai_addrlen ) < 0 ) { status = RW_CONN_BADCON; continue; } status = RW_CONN_GOOD; break; } freeaddrinfo(res2); #else /* HAVE_GETADDRINFO */ struct sockaddr_in sin; struct hostent *host = NULL; if (!(host = gethostbyname(server->host))) { unsigned long hostaddr; hostaddr = inet_addr(server->host); if (!(host = gethostbyaddr((char *) &hostaddr, 4, AF_INET))) { status = RW_CONN_BADADDR; } } else { /* Create a socket if ok continue on to contact server */ if ((server->sock = socket( PF_INET, SOCK_STREAM, 0)) < 0) { status = RW_CONN_BADSOCK; } else { /* Bind to that socket */ sin.sin_family = host->h_addrtype; /* lookup the port number for the rwhois server */ memcpy (&sin.sin_addr,host->h_addr, host->h_length); if (!server->port) { service = getservbyname(SERVICE, PROTOCOL); /* Connect to that port */ if ( service == NULL ) { sin.sin_port = htons(NORMPORT); } else { sin.sin_port = service->s_port; } } else { sin.sin_port = htons(server->port); } if (connect(server->sock, (struct sockaddr *) &sin, sizeof (sin)) < 0) { status = RW_CONN_BADCON; } } } #endif /* HAVE_GETADDRINFO */ return status; } static int fetch_intial_banner(rw_server_struct *server) { int fetch_ret; char buffer[MAXBUFFER]; /* read the banner line. RWhois insists that it is there, so if it isn't, this bad */ fetch_ret = rwhois_read(server, buffer, sizeof(buffer)); if (fetch_ret <= 0) { rwhois_close(server); return RW_ERROR; } if (!STRN_EQ(buffer, "%rwhois", 7)) { rwhois_close(server); return RW_ERROR; } server->version = get_version(buffer); get_capability(buffer, &(server->capability)); return RW_OK; } /* This function establishes a connection between the server and the client. */ int rwhois_connect(rw_server_struct *server, char *spec_version, char *imp_version, char *info_buffer, size_t info_buffer_len, rw_response_struct *response) { rw_connect_state_enum connect_status = RW_CONN_GOOD; rw_record_struct *record = NULL; char output_buffer[MAXBUFFER]; assert(server); assert(response); assert(STR_EXISTS(spec_version)); assert(STR_EXISTS(imp_version)); if (NOT_STR_EXISTS(server->host)) { rwhois_set_response(response, RW_ERROR, "Cannot connect to null host"); return RW_ERROR; } set_sig_ignore(SIGPIPE); if ((connect_status = contact(server)) != RW_CONN_GOOD) { rwhois_set_response(response, RW_ERROR, connect_state_to_string(connect_status)); return RW_ERROR; } /* read the banner; rwhois_fetch_record will fill the server record with information gleaned from the banner */ if (fetch_intial_banner(server) != RW_OK) { rwhois_set_response(response, RW_ERROR, "Could not parse initial RWhois banner."); return RW_ERROR; } /* if we are standard version 1.0 or 1.5, do the -rwhois response to the banner */ if ( (server->version == VER_1_0) || (server->version == VER_1_5) ) { sprintf(output_buffer, "-rwhois V-%s (NSI, Inc. V-%s)", spec_version, imp_version); /* send the -rwhois command */ rwhois_command(server, output_buffer, response); if (response->status < RW_OK) { return response->status; } /* read the banner reply (again) */ record = rwhois_fetch_record(server, response); /* if there was an info block... */ if (record && record->text && info_buffer) { strncpy(info_buffer, record->text, info_buffer_len); } /* free the record, if one was allocated */ destroy_rw_record_struct(record); /* if we got a good response.. */ if (response->status >= RW_OK) { rwhois_set_response(response, RW_OK, ""); return RW_OK; } /* otherwise, fall through */ } /* if we were an unknown version, or we got a bad response from the '-rwhois' directive */ rwhois_set_response(response, RW_ERROR, NULL); return RW_ERROR; } size_t rwhois_prim_write(rw_server_struct *server, char *str, size_t n) { size_t nleft; ssize_t nwritten; char *ptr; assert(server); assert(STR_EXISTS(str)); if (! rwhois_is_open(server)) { return RW_ERROR; } ptr = str; nleft = n; while (nleft > 0) { if ( (nwritten = write(server->sock, ptr, nleft)) <= 0) { if (errno == EINTR) { nwritten = 0; } else { /* we have encountered a fatal write error, so close the connection */ rwhois_close(server); return RW_ERROR; } } nleft -= nwritten; ptr += nwritten; } if (server->debug_level > 1) fputs(str, stdout); return n; } size_t rwhois_write(rw_server_struct *server, char *str, size_t n) { assert(server); if (server->debug_level > 1)fputs("C: ", stdout); if (rwhois_prim_write(server, str, n) < 0) { return RW_ERROR; } if (rwhois_prim_write(server, "\r\n", 2) < 0) { return RW_ERROR; } return n; } ssize_t rwhois_prim_read(rw_server_struct *server, char *buffer, size_t maxlen) { ssize_t n; ssize_t rc; char *ptr; char c; assert(server); assert(buffer); ptr = buffer; for (n = 1; n < maxlen; n++) { if ( (rc = read(server->sock, &c, 1)) == 1) { /* we successfully read the character */ *ptr++ = c; if (c == '\n') break; /* we hit EOL, so we are done */ } else if (rc == 0) { if (n == 1) return 0; /* EOF; no data read at all */ /* else */ break; /* we've hit the end of the stream, so we are done for now */ } else { if (errno == EINTR) continue; /* retry interrupts */ if (errno == EPIPE) continue; /* try to get the rest of the recv buf */ /* else */ rwhois_close(server); return RW_ERROR; } } /* null terminate the string */ *ptr = '\0'; if (server->debug_level > 1) { fputs("S: ", stdout); fputs(buffer, stdout); } return n; } ssize_t rwhois_read(rw_server_struct *server, char *buffer, size_t n) { ssize_t r = rwhois_prim_read(server, buffer, n); if (r < 0) { return r; } rtrim(buffer); return r; } int rwhois_close(rw_server_struct *server) { if (server != NULL) { close(server->sock); server->sock = 0; } return RW_OK; } int rwhois_is_open(rw_server_struct *server) { if (!server) return FALSE; /* FIXME: should probably have a postive check for socket openness */ if (server->sock != 0) return TRUE; return FALSE; } int destroy_rw_server_struct(rw_server_struct *server) { if (!server) return TRUE; if (server->host) free(server->host); free(server); return TRUE; } /* this is part of the whoisquery support. It's prototype is in whoisquery.h */ int rwhois_whoisconnect(rw_server_struct *server, rw_response_struct *response) { rw_connect_state_enum connect_status = RW_CONN_GOOD; assert(server); assert(response); if (NOT_STR_EXISTS(server->host)) { rwhois_set_response(response, RW_ERROR, "Cannot connect to null host"); return RW_ERROR; } if (server->port <= 0) { server->port = WHOIS_PORT; } set_sig_ignore(SIGPIPE); if ((connect_status = contact(server)) != RW_CONN_GOOD) { rwhois_set_response(response, RW_ERROR, connect_state_to_string(connect_status)); return RW_ERROR; } /* whois is so simple, that there isn't anything else to do */ rwhois_set_response(response, RW_OK, ""); return RW_OK; }