/****************************************************************************
* Copyright (C) 1998 WIDE Project. All rights reserved.
* Copyright (C) 1999,2000,2001,2002 University of Tromso. All rights reserved.
* Copyright (C) 2002 Invenia Innovation AS. All rights reserved.
*
* Author: Feike W. Dillema, feico@pasta.cs.uit.no.
* based on newbie code by Yusuke DOI, Keio Univ. Murai Lab.
****************************************************************************/
/*
* <$Id: ne_io.c,v 3.65 2005/03/31 09:28:29 dillema Exp $>
*/
#include "totd.h"
static G_List *NI_head = NULL; /* lists are to be allocated */
static G_List *NI_wildcard = NULL; /* linked element of wildcard socket */
static G_List *NI_wildcard6 = NULL; /* wildcard 6 */
static int net_ifc_cmp (struct ifconf *, struct ifconf *);
static Nia *nia_alloc (struct sockaddr *, int, int);
static void nia_list_destroy (G_List *);
static int nia_is_in_totd_iflist(char *);
static int net_get_ifaddrs (G_List *, int);
static int nia_set_wildsock (G_List *, int);
/*
* nia_find_by_sock : find Nia by socket id
* nia_fds_set : set all bits in the fd_set for all socks
* nia_fds_isset : check Nia available for fd_set
*
* net_init_socketlist
* net_reinit_socketlist
* net_bind_socketlist
* net_delete_socketlist
*
* net_stream_socket: open stream socket, bind it and listen
* net_mesg_socket: open dgram binded socket on a interface
*
* net_mesg_send : send message from specific interface
*
*/
static int nia_is_in_totd_iflist(char *ifname) {
char **ifp;
ifp = T.iflist;
while (*ifp) {
if (!strcmp("any", *ifp))
return 1;
if (!strcmp("all", *ifp))
return 1;
if (!strcmp(ifname, *ifp))
return 1;
ifp++;
}
return 0;
}
static int net_ifc_cmp (struct ifconf *ifc1, struct ifconf *ifc2) {
struct ifreq *ifr_p1;
struct ifreq *ifr_p2;
u_char *cp1;
u_char *cp2;
if (ifc1->ifc_len != ifc2->ifc_len) {
if (T.debug > 4)
syslog (LOG_DEBUG, "net_ifc_cmp(): lengths differ");
return 1;
}
cp1 = ifc1->ifc_buf;
cp2 = ifc2->ifc_buf;
while (*cp1 && *cp2) {
ifr_p1 = (struct ifreq *) cp1;
ifr_p2 = (struct ifreq *) cp2;
cp1 = cp1 + IFNAMSIZ + SOCKADDR_SIZEOF (ifr_p1->ifr_addr);
cp2 = cp2 + IFNAMSIZ + SOCKADDR_SIZEOF (ifr_p2->ifr_addr);
if (T.debug > 4) {
char astr[MAX_DNAME], bstr[MAX_DNAME];
sprint_inet(&ifr_p1->ifr_addr, astr);
sprint_inet(&ifr_p2->ifr_addr, bstr);
syslog (LOG_DEBUG, "net_ifc_cmp(): if1 %s, if2 %s, \
af1 %d, af2 %d, addr1 %s, addr2 %s", ifr_p1->ifr_name, ifr_p2->ifr_name, \
ifr_p1->ifr_addr.sa_family, ifr_p1->ifr_addr.sa_family, astr, bstr);
}
#ifdef USE_INET4
if (ifr_p1->ifr_addr.sa_family != AF_INET &&
ifr_p2->ifr_addr.sa_family != AF_INET)
#endif
#ifdef USE_INET6
if (ifr_p1->ifr_addr.sa_family != AF_INET6 &&
ifr_p2->ifr_addr.sa_family != AF_INET6)
#endif
continue; /* skip */
/* comparison only AF_INET and AF_INET6 */
if (ifr_p1->ifr_addr.sa_family != ifr_p2->ifr_addr.sa_family)
return 1;
if (strcmp (ifr_p1->ifr_name, ifr_p2->ifr_name))
return 1;
if (memcmp (&(ifr_p1->ifr_addr), &(ifr_p2->ifr_addr),
SOCKADDR_SIZEOF (ifr_p1->ifr_addr)))
return 1;
}
return 0; /* equal! */
}
static Nia *nia_alloc (struct sockaddr *sa_p, int usock, int tsock) {
Nia *ni;
ni = (Nia *) malloc (sizeof (Nia));
if (!ni)
return NULL;
if (sa_p) {
ni->sa_p =
(struct sockaddr *) malloc (SOCKADDR_SIZEOF (*sa_p));
if (!ni->sa_p) {
free (ni);
return NULL;
}
memcpy (ni->sa_p, sa_p, SOCKADDR_SIZEOF (*sa_p));
} else
ni->sa_p = NULL;
ni->udp_sock = usock;
ni->tcp_sock = tsock;
return ni;
}
static void nia_list_destroy (G_List *list_head) {
list_destroy(list_head, nia_free_closev);
return;
}
void nia_free_closev (void *ni) {
nia_free((Nia *)ni, 1);
}
void nia_free (Nia *ni, int close_flag) {
const char *fn = "nia_free()";
if (ni->sa_p)
free (ni->sa_p);
if (ni->udp_sock >= 0 && close_flag) {
if (T.debug > 4)
syslog (LOG_DEBUG, "%s: socket close(fd = %d)",
fn, ni->udp_sock);
close (ni->udp_sock);
}
if (ni->tcp_sock >= 0 && close_flag) {
if (T.debug > 4)
syslog (LOG_DEBUG, "%s: socket close(fd = %d)",
fn, ni->tcp_sock);
close (ni->tcp_sock);
}
free (ni);
}
Nia *nia_copy (Nia *ni) {
if (!ni)
return NULL;
return nia_alloc (ni->sa_p, ni->udp_sock, ni->tcp_sock);
}
Nia *nia_find_by_sock (int sock_id) {
G_List *gl;
Nia *ni;
for (gl = NI_head->next; gl->list_data; gl = gl->next) {
ni = (Nia *) gl->list_data;
if (!ni->sa_p)
continue;
if (ni->udp_sock == sock_id)
return ni;
if (ni->tcp_sock == sock_id)
return ni;
}
return NULL;
}
void nia_fds_set (fd_set *fds, int *max_fd) {
const char *fn = "nia_fds_set()";
G_List *gl;
Nia *ni;
if (!NI_head)
return;
for (gl = NI_head->next; gl->list_data; gl = gl->next) {
ni = (Nia *) gl->list_data;
if (!ni->sa_p)
continue;
if (ni->udp_sock >= 0) {
if (T.debug > 4)
syslog (LOG_DEBUG, "%s: FD_SET(%d)", fn,
ni->udp_sock);
FD_SET (ni->udp_sock, fds);
if (max_fd)
*max_fd = MAXNUM (*max_fd, ni->udp_sock);
}
if (ni->tcp_sock >= 0) {
if (T.debug > 4)
syslog (LOG_DEBUG, "%s: FD_SET(%d)", fn,
ni->tcp_sock);
FD_SET (ni->tcp_sock, fds);
if (max_fd)
*max_fd = MAXNUM (*max_fd, ni->tcp_sock);
}
}
return;
}
int nia_fds_isset (fd_set *fds, int *sock) {
const char *fn = "nia_fds_isset()";
G_List *gl;
Nia *ni;
*sock = -1;
if (!NI_head)
return -1;
for (gl = NI_head->next; gl->list_data; gl = gl->next) {
ni = (Nia *) gl->list_data;
if (!ni->sa_p)
continue;
if (ni->udp_sock >= 0 && FD_ISSET (ni->udp_sock, fds)) {
if (T.debug > 4)
syslog (LOG_DEBUG, "%s: %d FD_ISSET",
fn, ni->udp_sock);
FD_CLR (ni->udp_sock, fds);
*sock = ni->udp_sock;
return 0; /* UDP */
}
if (ni->tcp_sock >= 0 && FD_ISSET (ni->tcp_sock, fds)) {
if (T.debug > 4)
syslog (LOG_DEBUG, "%s: %d FD_ISSET",
fn, ni->tcp_sock);
FD_CLR (ni->tcp_sock, fds);
*sock = ni->tcp_sock;
return 1; /* TCP */
}
}
return -1;
}
static int nia_set_wildsock (G_List *list_head, int port) {
Nia *ni;
#ifdef USE_INET4
if (!T.use_mapped) {
struct sockaddr_in sin;
memset (&sin, 0, sizeof (sin));
#ifdef HAVE_SA_LEN_FIELD
sin.sin_len = sizeof (struct sockaddr_in);
#endif
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = INADDR_ANY;
ni = nia_alloc((struct sockaddr *)&sin, -1, -1);
if (!ni)
return -1;
if (list_add (list_head, ni) < 0) {
nia_free (ni, 0);
return -1;
}
NI_wildcard = list_head->next;
}
#endif
#ifdef USE_INET6
if (1) {
struct sockaddr_in6 sin6;
memset (&sin6, 0, sizeof (sin6));
#ifdef HAVE_SA_LEN_FIELD
sin6.sin6_len = sizeof (struct sockaddr_in6);
#endif
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(port);
sin6.sin6_addr = in6addr_any;
ni = nia_alloc((struct sockaddr *)&sin6, -1, -1);
if (!ni)
return -1;
if (list_add (list_head, ni) < 0) {
nia_free (ni, 0);
return -1;
}
NI_wildcard6 = list_head->next;
}
#endif
/* SUCCESS */
return 0;
}
int net_init_socketlist (int port) {
NI_head = list_init();
if (!NI_head)
return -1;
if (T.wildcard)
nia_set_wildsock (NI_head, T.port);
else {
/* wildcard for sending requests only */
nia_set_wildsock (NI_head, 0);
if (net_get_ifaddrs (NI_head, port) != 1)
return -1;
}
return 0;
}
int net_reinit_socketlist (int port, int force_update) {
G_List *newlist;
newlist = list_init();
if (!newlist)
return -1;
if (T.wildcard) {
if (force_update) {
nia_list_destroy (NI_head);
nia_set_wildsock (newlist, T.port);
NI_head = newlist;
net_bind_socketlist();
return 1;
} else {
nia_list_destroy (newlist);
return 0;
}
}
switch (net_get_ifaddrs (newlist, port)) {
case 0: /* no change */
if (!force_update) {
nia_list_destroy (newlist);
return 0;
}
/* else fall through to next case */
case 1: /* changed list */
nia_list_destroy (NI_head);
/* wildcard for sending requests only */
nia_set_wildsock (newlist, 0);
NI_head = newlist;
net_bind_socketlist();
return 1;
default: /* error */
nia_list_destroy (newlist);
return -1;
}
}
/* returns number of sockets opened successfully */
int net_bind_socketlist (void) {
G_List *gl;
Nia *ni;
int s;
if (!NI_head)
return -1;
s = 0; /* no socket opened yet */
for (gl = NI_head->next; gl->list_data; gl = gl->next) {
ni = (Nia *) gl->list_data;
if (!ni->sa_p)
continue;
if (net_mesg_socket (ni) >= 0)
s++;
if (net_stream_socket (ni) >= 0)
s++;
}
return s;
}
int net_delete_socketlist (void) {
/* throw out old list if any */
nia_list_destroy (NI_head);
NI_head = NI_wildcard = NI_wildcard6 = NULL;
return 0;
}
static int net_get_ifaddrs (G_List *list_head, int port) {
const char *fn = "net_get_ifaddrs()";
static struct ifconf ifc_old;
struct ifconf ifc;
int dummy_sock = -1, status;
u_char buf[8192];
u_char *cp;
memset (buf, 0, sizeof (buf));
ifc.ifc_len = sizeof (buf);
ifc.ifc_buf = buf;
#ifdef USE_INET6
if (dummy_sock < 0)
dummy_sock = socket (AF_INET6, SOCK_STREAM, 0);
#endif
#ifdef USE_INET4
if (dummy_sock < 0)
dummy_sock = socket (AF_INET, SOCK_STREAM, 0);
#endif
if (dummy_sock < 0)
return -1;
/*
* check whether list of interface addresses has changed
*/
if (ioctl (dummy_sock, SIOCGIFCONF, (char *) &ifc) < 0) {
close(dummy_sock);
syslog (LOG_ERR, "%s: get iflist error: %m", fn);
return -1;
}
close(dummy_sock);
status = 1;
if (ifc_old.ifc_buf) {
status = net_ifc_cmp (&ifc_old, &ifc);
syslog (LOG_DEBUG, "%s: checked interface data %d", fn, status);
}
/*
* Interface address list changed or we got called for
* for the first time
*/
cp = buf;
while (*cp) {
int valid_address = 0;
char astr[MAX_DNAME];
struct ifreq *ifr_p;
ifr_p = (struct ifreq *) cp;
sprint_inet(&ifr_p->ifr_addr, astr);
#ifdef USE_INET6
if (T.ip6 && ifr_p->ifr_addr.sa_family == AF_INET6)
if (nia_is_in_totd_iflist(ifr_p->ifr_name))
valid_address = 1;
#endif
#ifdef USE_INET4
if (T.ip4 && ifr_p->ifr_addr.sa_family == AF_INET)
if (T.ip4 && nia_is_in_totd_iflist(ifr_p->ifr_name))
valid_address = 1;
#endif
if (valid_address) {
Nia *ni;
ni = nia_alloc (&ifr_p->ifr_addr, -1, -1);
if (!ni)
return -1;
#ifdef USE_INET6
if (T.ip6 && ni->sa_p->sa_family == AF_INET6) {
struct sockaddr_in6 *sa_p;
sa_p = (struct sockaddr_in6 *) ni->sa_p;
sa_p->sin6_port = htons (port);
}
#endif
#ifdef USE_INET4
if (T.ip4 && ni->sa_p->sa_family == AF_INET) {
struct sockaddr_in *sa_p;
sa_p = (struct sockaddr_in *) ni->sa_p;
sa_p->sin_port = htons (port);
}
#endif
if (*astr && T.debug > 3)
syslog (LOG_DEBUG, "Found address %s on if %s",
astr, ifr_p->ifr_name);
/* ok! add to the list */
if (list_add (list_head, ni) < 0)
return -1;
} else if (*astr && T.debug > 3)
syslog (LOG_DEBUG, "Ignoring address %s on if %s",
astr, ifr_p->ifr_name);
/* point to next address entry in list */
cp = cp + IFNAMSIZ + SOCKADDR_SIZEOF (ifr_p->ifr_addr);
}
/*
* preserve old buffer
*/
if (ifc_old.ifc_buf)
free (ifc_old.ifc_buf);
ifc_old.ifc_buf = malloc (ifc.ifc_len);
if (ifc_old.ifc_buf) {
memcpy (ifc_old.ifc_buf, buf, ifc.ifc_len);
ifc_old.ifc_len = ifc.ifc_len;
}
return status;
}
int net_mesg_send (Nia *ni, u_char *mesg, int mesg_len,
struct sockaddr *sa_p) {
const char *fn = "net_mesg_send()";
if (!ni) {
#ifdef USE_INET4
if (sa_p->sa_family == AF_INET)
ni = (Nia *) NI_wildcard->list_data;
#endif
#ifdef USE_INET6
if (sa_p->sa_family == AF_INET6)
ni = (Nia *) NI_wildcard6->list_data;
#endif
}
if (!ni || ni->udp_sock < 0) {
syslog (LOG_WARNING, "%s: no socket to send message over", fn);
return -1;
}
#ifdef USE_INET4
if (ni->sa_p->sa_family == AF_INET && sa_p->sa_family == AF_INET)
return sendto (ni->udp_sock, mesg, mesg_len, 0, sa_p,
SOCKADDR_SIZEOF (*sa_p));
#endif
#ifdef USE_INET6
if (ni->sa_p->sa_family == AF_INET6 && sa_p->sa_family == AF_INET6)
return sendto (ni->udp_sock, mesg, mesg_len, 0, sa_p,
SOCKADDR_SIZEOF (*sa_p));
#endif
return -1;
}
int net_mesg_socket (Nia *ni) {
const char *fn = "net_mesg_socket()";
char astr[MAX_DNAME];
const int on = 1;
int sock;
ni->udp_sock = -1;
sock = socket (ni->sa_p->sa_family, SOCK_DGRAM, 0);
if (sock < 0) {
syslog (LOG_ERR, "%s: socket open failed: %m", fn);
return -1;
}
if (T.debug > 4)
syslog (LOG_DEBUG, "%s: socket open(fd = %d)", fn, sock);
if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
(char *) &on, sizeof (on)))
syslog (LOG_WARNING, "setsockopt: %m");
if (bind (sock, ni->sa_p, SOCKADDR_SIZEOF (*ni->sa_p)) < 0) {
syslog (LOG_ERR, "Can not bind datagram socket: %m");
close (sock);
return -1;
}
/* if no port set, then it is for outgoing messages only */
if (((struct sockaddr_in *)ni->sa_p)->sin_port) {
sprint_inet(ni->sa_p, astr);
syslog (LOG_NOTICE, "Listening on %s for UDP", astr);
}
#ifdef USE_INET6
#ifdef IPV6_USE_MIN_MTU
if (ni->sa_p->sa_family == AF_INET6) {
const int on = 1;
/* ignore error */
(void)setsockopt(sock, IPPROTO_IPV6,
IPV6_USE_MIN_MTU, &on, sizeof(on));
}
#endif
#endif
ni->udp_sock = sock;
return sock;
}
int net_stream_socket (Nia *ni) {
const char *fn = "net_stream_socket()";
char astr[MAX_DNAME];
const int on = 1;
int sock;
ni->tcp_sock = -1;
/* if no port set, do nothing for TCP */
if (!((struct sockaddr_in *)ni->sa_p)->sin_port)
return -1;
sock = socket (ni->sa_p->sa_family, SOCK_STREAM, 0);
if (sock < 0) {
syslog (LOG_ERR, "%s: socket open failed: %m", fn);
return -1;
}
if (T.debug > 4)
syslog (LOG_DEBUG, "%s: socket open(fd = %d)", fn, sock);
if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR,
(char *) &on, sizeof (on)))
syslog (LOG_WARNING, "setsockopt: %m");
if (bind (sock, ni->sa_p, SOCKADDR_SIZEOF (*ni->sa_p)) != 0) {
syslog (LOG_ERR, "Can't bind TCP socket: %m");
close (sock);
return -1;
}
if (ioctl (sock, FIONBIO, (char *) &on) < 0) {
syslog (LOG_ERR, "Can't ioctl on service socket: %m");
return -1;
}
if (listen (sock, 5) != 0) {
syslog (LOG_ERR, "Listen failed: %m");
close (sock);
return -1;
}
sprint_inet(ni->sa_p, astr);
syslog (LOG_NOTICE, "Listening on %s for TCP", astr);
ni->tcp_sock = sock;
return sock;
}
syntax highlighted by Code2HTML, v. 0.9.1