diff --git a/lib/nvt_socket.c b/lib/nvt_socket.c index 2ab2e32..08b1b44 100644 --- a/lib/nvt_socket.c +++ b/lib/nvt_socket.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "nvt_log.h" #include "nvt_socket.h" @@ -18,42 +19,95 @@ typedef struct { union { struct sockaddr gen; struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; + struct sockaddr_un un; }; } sa_t; -static int _sa_host_lookup(sa_t * sa, const char *host) +static int _sa_host_lookup(sa_t * sa, const char *host, const char *port) { - struct hostent *server; + struct addrinfo hint, *adrinf = NULL, *p; + int ret = -1; - server = gethostbyname(host); - if (server == NULL) - { + memset(&hint, 0, sizeof(struct addrinfo)); + + if(getaddrinfo(host, port, &hint, &adrinf) != 0) { nvt_log(LOG_ERR, "Cannot resolve host name: %m\n"); return -1; } - sa->slen = sizeof(sa->ipv4); - - sa->ipv4.sin_family = AF_INET; - memcpy(&sa->ipv4.sin_addr.s_addr, server->h_addr, server->h_length); - - return 0; + // Priority is given to the use of IPv4 + // This code must be modified if the preffered protocol is changed or varied. + for(p = adrinf; p; p = p->ai_next) { + if(p->ai_family == AF_INET) { + sa->slen = sizeof(sa->ipv4); + memcpy(&sa->ipv4, p->ai_addr, sizeof(struct sockaddr_in)); + ret = 0; + goto exit_host_lookup; + } + } + for(p = adrinf; p; p = p->ai_next) { + if(p->ai_family == AF_INET6) { + sa->slen = sizeof(sa->ipv6); + memcpy(&sa->ipv6, p->ai_addr, sizeof(struct sockaddr_in6)); + ret = 0; + goto exit_host_lookup; + } + } +exit_host_lookup: + freeaddrinfo(adrinf); + return ret; } static int _sa_addr_parse(sa_t * sa, const char *addr) { - char host[64], port[64]; + ssize_t host_len; + char host[64]; + const char *port; memset(sa, 0, sizeof(sa_t)); - sscanf(addr, "%63[^:]:%63s", host, port); + if (strncmp(addr, "unix:", 5) == 0) { // case of unix domain sockets ... + addr += 5; // skip "unix:" prefix + if(strlen(addr) > sizeof(sa->un.sun_path) - 1) { + nvt_log(LOG_ERR, "UNIX domain socket path is too long.\n"); + return -1; + } + sa->slen = sizeof(sa->un); + sa->un.sun_family = AF_UNIX; + strcpy(sa->un.sun_path, addr); + sa->type = SOCK_STREAM; + return 0; + } - if (_sa_host_lookup(sa, host) != 0) + if(!(port = strrchr(addr, ':'))) { + nvt_log(LOG_ERR, "bad server address format.\n"); + return -1; + } + host_len = (port++) - addr; + // *port indicates the last ':', its next is 1st char of the port number + if(host_len >= 2 && addr[0] == '[' && addr[host_len-1] == ']') { + // IPv6 addresses are enclosed in pair of square brackets. + addr += 1; + host_len -= 2; + } else { + if(strchr(addr, ':') != port-1) { + nvt_log(LOG_ERR, "addresses containing colons must be enclosed in square brackets.\n"); + return -1; + } + } + if((ssize_t)sizeof(host) <= host_len) { + nvt_log(LOG_ERR, "host name is too long.\n"); + return -1; + } + strncpy(host, addr, host_len); + host[host_len] = '\0'; // strncpy doesn't add null char + + if (_sa_host_lookup(sa, host, port) != 0) return -1; sa->type = SOCK_STREAM; - sa->ipv4.sin_port = htons(atoi(port)); return 0; } @@ -103,7 +157,7 @@ int socket_create_server(const char *addr) return -1; } - if (sa.gen.sa_family == AF_INET) + if (sa.gen.sa_family == AF_INET || sa.gen.sa_family == AF_INET6) { /* Avoid TIME_WAIT state on socket close */ opt = 1; diff --git a/src/ttynvt.c b/src/ttynvt.c index c164a22..919f24c 100644 --- a/src/ttynvt.c +++ b/src/ttynvt.c @@ -1172,7 +1172,9 @@ static void print_help(void) "\t-q\t\tBe quiet (suppress informational messages)\n" "\t-r\t\tDisable telnet processing\n" "\t-s delay \t\tDelay at close in ms (use u suffix for us)\n" - "\t-S server, --server=host:port\n"); + "\t-S server, --server=host:port\n" + "\t\t\t[ipv6addr]:port (connect via IPv6)\n" + "\t\t\tunix:/path/to.socket (connect via UNIX domain socket)\n"); } static int