/* * Socket interface API */ #include /* getaddrinfo() */ #include #include #include #include #include #include #include "nvt_log.h" #include "nvt_socket.h" typedef struct { unsigned short type; // socket type unsigned short slen; // sockaddr length 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, const char *port) { struct addrinfo hint, *adrinf = NULL, *p; int ret = -1; 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; } // 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) { ssize_t host_len; char host[64]; const char *port; memset(sa, 0, sizeof(sa_t)); 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 (!(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; return 0; } int socket_create_client(const char *addr) { sa_t sa; int fd, err; if (_sa_addr_parse(&sa, addr) != 0) return -1; fd = socket(sa.gen.sa_family, sa.type, 0); if (fd < 0) { nvt_log(LOG_ERR, "%s: Could not find address: '%s'", __func__, addr); return fd; } err = connect(fd, &sa.gen, sa.slen); if (err < 0) { nvt_log(LOG_ERR, "Failed to connect to: %s: %m\n", addr); close(fd); return -1; } return fd; } int socket_create_server(const char *addr) { sa_t sa; int err, fd, opt; err = _sa_addr_parse(&sa, addr); if (err != 0) { nvt_log(LOG_ERR, "%s: Could not find address: '%s'", __func__, addr); return -1; } fd = socket(sa.gen.sa_family, sa.type, 0); if (fd < 0) { nvt_log(LOG_ERR, "%s: *** socket(%s): %m", __func__, addr); return -1; } if (sa.gen.sa_family == AF_INET || sa.gen.sa_family == AF_INET6) { /* Avoid TIME_WAIT state on socket close */ opt = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); } err = bind(fd, &sa.gen, sa.slen); if (err != 0) { nvt_log(LOG_ERR, "%s: *** bind(%s): %m", __func__, addr); close(fd); return -1; } if (sa.type == SOCK_STREAM) { err = listen(fd, 5); if (err != 0) { nvt_log(LOG_ERR, "%s: *** listen(%s): %m", __func__, addr); close(fd); return -1; } } return fd; } int socket_create_server_client(int sfd) { int cfd; socklen_t slen; slen = 0; cfd = accept(sfd, NULL, &slen); if (cfd < 0) { nvt_log(LOG_ERR, "%s: *** accept(): %m", __func__); return -1; } return cfd; }