/* -*- Mode:C; c-basic-offset:4; tab-width:8; indent-tabs-mode:t -*- */
/* vim:set sts=4 ts=8: */
/*
* Copyright (c) 2001
* YOID Project.
* University of Southern California/Information Sciences Institute.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ident "$XORP: xorp/libcomm/comm_sock.c,v 1.34 2006/10/12 01:24:45 pavlin Exp $"
/*
* COMM socket library lower `sock' level implementation.
*/
#include "comm_module.h"
#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_WS2TCPIP_H
#include <ws2tcpip.h>
#endif
#include "comm_api.h"
#include "comm_private.h"
/* XXX: Single threaded socket errno, used to record last error code. */
int _comm_serrno;
#if defined(HOST_OS_WINDOWS) && defined(HAVE_IPV6)
/*
* Windows declares these in <ws2tcpip.h> as externs, but does not
* supply symbols for them in the -lws2_32 import library or DLL.
*/
const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } };
const struct in6_addr in6addr_loopback = { { IN6ADDR_LOOPBACK_INIT } };
#endif
/**
* comm_sock_open:
* @domain: The domain of the socket (e.g., %AF_INET, %AF_INET6).
* @type: The type of the socket (e.g., %SOCK_STREAM, %SOCK_DGRAM).
* @protocol: The particular protocol to be used with the socket.
* @is_blocking: If true, then the socket will be blocking, otherwise
* non-blocking.
*
* Open a socket of domain = @domain, type = @type, and protocol = @protocol.
* The sending and receiving buffer size are set, and the socket
* itself is set with %TCP_NODELAY (if a TCP socket).
*
* Return value: The open socket on success, otherwise %XORP_BAD_SOCKET.
**/
xsock_t
comm_sock_open(int domain, int type, int protocol, int is_blocking)
{
xsock_t sock;
/* Create the kernel socket */
sock = socket(domain, type, protocol);
if (sock == XORP_BAD_SOCKET) {
_comm_set_serrno();
XLOG_ERROR("Error opening socket (domain = %d, type = %d, "
"protocol = %d): %s",
domain, type, protocol,
comm_get_error_str(comm_get_last_error()));
return (XORP_BAD_SOCKET);
}
/* Set the receiving and sending socket buffer size in the kernel */
if (comm_sock_set_rcvbuf(sock, SO_RCV_BUF_SIZE_MAX, SO_RCV_BUF_SIZE_MIN)
< SO_RCV_BUF_SIZE_MIN) {
_comm_set_serrno();
comm_sock_close(sock);
return (XORP_BAD_SOCKET);
}
if (comm_sock_set_sndbuf(sock, SO_SND_BUF_SIZE_MAX, SO_SND_BUF_SIZE_MIN)
< SO_SND_BUF_SIZE_MIN) {
_comm_set_serrno();
comm_sock_close(sock);
return (XORP_BAD_SOCKET);
}
/* Enable TCP_NODELAY */
if (type == SOCK_STREAM && comm_set_nodelay(sock, 1) != XORP_OK) {
_comm_set_serrno();
comm_sock_close(sock);
return (XORP_BAD_SOCKET);
}
/* Set blocking mode */
if (comm_sock_set_blocking(sock, is_blocking) != XORP_OK) {
_comm_set_serrno();
comm_sock_close(sock);
return (XORP_BAD_SOCKET);
}
return (sock);
}
/**
* comm_sock_pair:
*
* Create a pair of connected sockets. The sockets will be created in
* the blocking state by default, and with no additional socket options set.
*
* On Windows platforms, a domain of AF_UNIX, AF_INET, or AF_INET6 must
* be specified. For the AF_UNIX case, the sockets created will actually
* be in the AF_INET domain. The protocol field is ignored.
*
* On UNIX, this function simply wraps the socketpair() system call.
*
* XXX: There may be UNIX platforms lacking socketpair() where we
* have to emulate it.
*
* @param domain the domain of the socket (e.g., AF_INET, AF_INET6).
* @param type the type of the socket (e.g., SOCK_STREAM, SOCK_DGRAM).
* @param protocol the particular protocol to be used with the socket.
* @param sv pointer to an array of two xsock_t handles to receive the
* allocated socket pair.
*
* @return XORP_OK if the socket pair was created, otherwise if any error
* is encountered, XORP_ERROR.
**/
int
comm_sock_pair(int domain, int type, int protocol, xsock_t sv[2])
{
#ifndef HOST_OS_WINDOWS
if (socketpair(domain, type, protocol, sv) == -1) {
_comm_set_serrno();
return (XORP_ERROR);
}
return (XORP_OK);
#else /* HOST_OS_WINDOWS */
struct sockaddr_storage ss;
struct sockaddr_in *psin;
socklen_t sslen;
SOCKET st[3];
u_long optval;
int numtries, error, intdomain;
unsigned short port;
static const int CSP_LOWPORT = 40000;
static const int CSP_HIGHPORT = 65536;
#ifdef HAVE_IPV6
struct sockaddr_in6 *psin6;
#endif
UNUSED(protocol);
if (domain != AF_UNIX && domain != AF_INET
#ifdef HAVE_IPV6
&& domain != AF_INET6
#endif
) {
_comm_serrno = WSAEAFNOSUPPORT;
return (XORP_ERROR);
}
intdomain = domain;
if (intdomain == AF_UNIX)
intdomain = AF_INET;
st[0] = st[1] = st[2] = INVALID_SOCKET;
st[2] = socket(intdomain, type, 0);
if (st[2] == INVALID_SOCKET)
goto error;
memset(&ss, 0, sizeof(ss));
psin = (struct sockaddr_in *)&ss;
#ifdef HAVE_IPV6
psin6 = (struct sockaddr_in6 *)&ss;
if (intdomain == AF_INET6) {
sslen = sizeof(struct sockaddr_in6);
ss.ss_family = AF_INET6;
psin6->sin6_addr = in6addr_loopback;
} else
#endif /* HAVE_IPV6 */
{
sslen = sizeof(struct sockaddr_in);
ss.ss_family = AF_INET;
psin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
}
numtries = 3;
do {
port = htons((rand() % (CSP_LOWPORT - CSP_HIGHPORT)) + CSP_LOWPORT);
#ifdef HAVE_IPV6
if (intdomain == AF_INET6)
psin6->sin6_port = port;
else
#endif
psin->sin_port = port;
error = bind(st[2], (struct sockaddr *)&ss, sslen);
if (error == 0)
break;
if ((error != 0) &&
((WSAGetLastError() != WSAEADDRNOTAVAIL) ||
(WSAGetLastError() != WSAEADDRINUSE)))
break;
} while (--numtries > 0);
if (error != 0)
goto error;
error = listen(st[2], 5);
if (error != 0)
goto error;
st[0] = socket(intdomain, type, 0);
if (st[0] == INVALID_SOCKET)
goto error;
optval = 1L;
error = ioctlsocket(st[0], FIONBIO, &optval);
if (error != 0)
goto error;
error = connect(st[0], (struct sockaddr *)&ss, sslen);
if (error != 0 && WSAGetLastError() != WSAEWOULDBLOCK)
goto error;
numtries = 3;
do {
st[1] = accept(st[2], NULL, NULL);
if (st[1] != INVALID_SOCKET) {
break;
} else {
if (WSAGetLastError() == WSAEWOULDBLOCK) {
SleepEx(100, TRUE);
} else {
break;
}
}
} while (--numtries > 0);
if (st[1] == INVALID_SOCKET)
goto error;
/* Squelch inherited socket event mask. */
(void)WSAEventSelect(st[1], NULL, 0);
/*
* XXX: Should use getsockname() here to verify that the client socket
* is connected.
*/
optval = 0L;
error = ioctlsocket(st[0], FIONBIO, &optval);
if (error != 0)
goto error;
closesocket(st[2]);
sv[0] = st[0];
sv[1] = st[1];
return (XORP_OK);
error:
if (st[0] != INVALID_SOCKET)
closesocket(st[0]);
if (st[1] != INVALID_SOCKET)
closesocket(st[1]);
if (st[2] != INVALID_SOCKET)
closesocket(st[2]);
return (XORP_ERROR);
#endif /* HOST_OS_WINDOWS */
}
/**
* comm_sock_bind4:
* @sock: The socket to bind.
* @my_addr: The address to bind to (in network order).
* If it is NULL, will bind to `any' local address.
* @my_port: The port to bind to (in network order).
*
* Bind an IPv4 socket to an address and a port.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_sock_bind4(xsock_t sock, const struct in_addr *my_addr,
unsigned short my_port)
{
int family;
struct sockaddr_in sin_addr;
family = comm_sock_get_family(sock);
if (family != AF_INET) {
XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)",
sock, family, AF_INET);
return (XORP_ERROR);
}
memset(&sin_addr, 0, sizeof(sin_addr));
#ifdef HAVE_SIN_LEN
sin_addr.sin_len = sizeof(sin_addr);
#endif
sin_addr.sin_family = (u_char)family;
sin_addr.sin_port = my_port; /* XXX: in network order */
if (my_addr != NULL)
sin_addr.sin_addr.s_addr = my_addr->s_addr; /* XXX: in network order */
else
sin_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr *)&sin_addr, sizeof(sin_addr)) < 0) {
_comm_set_serrno();
XLOG_ERROR("Error binding socket (family = %d, "
"my_addr = %s, my_port = %d): %s",
family,
(my_addr)? inet_ntoa(*my_addr) : "ANY",
ntohs(my_port),
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
}
/**
* comm_sock_bind6:
* @sock: The socket to bind.
* @my_addr: The address to bind to (in network order).
* If it is NULL, will bind to `any' local address.
* @my_port: The port to bind to (in network order).
*
* Bind an IPv6 socket to an address and a port.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_sock_bind6(xsock_t sock, const struct in6_addr *my_addr,
unsigned short my_port)
{
#ifdef HAVE_IPV6
int family;
struct sockaddr_in6 sin6_addr;
family = comm_sock_get_family(sock);
if (family != AF_INET6) {
XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)",
sock, family, AF_INET6);
return (XORP_ERROR);
}
memset(&sin6_addr, 0, sizeof(sin6_addr));
#ifdef HAVE_SIN6_LEN
sin6_addr.sin6_len = sizeof(sin6_addr);
#endif
sin6_addr.sin6_family = (u_char)family;
sin6_addr.sin6_port = my_port; /* XXX: in network order */
sin6_addr.sin6_flowinfo = 0; /* XXX: unused (?) */
if (my_addr != NULL)
memcpy(&sin6_addr.sin6_addr, my_addr, sizeof(sin6_addr.sin6_addr));
else
memcpy(&sin6_addr.sin6_addr, &in6addr_any, sizeof(sin6_addr.sin6_addr));
sin6_addr.sin6_scope_id = 0; /* XXX: unused (?) */
if (bind(sock, (struct sockaddr *)&sin6_addr, sizeof(sin6_addr)) < 0) {
char addr_str[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
_comm_set_serrno();
XLOG_ERROR("Error binding socket (family = %d, "
"my_addr = %s, my_port = %d): %s",
family,
(my_addr)?
inet_ntop(family, my_addr, addr_str, sizeof(addr_str))
: "ANY",
ntohs(my_port), comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
#else /* ! HAVE_IPV6 */
comm_sock_no_ipv6("comm_sock_bind6", sock, my_addr, my_port);
return (XORP_ERROR);
#endif /* ! HAVE_IPV6 */
}
/**
* Bind a socket (IPv4 or IPv6) to an address and a port.
*
* @param sock the socket to bind.
* @param sin agnostic sockaddr containing the local address (If it is
* NULL, will bind to `any' local address.) and the local port to
* bind to all in network order.
* @return XORP_OK on success, otherwise XORP_ERROR.
*/
int
comm_sock_bind(xsock_t sock, const struct sockaddr *sin)
{
switch (sin->sa_family) {
case AF_INET:
{
const struct sockaddr_in *sin4 = (const struct sockaddr_in *)((const void *)sin);
return comm_sock_bind4(sock, &sin4->sin_addr, sin4->sin_port);
}
break;
#ifdef HAVE_IPV6
case AF_INET6:
{
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)((const void *)sin);
return comm_sock_bind6(sock, &sin6->sin6_addr, sin6->sin6_port);
}
break;
#endif /* HAVE_IPV6 */
default:
XLOG_FATAL("Error comm_sock_bind invalid family = %d", sin->sa_family);
return (XORP_ERROR);
}
XLOG_UNREACHABLE();
return XORP_ERROR;
}
/**
* comm_sock_join4:
* @sock: The socket to join the group.
* @mcast_addr: The multicast address to join.
* @my_addr: The local unicast address of an interface to join.
* If it is NULL, the interface is chosen by the kernel.
*
* Join an IPv4 multicast group on a socket (and an interface).
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_sock_join4(xsock_t sock, const struct in_addr *mcast_addr,
const struct in_addr *my_addr)
{
int family;
struct ip_mreq imr; /* the multicast join address */
family = comm_sock_get_family(sock);
if (family != AF_INET) {
XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)",
sock, family, AF_INET);
return (XORP_ERROR);
}
memset(&imr, 0, sizeof(imr));
imr.imr_multiaddr.s_addr = mcast_addr->s_addr;
if (my_addr != NULL)
imr.imr_interface.s_addr = my_addr->s_addr;
else
imr.imr_interface.s_addr = INADDR_ANY;
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
XORP_SOCKOPT_CAST(&imr), sizeof(imr)) < 0) {
char mcast_addr_str[32], my_addr_str[32];
_comm_set_serrno();
strncpy(mcast_addr_str, inet_ntoa(*mcast_addr),
sizeof(mcast_addr_str) - 1);
mcast_addr_str[sizeof(mcast_addr_str) - 1] = '\0';
if (my_addr != NULL)
strncpy(my_addr_str, inet_ntoa(*my_addr),
sizeof(my_addr_str) - 1);
else
strncpy(my_addr_str, "ANY", sizeof(my_addr_str) - 1);
my_addr_str[sizeof(my_addr_str) - 1] = '\0';
XLOG_ERROR("Error joining mcast group (family = %d, "
"mcast_addr = %s my_addr = %s): %s",
family, mcast_addr_str, my_addr_str,
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
}
/**
* comm_sock_join6:
* @sock: The socket to join the group.
* @mcast_addr: The multicast address to join.
* @my_ifindex: The local unicast interface index to join.
* If it is 0, the interface is chosen by the kernel.
*
* Join an IPv6 multicast group on a socket (and an interface).
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_sock_join6(xsock_t sock, const struct in6_addr *mcast_addr,
unsigned int my_ifindex)
{
#ifdef HAVE_IPV6
int family;
struct ipv6_mreq imr6; /* the multicast join address */
family = comm_sock_get_family(sock);
if (family != AF_INET6) {
XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)",
sock, family, AF_INET6);
return (XORP_ERROR);
}
memset(&imr6, 0, sizeof(imr6));
memcpy(&imr6.ipv6mr_multiaddr, mcast_addr, sizeof(*mcast_addr));
imr6.ipv6mr_interface = my_ifindex;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
XORP_SOCKOPT_CAST(&imr6), sizeof(imr6)) < 0) {
char addr_str[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
_comm_set_serrno();
XLOG_ERROR("Error joining mcast group (family = %d, "
"mcast_addr = %s my_ifindex = %d): %s",
family,
inet_ntop(family, mcast_addr, addr_str, sizeof(addr_str)),
my_ifindex, comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
#else /* ! HAVE_IPV6 */
comm_sock_no_ipv6("comm_sock_join6", sock, mcast_addr, my_ifindex);
return (XORP_ERROR);
#endif /* ! HAVE_IPV6 */
}
/**
* comm_sock_leave4:
* @sock: The socket to leave the group.
* @mcast_addr: The multicast address to leave.
* @my_addr: The local unicast address of an interface to leave.
* If it is NULL, the interface is chosen by the kernel.
*
* Leave an IPv4 multicast group on a socket (and an interface).
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_sock_leave4(xsock_t sock, const struct in_addr *mcast_addr,
const struct in_addr *my_addr)
{
int family;
struct ip_mreq imr; /* the multicast leave address */
family = comm_sock_get_family(sock);
if (family != AF_INET) {
XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)",
sock, family, AF_INET);
return (XORP_ERROR);
}
memset(&imr, 0, sizeof(imr));
imr.imr_multiaddr.s_addr = mcast_addr->s_addr;
if (my_addr != NULL)
imr.imr_interface.s_addr = my_addr->s_addr;
else
imr.imr_interface.s_addr = INADDR_ANY;
if (setsockopt(sock, IPPROTO_IP, IP_DROP_MEMBERSHIP,
XORP_SOCKOPT_CAST(&imr), sizeof(imr)) < 0) {
char mcast_addr_str[32], my_addr_str[32];
_comm_set_serrno();
strncpy(mcast_addr_str, inet_ntoa(*mcast_addr),
sizeof(mcast_addr_str) - 1);
mcast_addr_str[sizeof(mcast_addr_str) - 1] = '\0';
if (my_addr != NULL)
strncpy(my_addr_str, inet_ntoa(*my_addr),
sizeof(my_addr_str) - 1);
else
strncpy(my_addr_str, "ANY", sizeof(my_addr_str) - 1);
my_addr_str[sizeof(my_addr_str) - 1] = '\0';
XLOG_ERROR("Error leaving mcast group (family = %d, "
"mcast_addr = %s my_addr = %s): %s",
family, mcast_addr_str, my_addr_str,
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
}
/**
* comm_sock_leave6:
* @sock: The socket to leave the group.
* @mcast_addr: The multicast address to leave.
* @my_ifindex: The local unicast interface index to leave.
* If it is 0, the interface is chosen by the kernel.
*
* Leave an IPv6 multicast group on a socket (and an interface).
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_sock_leave6(xsock_t sock, const struct in6_addr *mcast_addr,
unsigned int my_ifindex)
{
#ifdef HAVE_IPV6
int family;
struct ipv6_mreq imr6; /* the multicast leave address */
family = comm_sock_get_family(sock);
if (family != AF_INET6) {
XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)",
sock, family, AF_INET6);
return (XORP_ERROR);
}
memset(&imr6, 0, sizeof(imr6));
memcpy(&imr6.ipv6mr_multiaddr, mcast_addr, sizeof(*mcast_addr));
imr6.ipv6mr_interface = my_ifindex;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
XORP_SOCKOPT_CAST(&imr6), sizeof(imr6)) < 0) {
char addr_str[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
_comm_set_serrno();
XLOG_ERROR("Error leaving mcast group (family = %d, "
"mcast_addr = %s my_ifindex = %d): %s",
family,
inet_ntop(family, mcast_addr, addr_str, sizeof(addr_str)),
my_ifindex, comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
#else /* ! HAVE_IPV6 */
comm_sock_no_ipv6("comm_sock_leave6", sock, mcast_addr, my_ifindex);
return (XORP_ERROR);
#endif /* ! HAVE_IPV6 */
}
/**
* Connect to a remote IPv4 address.
*
* Note that we can use this function not only for TCP, but for UDP sockets
* as well.
*
* @param sock the socket to use to connect.
* @param remote_addr the remote address to connect to.
* @param remote_port the remote port to connect to.
* @param is_blocking if true, the socket is blocking, otherwise non-blocking.
* @param in_progress if the socket is non-blocking and the connect cannot be
* completed immediately, then the referenced value is set to 1, and the
* return value is XORP_ERROR. For all other errors or if the non-blocking
* socket was connected, the referenced value is set to 0. If the return value
* is XORP_OK or if the socket is blocking, then the return value is undefined.
* @return XORP_OK on success, otherwise XORP_ERROR.
*/
int
comm_sock_connect4(xsock_t sock, const struct in_addr *remote_addr,
unsigned short remote_port, int is_blocking,
int *in_progress)
{
int family;
struct sockaddr_in sin_addr;
if (in_progress != NULL)
*in_progress = 0;
family = comm_sock_get_family(sock);
if (family != AF_INET) {
XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)",
sock, family, AF_INET);
return (XORP_ERROR);
}
memset(&sin_addr, 0, sizeof(sin_addr));
#ifdef HAVE_SIN_LEN
sin_addr.sin_len = sizeof(sin_addr);
#endif
sin_addr.sin_family = (u_char)family;
sin_addr.sin_port = remote_port; /* XXX: in network order */
sin_addr.sin_addr.s_addr = remote_addr->s_addr; /* XXX: in network order */
if (connect(sock, (struct sockaddr *)&sin_addr, sizeof(sin_addr)) < 0) {
_comm_set_serrno();
if (! is_blocking) {
#ifdef HOST_OS_WINDOWS
if (comm_get_last_error() == WSAEWOULDBLOCK) {
#else
if (comm_get_last_error() == EINPROGRESS) {
#endif
/*
* XXX: The connection is non-blocking, and the connection
* cannot be completed immediately, therefore set the
* in_progress flag to 1 and return an error.
*/
if (in_progress != NULL)
*in_progress = 1;
return (XORP_ERROR);
}
}
XLOG_ERROR("Error connecting socket (family = %d, "
"remote_addr = %s, remote_port = %d): %s",
family, inet_ntoa(*remote_addr), ntohs(remote_port),
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
}
/**
* Connect to a remote IPv6 address.
*
* Note that we can use this function not only for TCP, but for UDP sockets
* as well.
*
* @param sock the socket to use to connect.
* @param remote_addr the remote address to connect to.
* @param remote_port the remote port to connect to.
* @param is_blocking if true, the socket is blocking, otherwise non-blocking.
* @param in_progress if the socket is non-blocking and the connect cannot be
* completed immediately, then the referenced value is set to 1, and the
* return value is XORP_ERROR. For all other errors or if the non-blocking
* socket was connected, the referenced value is set to 0. If the return value
* is XORP_OK or if the socket is blocking, then the return value is undefined.
* @return XORP_OK on success, otherwise XORP_ERROR.
*/
int
comm_sock_connect6(xsock_t sock, const struct in6_addr *remote_addr,
unsigned short remote_port, int is_blocking,
int *in_progress)
{
#ifdef HAVE_IPV6
int family;
struct sockaddr_in6 sin6_addr;
if (in_progress != NULL)
*in_progress = 0;
family = comm_sock_get_family(sock);
if (family != AF_INET6) {
XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)",
sock, family, AF_INET6);
return (XORP_ERROR);
}
memset(&sin6_addr, 0, sizeof(sin6_addr));
#ifdef HAVE_SIN6_LEN
sin6_addr.sin6_len = sizeof(sin6_addr);
#endif
sin6_addr.sin6_family = (u_char)family;
sin6_addr.sin6_port = remote_port; /* XXX: in network order */
sin6_addr.sin6_flowinfo = 0; /* XXX: unused (?) */
memcpy(&sin6_addr.sin6_addr, remote_addr, sizeof(sin6_addr.sin6_addr));
sin6_addr.sin6_scope_id = 0; /* XXX: unused (?) */
if (connect(sock, (struct sockaddr *)&sin6_addr, sizeof(sin6_addr)) < 0) {
char addr_str[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
_comm_set_serrno();
if (! is_blocking) {
#ifdef HOST_OS_WINDOWS
if (comm_get_last_error() == WSAEWOULDBLOCK) {
#else
if (comm_get_last_error() == EINPROGRESS) {
#endif
/*
* XXX: The connection is non-blocking, and the connection
* cannot be completed immediately, therefore set the
* in_progress flag to 1 and return an error.
*/
if (in_progress != NULL)
*in_progress = 1;
return (XORP_ERROR);
}
}
XLOG_ERROR("Error connecting socket (family = %d, "
"remote_addr = %s, remote_port = %d): %s",
family,
(remote_addr)?
inet_ntop(family, remote_addr, addr_str, sizeof(addr_str))
: "ANY",
ntohs(remote_port),
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
#else /* ! HAVE_IPV6 */
if (in_progress != NULL)
*in_progress = 0;
comm_sock_no_ipv6("comm_sock_connect6", sock, remote_addr, remote_port,
is_blocking);
return (XORP_ERROR);
#endif /* ! HAVE_IPV6 */
}
/**
* Connect to a remote address (IPv4 or IPv6).
*
* Note that we can use this function not only for TCP, but for UDP sockets
* as well.
*
* @param sock the socket to use to connect.
* @param sin agnostic sockaddr containing the local address (If it is
* NULL, will bind to `any' local address.) and the local port to
* bind to all in network order.
* @param is_blocking if true, the socket is blocking, otherwise non-blocking.
* @param in_progress if the socket is non-blocking and the connect cannot be
* completed immediately, then the referenced value is set to 1, and the
* return value is XORP_ERROR. For all other errors or if the non-blocking
* socket was connected, the referenced value is set to 0. If the return value
* is XORP_OK or if the socket is blocking, then the return value is undefined.
* @return XORP_OK on success, otherwise XORP_ERROR.
*/
int
comm_sock_connect(xsock_t sock, const struct sockaddr *sin, int is_blocking,
int *in_progress)
{
switch (sin->sa_family) {
case AF_INET:
{
const struct sockaddr_in *sin4 = (const struct sockaddr_in *)((const void *)sin);
return comm_sock_connect4(sock, &sin4->sin_addr, sin4->sin_port,
is_blocking, in_progress);
}
break;
#ifdef HAVE_IPV6
case AF_INET6:
{
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)((const void *)sin);
return comm_sock_connect6(sock, &sin6->sin6_addr, sin6->sin6_port,
is_blocking, in_progress);
}
break;
#endif /* HAVE_IPV6 */
default:
XLOG_FATAL("Error comm_sock_connect invalid family = %d",
sin->sa_family);
return (XORP_ERROR);
}
XLOG_UNREACHABLE();
return XORP_ERROR;
}
/**
* comm_sock_accept:
* @sock: The listening socket to accept on.
*
* Accept a connection on a listening socket.
*
* Return value: The accepted socket on success, otherwise %XORP_BAD_SOCKET.
**/
xsock_t
comm_sock_accept(xsock_t sock)
{
xsock_t sock_accept;
struct sockaddr_in addr;
socklen_t socklen = sizeof(addr);
sock_accept = accept(sock, (struct sockaddr *)&addr, &socklen);
if (sock_accept == XORP_BAD_SOCKET) {
_comm_set_serrno();
XLOG_ERROR("Error accepting socket %d: %s",
sock, comm_get_error_str(comm_get_last_error()));
return (XORP_BAD_SOCKET);
}
#ifdef HOST_OS_WINDOWS
/*
* Squelch Winsock event notifications on the new socket which may
* have been inherited from the parent listening socket.
*/
(void)WSAEventSelect(sock_accept, NULL, 0);
#endif
/* Enable TCP_NODELAY */
if (comm_set_nodelay(sock_accept, 1) != XORP_OK) {
comm_sock_close(sock_accept);
return (XORP_BAD_SOCKET);
}
return (sock_accept);
}
/**
* comm_sock_close:
* @sock: The socket to close.
*
* Close a socket.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_sock_close(xsock_t sock)
{
int ret;
#ifndef HOST_OS_WINDOWS
ret = close(sock);
#else
(void)WSAEventSelect(sock, NULL, 0);
ret = closesocket(sock);
#endif
if (ret < 0) {
_comm_set_serrno();
XLOG_ERROR("Error closing socket (socket = %d) : %s",
sock, comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
}
/**
* comm_set_nodelay:
* @sock: The socket whose option we want to set/reset.
* @val: If non-zero, the option will be set, otherwise will be reset.
*
* Set/reset the %TCP_NODELAY option on a TCP socket.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_set_nodelay(xsock_t sock, int val)
{
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) {
_comm_set_serrno();
XLOG_ERROR("Error %s TCP_NODELAY on socket %d: %s",
(val)? "set": "reset", sock,
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
}
/**
* comm_set_reuseaddr:
* @sock: The socket whose option we want to set/reset.
* @val: If non-zero, the option will be set, otherwise will be reset.
*
* Set/reset the %SO_REUSEADDR option on a socket.
* XXX: if the OS doesn't support this option, %XORP_ERROR is returned.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_set_reuseaddr(xsock_t sock, int val)
{
#ifdef SO_REUSEADDR
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) {
_comm_set_serrno();
XLOG_ERROR("Error %s SO_REUSEADDR on socket %d: %s",
(val)? "set": "reset", sock,
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
#else /* ! SO_REUSEADDR */
UNUSED(sock);
UNUSED(val);
XLOG_WARNING("SO_REUSEADDR Undefined!");
return (XORP_ERROR);
#endif /* ! SO_REUSEADDR */
}
/**
* comm_set_reuseport:
* @sock: The socket whose option we want to set/reset.
* @val: If non-zero, the option will be set, otherwise will be reset.
*
* Set/reset the %SO_REUSEPORT option on a socket.
* XXX: if the OS doesn't support this option, %XORP_OK is returned.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_set_reuseport(xsock_t sock, int val)
{
#ifdef SO_REUSEPORT
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) {
_comm_set_serrno();
XLOG_ERROR("Error %s SO_REUSEPORT on socket %d: %s",
(val)? "set": "reset", sock,
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
#else /* ! SO_REUSEPORT */
UNUSED(sock);
UNUSED(val);
XLOG_WARNING("SO_REUSEPORT Undefined!");
return (XORP_OK);
#endif /* ! SO_REUSEPORT */
}
/**
* comm_set_loopback:
* @sock: The socket whose option we want to set/reset.
* @val: If non-zero, the option will be set, otherwise will be reset.
*
* Set/reset the multicast loopback option on a socket.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_set_loopback(xsock_t sock, int val)
{
int family = comm_sock_get_family(sock);
switch (family) {
case AF_INET:
{
u_char loop = val;
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
XORP_SOCKOPT_CAST(&loop), sizeof(loop)) < 0) {
_comm_set_serrno();
XLOG_ERROR("setsockopt IP_MULTICAST_LOOP %u: %s",
loop,
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
break;
}
#ifdef HAVE_IPV6
case AF_INET6:
{
u_int loop6 = val;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
XORP_SOCKOPT_CAST(&loop6), sizeof(loop6)) < 0) {
_comm_set_serrno();
XLOG_ERROR("setsockopt IPV6_MULTICAST_LOOP %u: %s",
loop6, comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
break;
}
#endif /* HAVE_IPV6 */
default:
XLOG_FATAL("Error %s setsockopt IP_MULTICAST_LOOP/IPV6_MULTICAST_LOOP "
"on socket %d: invalid family = %d",
(val)? "set": "reset", sock, family);
return (XORP_ERROR);
}
return (XORP_OK);
}
/**
* comm_set_tcpmd5:
* @sock: The socket whose option we want to set/reset.
* @val: If non-zero, the option will be set, otherwise will be reset.
*
* Set/reset the %TCP_MD5SIG option on a TCP socket.
* XXX: if the OS doesn't support this option, %XORP_ERROR is returned.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_set_tcpmd5(xsock_t sock, int val)
{
#ifdef TCP_MD5SIG /* XXX: Defined in <netinet/tcp.h> across Free/Net/OpenBSD */
if (setsockopt(sock, IPPROTO_TCP, TCP_MD5SIG,
XORP_SOCKOPT_CAST(&val), sizeof(val)) < 0) {
_comm_set_serrno();
XLOG_ERROR("Error %s TCP_MD5SIG on socket %d: %s",
(val)? "set": "reset", sock,
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
#else /* ! TCP_MD5SIG */
UNUSED(sock);
UNUSED(val);
XLOG_WARNING("TCP_MD5SIG Undefined!");
return (XORP_ERROR);
#endif /* ! TCP_MD5SIG */
}
/**
* comm_set_ttl:
* @sock: The socket whose TTL we want to set.
* @val: The TTL of the outgoing multicast packets.
*
* Set the TTL of the outgoing multicast packets on a socket.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_set_ttl(xsock_t sock, int val)
{
int family = comm_sock_get_family(sock);
switch (family) {
case AF_INET:
{
u_char ip_ttl = val; /* XXX: In IPv4 the value arg is 'u_char' */
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
XORP_SOCKOPT_CAST(&ip_ttl), sizeof(ip_ttl)) < 0) {
_comm_set_serrno();
XLOG_ERROR("setsockopt IP_MULTICAST_TTL %u: %s",
ip_ttl, comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
break;
}
#ifdef HAVE_IPV6
case AF_INET6:
{
int ip_ttl = val;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
XORP_SOCKOPT_CAST(&ip_ttl), sizeof(ip_ttl)) < 0) {
_comm_set_serrno();
XLOG_ERROR("setsockopt IPV6_MULTICAST_HOPS %u: %s",
ip_ttl, comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
break;
}
#endif /* HAVE_IPV6 */
default:
XLOG_FATAL("Error %s setsockopt IP_MULTICAST_TTL/IPV6_MULTICAST_HOPS "
"on socket %d: invalid family = %d",
(val)? "set": "reset", sock, family);
return (XORP_ERROR);
}
return (XORP_OK);
}
/**
* comm_set_iface4:
* @sock: The socket whose default multicast interface to set.
* @in_addr: The IPv4 address of the default interface to set.
* If @in_addr is NULL, the system will choose the interface each time
* a datagram is sent.
*
* Set default interface for IPv4 outgoing multicast on a socket.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_set_iface4(xsock_t sock, const struct in_addr *in_addr)
{
int family = comm_sock_get_family(sock);
struct in_addr my_addr;
if (family != AF_INET) {
XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)",
sock, family, AF_INET);
return (XORP_ERROR);
}
if (in_addr != NULL)
my_addr.s_addr = in_addr->s_addr;
else
my_addr.s_addr = INADDR_ANY;
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
XORP_SOCKOPT_CAST(&my_addr), sizeof(my_addr)) < 0) {
_comm_set_serrno();
XLOG_ERROR("setsockopt IP_MULTICAST_IF %s: %s",
(in_addr)? inet_ntoa(my_addr) : "ANY",
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
}
/**
* comm_set_iface6:
* @sock: The socket whose default multicast interface to set.
* @ifindex: The IPv6 interface index of the default interface to set.
* If @ifindex is 0, the system will choose the interface each time
* a datagram is sent.
*
* Set default interface for IPv6 outgoing multicast on a socket.
*
* Return value: %XORP_OK on success, otherwise %XORP_ERROR.
**/
int
comm_set_iface6(xsock_t sock, u_int ifindex)
{
#ifdef HAVE_IPV6
int family = comm_sock_get_family(sock);
if (family != AF_INET6) {
XLOG_ERROR("Invalid family of socket %d: family = %d (expected %d)",
sock, family, AF_INET6);
return (XORP_ERROR);
}
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
XORP_SOCKOPT_CAST(&ifindex), sizeof(ifindex)) < 0) {
_comm_set_serrno();
XLOG_ERROR("setsockopt IPV6_MULTICAST_IF for ifindex %d: %s",
ifindex, comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (XORP_OK);
#else /* ! HAVE_IPV6 */
comm_sock_no_ipv6("comm_set_iface6", sock, ifindex);
return (XORP_ERROR);
#endif /* ! HAVE_IPV6 */
}
/**
* comm_sock_set_sndbuf:
* @sock: The socket whose sending buffer size to set.
* @desired_bufsize: The preferred buffer size.
* @min_bufsize: The smallest acceptable buffer size.
*
* Set the sending buffer size of a socket.
*
* Return value: The successfully set buffer size on success,
* otherwise %XORP_ERROR.
**/
int
comm_sock_set_sndbuf(xsock_t sock, int desired_bufsize, int min_bufsize)
{
int delta = desired_bufsize / 2;
/*
* Set the socket buffer size. If we can't set it as large as we
* want, search around to try to find the highest acceptable
* value. The highest acceptable value being smaller than
* minsize is a fatal error.
*/
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
XORP_SOCKOPT_CAST(&desired_bufsize),
sizeof(desired_bufsize)) < 0) {
desired_bufsize -= delta;
while (1) {
if (delta > 1)
delta /= 2;
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
XORP_SOCKOPT_CAST(&desired_bufsize),
sizeof(desired_bufsize)) < 0) {
_comm_set_serrno();
desired_bufsize -= delta;
if (desired_bufsize <= 0)
break;
} else {
if (delta < 1024)
break;
desired_bufsize += delta;
}
}
if (desired_bufsize < min_bufsize) {
XLOG_ERROR("Cannot set sending buffer size of socket %d: "
"desired buffer size %u < minimum allowed %u",
sock, desired_bufsize, min_bufsize);
return (XORP_ERROR);
}
}
return (desired_bufsize);
}
/**
* comm_sock_set_rcvbuf:
* @sock: The socket whose receiving buffer size to set.
* @desired_bufsize: The preferred buffer size.
* @min_bufsize: The smallest acceptable buffer size.
*
* Set the receiving buffer size of a socket.
*
* Return value: The successfully set buffer size on success,
* otherwise %XORP_ERROR.
**/
int
comm_sock_set_rcvbuf(xsock_t sock, int desired_bufsize, int min_bufsize)
{
int delta = desired_bufsize / 2;
/*
* Set the socket buffer size. If we can't set it as large as we
* want, search around to try to find the highest acceptable
* value. The highest acceptable value being smaller than
* minsize is a fatal error.
*/
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
XORP_SOCKOPT_CAST(&desired_bufsize),
sizeof(desired_bufsize)) < 0) {
desired_bufsize -= delta;
while (1) {
if (delta > 1)
delta /= 2;
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
XORP_SOCKOPT_CAST(&desired_bufsize),
sizeof(desired_bufsize)) < 0) {
_comm_set_serrno();
desired_bufsize -= delta;
if (desired_bufsize <= 0)
break;
} else {
if (delta < 1024)
break;
desired_bufsize += delta;
}
}
if (desired_bufsize < min_bufsize) {
XLOG_ERROR("Cannot set receiving buffer size of socket %d: "
"desired buffer size %u < minimum allowed %u",
sock, desired_bufsize, min_bufsize);
return (XORP_ERROR);
}
}
return (desired_bufsize);
}
/**
* comm_sock_get_family:
* @sock: The socket whose address family we need to get.
*
* Get the address family of a socket.
* XXX: idea taken from W. Stevens' UNPv1, 2e (pp 109)
*
* Return value: The address family on success, otherwise %XORP_ERROR.
**/
int
comm_sock_get_family(xsock_t sock)
{
#ifdef HOST_OS_WINDOWS
WSAPROTOCOL_INFO wspinfo;
int err, len;
len = sizeof(wspinfo);
err = getsockopt(sock, SOL_SOCKET, SO_PROTOCOL_INFO,
XORP_SOCKOPT_CAST(&wspinfo), &len);
if (err != 0) {
_comm_set_serrno();
XLOG_ERROR("Error getsockopt(SO_PROTOCOL_INFO) for socket %d: %s",
sock, comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return ((int)wspinfo.iAddressFamily);
#else /* ! HOST_OS_WINDOWS */
/* XXX: Should use struct sockaddr_storage. */
#ifndef MAXSOCKADDR
#define MAXSOCKADDR 128 /* max socket address structure size */
#endif
union {
struct sockaddr sa;
char data[MAXSOCKADDR];
} un;
socklen_t len;
len = MAXSOCKADDR;
if (getsockname(sock, &un.sa, &len) < 0) {
_comm_set_serrno();
XLOG_ERROR("Error getsockname() for socket %d: %s",
sock, comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
return (un.sa.sa_family);
#endif /* ! HOST_OS_WINDOWS */
}
/**
* comm_sock_set_blocking:
*
* Set the blocking or non-blocking mode of an existing socket.
* @sock: The socket whose blocking mode is to be set.
* @is_blocking: If non-zero, then the socket will be blocking, otherwise
* non-blocking.
*
* Return value: XORP_OK if the operation was successful, otherwise
* if any error is encountered, XORP_ERROR.
**/
int
comm_sock_set_blocking(xsock_t sock, int is_blocking)
{
#ifdef HOST_OS_WINDOWS
u_long opt;
int flags;
if (is_blocking)
opt = 0;
else
opt = 1;
flags = ioctlsocket(sock, FIONBIO, &opt);
if (flags != 0) {
_comm_set_serrno();
XLOG_ERROR("FIONBIO error: %s",
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
#else /* ! HOST_OS_WINDOWS */
int flags;
if ( (flags = fcntl(sock, F_GETFL, 0)) < 0) {
_comm_set_serrno();
XLOG_ERROR("F_GETFL error: %s",
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
if (is_blocking)
flags &= ~O_NONBLOCK;
else
flags |= O_NONBLOCK;
if (fcntl(sock, F_SETFL, flags) < 0) {
_comm_set_serrno();
XLOG_ERROR("F_SETFL error: %s",
comm_get_error_str(comm_get_last_error()));
return (XORP_ERROR);
}
#endif /* ! HOST_OS_WINDOWS */
return (XORP_OK);
}
/**
* comm_sock_is_connected:
*
* Determine if an existing socket is in the connected state.
*
* Return value: XORP_OK if the socket is in the connected state, otherwise
* if it is not, or any other error is encountered, XORP_ERROR.
**/
int
comm_sock_is_connected(xsock_t sock)
{
struct sockaddr_storage ss;
int err;
socklen_t sslen;
sslen = sizeof(ss);
memset(&ss, 0, sslen);
err = getpeername(sock, (struct sockaddr *)&ss, &sslen);
if (err != 0) {
_comm_set_serrno();
return (XORP_ERROR);
}
return (XORP_OK);
}
/**
* comm_sock_no_ipv6:
*
* Log an error when an IPv6 specific method is called when IPv6 is
* not preset. Set an appropriate error code relevant to the platform
* we're running under.
*
* XXX This is currently done with knowledge of how the error code is
* internally stored, which is a design bug (we're not thread friendly).
*
* @param method C-style string denoting the ipv6 function called.
**/
void
comm_sock_no_ipv6(const char* method, ...)
{
#ifdef HOST_OS_WINDOWS
_comm_serrno = WSAEAFNOSUPPORT;
#else
_comm_serrno = EAFNOSUPPORT;
#endif
XLOG_ERROR("%s: IPv6 support not present.", method);
}
/**
* _comm_set_serrno:
*
* Fetch the socket layer error code from the underlying system, clear
* the general error condition, and record it.
*
* XXX: Currently not thread-safe. Internal use only.
**/
void
_comm_set_serrno(void)
{
#ifdef HOST_OS_WINDOWS
_comm_serrno = WSAGetLastError();
WSASetLastError(0);
#else
_comm_serrno = errno;
/*
* TODO: XXX - Temporarily don't set errno to 0 we still have code
* using errno 2005-05-09 Atanu.
*/
/* errno = 0; */
#endif
}
syntax highlighted by Code2HTML, v. 0.9.1