218 lines
5.0 KiB
C
218 lines
5.0 KiB
C
/*
|
|
* Socket interface API
|
|
*/
|
|
#include <netdb.h> /* getaddrinfo() */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
|
|
#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;
|
|
}
|