summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_network.c345
-rw-r--r--lib/errno_names.c60
-rw-r--r--lib/errno_names.h5
-rw-r--r--lib/pthread_safe.c249
-rw-r--r--lib/pthread_safe.h4
-rw-r--r--lib/sockunion.c15
-rw-r--r--lib/sockunion.h2
-rw-r--r--lib/vty_io.c2
8 files changed, 479 insertions, 203 deletions
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index 856447d7..710dc51e 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -86,10 +86,21 @@ typedef struct bgp_listener* bgp_listener ;
static bgp_listener bgp_listeners[] =
{
[AF_INET] = NULL,
+#if HAVE_IPV6
[AF_INET6] = NULL
+#endif
} ;
-CONFIRM((AF_INET < 20) && (AF_INET6 < 20)) ;
+CONFIRM(AF_INET < 20) ; /* The bgp_listeners array is not a silly size */
+#if HAVE_IPV6
+CONFIRM(AF_INET6 < 20) ; /* The bgp_listeners array is not a silly size */
+#endif
+
+#if defined(HAVE_IPV6) && !defined(NRL)
+# define BGP_USE_ADDRINFO 1
+#else
+# define BGP_USE_ADDRINFO 0
+#endif
/* BGP listening socket. */
struct bgp_listener
@@ -100,8 +111,11 @@ struct bgp_listener
} ;
/* Forward reference */
-static int bgp_open_listener_on(const char* address, unsigned short port);
-static int bgp_init_listener(int sock_fd, struct sockaddr *sa, socklen_t salen);
+static int bgp_open_listeners_addrinfo(const char* address,
+ unsigned short port) ;
+static int bgp_open_listeners_simple(const char* address, unsigned short port) ;
+static int bgp_open_listener(sockunion su, unsigned short port,
+ int sock_type, int sock_protocol) ;
/*------------------------------------------------------------------------------
* Open Listeners.
@@ -119,7 +133,7 @@ static int bgp_init_listener(int sock_fd, struct sockaddr *sa, socklen_t salen);
* "80.177.246.130," -- will list on that address and
* any other local address.
*
- * NB: only listens on AF_INET and (if HAVE_IPV6) AF_INET6.
+ * NB: only listens on AF_INET and AF_INET6 (if HAVE_IPV6).
*
* Returns: > 0 => OK -- number of listeners set up
* -1 => failed -- no listeners set up
@@ -128,39 +142,39 @@ extern int
bgp_open_listeners(const char* address, unsigned short port)
{
int count ;
- bool do_null ;
+ bool done_null ;
+ char* copy ;
+ char* next ;
- count = 0 ;
- do_null = (address == NULL) ;
+ if (address == NULL)
+ address = "" ;
+
+ copy = XSTRDUP(MTYPE_TMP, address) ;
- if (!do_null)
+ done_null = false ;
+ next = copy ;
+ count = 0 ;
+ do
{
- char* copy ;
- char* next ;
- char* this ;
+ address = next ;
+ next = strchr(address, ',') ;
- copy = XSTRDUP(MTYPE_TMP, address) ;
+ if (next != NULL)
+ *next++ = '\0' ; /* replace ',' and step past */
- next = copy ;
- while (next != NULL)
+ if (*address == '\0')
{
- this = next ;
- next = strchr(this, ',') ;
-
- if (next != NULL)
- *next++ = '\0' ;
-
- if (*this == '\0')
- do_null = true ; /* empty address => do_null */
+ if (done_null)
+ continue ; /* don't do "" more than once */
else
- count += bgp_open_listener_on(this, port) ;
+ done_null = true ;
} ;
- XFREE(MTYPE_TMP, copy) ;
- } ;
+ count += BGP_USE_ADDRINFO ? bgp_open_listeners_addrinfo(address, port)
+ : bgp_open_listeners_simple(address, port) ;
+ } while (next != NULL) ;
- if (do_null)
- count += bgp_open_listener_on(NULL, port) ;
+ XFREE(MTYPE_TMP, copy) ;
if (count == 0)
{
@@ -172,22 +186,20 @@ bgp_open_listeners(const char* address, unsigned short port)
} ;
/*------------------------------------------------------------------------------
- * Open Listeners.
- *
- * Using given address and port, get all possible addresses and set up a
- * listener on each one.
+ * Open listeners using getaddrinfo() to find the addresses.
*
- * NB: only listens on AF_INET and (if HAVE_IPV6) AF_INET6.
+ * Note that this will accept names as well as numeric addresses.
*
- * Returns: 0 => OK
- * -1 => failed -- no listeners set up
+ * Returns: count of listeners opened successfully.
*/
static int
-bgp_open_listener_on(const char* address, unsigned short port)
+bgp_open_listeners_addrinfo(const char* address, unsigned short port)
{
-#if defined (HAVE_IPV6) && ! defined (NRL) /*----------------------------*/
+#if BGP_USE_ADDRINFO
- /* IPv6 supported version of BGP server socket setup. */
+# ifndef HAVE_IPV6
+# error Using getaddrinfo() but HAVE_IPV6 is not defined ??
+# endif
struct addrinfo *ainfo;
struct addrinfo *ainfo_save;
@@ -203,119 +215,92 @@ bgp_open_listener_on(const char* address, unsigned short port)
snprintf (port_str, sizeof(port_str), "%d", port);
port_str[sizeof (port_str) - 1] = '\0';
+ if (*address == '\0')
+ address = NULL ;
+
ret = getaddrinfo (address, port_str, &req, &ainfo_save);
if (ret != 0)
{
- zlog_err ("getaddrinfo: %s", gai_strerror (ret));
+ zlog_err ("%s: getaddrinfo: %s", __func__, eaitoa(ret, errno, 0).str);
return 0 ;
}
count = 0;
for (ainfo = ainfo_save; ainfo; ainfo = ainfo->ai_next)
{
- int sock_fd;
+ union sockunion su ;
+ int err ;
- if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
+ if ((ainfo->ai_family != AF_INET) && (ainfo->ai_family != AF_INET6))
continue;
- sock_fd = sockunion_socket(ainfo->ai_family, ainfo->ai_socktype,
- ainfo->ai_protocol);
- if (sock_fd < 0)
- {
- zlog_err ("socket: %s", errtoa(errno, 0).str);
- continue;
- }
-
- ret = bgp_init_listener(sock_fd, ainfo->ai_addr, ainfo->ai_addrlen);
-
- if (ret == 0)
+ sockunion_new_sockaddr(&su, ainfo->ai_addr) ;
+ err = bgp_open_listener(&su, port,
+ ainfo->ai_socktype, ainfo->ai_protocol) ;
+ if (err == 0)
++count;
- else
- close(sock_fd);
}
freeaddrinfo (ainfo_save);
return count ;
-}
-#else /*----------------------------------------------------*/
-
- /* Traditional IPv4 only version. */
-
- int sock_fd;
- int socklen;
- struct sockaddr_in sin;
- int ret, en;
-
- memset (&sin, 0, sizeof (struct sockaddr_in));
- sin.sin_family = AF_INET;
- sin.sin_port = htons (port);
- socklen = sizeof (struct sockaddr_in);
-
- if (address && ((ret = inet_aton(address, &sin.sin_addr)) < 1))
- {
- zlog_err("bgp_socket: could not parse ip address %s: %s",
- address, errtoa(errno, 0).str);
- return ret;
- }
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- sin.sin_len = socklen;
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
-
- sock_fd = socket (AF_INET, SOCK_STREAM, 0);
- if (sock_fd < 0)
- {
- zlog_err ("socket: %s", errtoa(errno, 0).str);
- return sock_fd;
- }
- ret = bgp_init_listener (sock_fd, (struct sockaddr *) &sin, socklen);
- if (ret < 0)
- {
- close (sock_fd);
- return ret;
- }
- return sock_fd;
+#else
+ zabort("bgp_open_listeners_addrinfo not implemented") ;
+#endif /* BGP_USE_ADDRINFO */
}
-#endif /* HAVE_IPV6 && !NRL --------------------------------------------------*/
-
/*------------------------------------------------------------------------------
- * Close Listeners.
+ * Open listener the old fashioned way.
*
- * Empty the listener lists, close files, remove from the selection.
+ * NB: if address is "" tries IPv4 and IPv6 (if supported).
*
+ * NB: if address is not NULL, must be a numeric IP address (which may be IPv6
+ * if that is supported).
+ *
+ * Returns: count of listeners opened successfully.
*/
-static void bgp_reset_listeners(bgp_listener* p_listener) ;
-
-extern void
-bgp_close_listeners(void)
-{
- bgp_reset_listeners(&bgp_listeners[AF_INET]) ;
- bgp_reset_listeners(&bgp_listeners[AF_INET6]) ;
-} ;
-
-static void
-bgp_reset_listeners(bgp_listener* p_listener)
+static int
+bgp_open_listeners_simple(const char* address, unsigned short port)
{
- bgp_listener listener ;
- bgp_listener next ;
-
- next = *p_listener ;
- *p_listener = NULL ;
+ union sockunion su ;
+ int err ;
+ int count ;
- while (next != NULL)
+ /* If address is not null, must be a single, specific, numeric address */
+ if (*address != '\0')
{
- listener = next ;
- next = listener->next ;
+ int ret = str2sockunion (address, &su) ;
+ if (ret < 0)
+ {
+ zlog_err("bgp_socket: could not parse ip address %s: %s",
+ address, errtoa(errno, 0).str);
+ return 0 ;
+ }
- close(qps_file_fd(&listener->qf)) ;
- qps_remove_file(&listener->qf) ;
+ err = bgp_open_listener(&su, port, SOCK_STREAM, 0) ;
- XFREE(MTYPE_BGP_LISTENER, listener) ;
+ return (err == 0) ? 1 : 0 ;
} ;
+
+ /* Null address, try <any> for IPv4 and (if supported) IPv6 */
+ count = 0 ;
+
+ sockunion_init_new(&su, AF_INET) ;
+ err = bgp_open_listener(&su, port, SOCK_STREAM, 0) ;
+ if (err == 0)
+ ++count ;
+
+#ifdef HAVE_IPV6
+ sockunion_init_new(&su, AF_INET6) ;
+ err = bgp_open_listener(&su, port, SOCK_STREAM, 0) ;
+ if (err == 0)
+ ++count ;
+#endif
+
+ return count ;
} ;
/*------------------------------------------------------------------------------
- * Initialise Listener Socket
+ * Open Listener Socket
*
* Sets up socket with the usual options. Binds to given address and listens.
*
@@ -328,50 +313,77 @@ bgp_reset_listeners(bgp_listener* p_listener)
* != 0 : error number (from errno or otherwise)
*/
static int
-bgp_init_listener(int sock_fd, struct sockaddr *sa, socklen_t salen)
+bgp_open_listener(sockunion su, unsigned short port,
+ int sock_type, int sock_protocol)
{
bgp_listener listener ;
int ret, err ;
+ int slen ;
+ int sock_fd ;
- err = bgp_socket_set_common_options(sock_fd, (union sockunion*)sa, 0, NULL) ;
- if (err != 0)
- return err ;
+ /* Construct socket and set the common options. */
+ sock_fd = socket (sockunion_family(su), sock_type, sock_protocol) ;
+ if (sock_fd < 0)
+ {
+ err = errno ;
+ zlog_err ("%s: could not open socket for family %d: %s", __func__,
+ sockunion_family(su), errtoa(err, 0).str) ;
+ return errno = err ;
+ }
+
+ err = bgp_socket_set_common_options(sock_fd, su, 0, NULL) ;
-#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)
/* Want only IPV6 on ipv6 socket (not mapped addresses)
*
* This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the
* attempt to bind to :: after binding to 0.0.0.0.
+ *
+ * Also, for all the apparent utility of IPv4-mapped addresses, the semantics
+ * are simpler if IPv6 sockets speak IPv6 and IPv4 sockets speak IPv4.
*/
- if (sa->sa_family == AF_INET6)
- {
- int on = 1;
- ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
- if (ret < 0)
- return errno ;
- }
-#endif
-
- if (bgpd_privs.change(ZPRIVS_RAISE))
+#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY)
+ if ((err == 0) && (sockunion_family(su) == AF_INET6))
{
- err = errno ;
- zlog_err("%s: could not raise privs: %s", __func__, errtoa(errno, 0).str);
+ int on = 1;
+ ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+ if (ret < 0)
+ {
+ err = errno ;
+ zlog_err("%s: could not set IPV6_V6ONLY: %s", __func__,
+ errtoa(err, 0).str) ;
+ }
} ;
+#endif
- ret = bind(sock_fd, sa, salen) ;
- if (ret < 0)
+ /* Bind to port and address (if any) */
+ if (err == 0)
{
- err = errno ;
- zlog_err ("%s: bind: %s", __func__, errtoa(err, 0).str);
- } ;
+ if (bgpd_privs.change(ZPRIVS_RAISE))
+ {
+ err = errno ;
+ zlog_err("%s: could not raise privs: %s", __func__,
+ errtoa(errno, 0).str) ;
+ } ;
- if (bgpd_privs.change(ZPRIVS_LOWER))
- {
- if (err == 0)
- err = errno ;
- zlog_err("%s: could not lower privs: %s", __func__, errtoa(errno, 0).str);
+ slen = sockunion_set_port(su, port) ;
+
+ ret = bind(sock_fd, &su->sa, slen) ;
+ if (ret < 0)
+ {
+ err = errno ;
+ zlog_err ("%s: bind: %s", __func__, errtoa(err, 0).str);
+ } ;
+
+ if (bgpd_privs.change(ZPRIVS_LOWER))
+ {
+ if (err == 0)
+ err = errno ;
+ zlog_err("%s: could not lower privs: %s", __func__,
+ errtoa(errno, 0).str) ;
+ } ;
} ;
+ /* Last lap... listen() */
if (err == 0)
{
ret = listen (sock_fd, 43);
@@ -392,22 +404,57 @@ bgp_init_listener(int sock_fd, struct sockaddr *sa, socklen_t salen)
* again, add it to the BGP Engine Nexus file selection and enable it for
* reading.
*/
-
listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(struct bgp_listener)) ;
qps_file_init_new(&listener->qf, NULL) ;
qps_add_file(bgp_nexus->selection, &listener->qf, sock_fd, listener) ;
qps_enable_mode(&listener->qf, qps_read_mnum, bgp_accept_action) ;
- memcpy(&listener->su, sa, salen) ;
+ sockunion_copy(&listener->su, su) ;
- listener->next = bgp_listeners[sa->sa_family] ;
- bgp_listeners[sa->sa_family] = listener ;
+ listener->next = bgp_listeners[sockunion_family(su)] ;
+ bgp_listeners[sockunion_family(su)] = listener ;
return 0 ;
} ;
/*------------------------------------------------------------------------------
+ * Close Listeners.
+ *
+ * Empty the listener lists, close files, remove from the selection.
+ *
+ */
+static void bgp_reset_listeners(bgp_listener* p_listener) ;
+
+extern void
+bgp_close_listeners(void)
+{
+ bgp_reset_listeners(&bgp_listeners[AF_INET]) ;
+ bgp_reset_listeners(&bgp_listeners[AF_INET6]) ;
+} ;
+
+static void
+bgp_reset_listeners(bgp_listener* p_listener)
+{
+ bgp_listener listener ;
+ bgp_listener next ;
+
+ next = *p_listener ;
+ *p_listener = NULL ;
+
+ while (next != NULL)
+ {
+ listener = next ;
+ next = listener->next ;
+
+ close(qps_file_fd(&listener->qf)) ;
+ qps_remove_file(&listener->qf) ;
+
+ XFREE(MTYPE_BGP_LISTENER, listener) ;
+ } ;
+} ;
+
+/*------------------------------------------------------------------------------
* Prepare to accept() connection
*
* If the session has a password, then this is where the listener(s) for the
diff --git a/lib/errno_names.c b/lib/errno_names.c
index 51f430a9..791f8001 100644
--- a/lib/errno_names.c
+++ b/lib/errno_names.c
@@ -21,6 +21,8 @@
#include <stddef.h>
#include <errno.h>
+#include <netdb.h>
+
#include "errno_names.h"
/*==============================================================================
@@ -291,7 +293,7 @@ static const char* errno_name_table[] =
enum { errno_last = (sizeof(errno_name_table) / sizeof(char*)) - 1 } ;
-/*==============================================================================
+/*------------------------------------------------------------------------------
* Lookup the name for given error number.
*
* Returns: address of string, or NULL if not known
@@ -308,4 +310,60 @@ errno_name_lookup(int err)
return errno_name_table[err] ;
} ;
+/*==============================================================================
+ * Table to map EAI error number to its name -- the errors generated by
+ * getaddrinfo() and getnameinfo().
+ *
+ * At least one system uses -ve numbers for these... so the following will
+ * support either +ve or -ve values, provided that all have the same sign.
+ */
+#if EAI_AGAIN < 0
+enum { eai_sgn = -1 } ;
+#else
+enum { eai_sgn = +1 } ;
+#endif
+
+#define EAINO(eai) [eai * eai_sgn] = #eai
+
+static const char* eaino_name_table[] =
+{
+ /* Error number for no error
+ *
+ * (123456789012345), /-- no name is more than 15 characters
+ */
+ EAINO(EAI_OK), /* No error */
+ /* POSIX Error Numbers -- taken Open Group Base Specifications Issue 7
+ * IEEE Std 1003.1-2008
+ */
+ EAINO(EAI_AGAIN), /* Temporary failure in name resolution. */
+ EAINO(EAI_BADFLAGS), /* Invalid value for 'ai_flags' field. */
+ EAINO(EAI_FAIL), /* Non-recoverable failure in name res. */
+ EAINO(EAI_FAMILY), /* 'ai_family' not supported. */
+ EAINO(EAI_MEMORY), /* Memory allocation failure. */
+ EAINO(EAI_NONAME), /* NAME or SERVICE is unknown. */
+ EAINO(EAI_OVERFLOW), /* Argument buffer overflow. */
+ EAINO(EAI_SERVICE), /* SERVICE not supported for 'ai_socktype'. */
+ EAINO(EAI_SOCKTYPE), /* 'ai_socktype' not supported. */
+ EAINO(EAI_SYSTEM), /* System error returned in 'errno'. */
+} ;
+
+enum { eaino_last = (sizeof(eaino_name_table) / sizeof(char*)) - 1 } ;
+
+/*------------------------------------------------------------------------------
+ * Lookup the name for given error number.
+ *
+ * Returns: address of string, or NULL if not known
+ *
+ * NB: for 0 returns "EOK".
+ *
+ * NB: async-signal-safe and thread-safe !
+ */
+extern const char*
+eaino_name_lookup(int eai)
+{
+ eai *= eai_sgn ;
+ if ((eai < 0) || (eai > eaino_last))
+ return NULL ;
+ return eaino_name_table[eai] ;
+} ;
diff --git a/lib/errno_names.h b/lib/errno_names.h
index f095640d..a4f1f2e3 100644
--- a/lib/errno_names.h
+++ b/lib/errno_names.h
@@ -23,4 +23,9 @@
#define EOK 0
#endif
+#ifndef EAI_OK
+#define EAI_OK 0
+#endif
+
extern const char* errno_name_lookup(int err) ;
+extern const char* eaino_name_lookup(int err) ;
diff --git a/lib/pthread_safe.c b/lib/pthread_safe.c
index b6c429e4..c4ab9679 100644
--- a/lib/pthread_safe.c
+++ b/lib/pthread_safe.c
@@ -36,6 +36,7 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
+#include <netdb.h>
#include "qfstring.h"
#include "errno_names.h"
@@ -101,29 +102,94 @@ safe_strerror(int errnum)
}
}
-/*------------------------------------------------------------------------------
- * Construct string to describe the given error of the form:
+/*==============================================================================
+ * Alternative error number handling.
*
- * ENAME '<strerror>'
+ * The descriptive strings for error numbers are all very well, but for some
+ * purposes knowing the name for the error is more useful -- the name, not the
+ * number, as the number is system dependent.
+ *
+ * The following provides:
+ *
+ * * errtoa() -- which maps error number to: ENAME '<strerror>'
*
- * where <strerror> is given by strerror() or, if pthreads_enabled, strerror_r()
+ * * errtoname() -- which maps error number to: ENAME
*
- * Truncates the result to the given len (0 => no truncation). But silently
- * imposes the maximum length allowed by the strerror_t.
+ * * errtostr() -- which maps error number to: <strerror>
*
- * If has to truncate, places "..." at the end of the message to show this has
- * happened.
+ * where:
*
- * If a name is not known then (assuming strerror() won't know it either):
+ * * if name is not known gives: ERRNO=999
*
- * ERRNO=999 *unknown error*
+ * * if strerror rejects the number gives: *unknown error*
*
- * For err==0 returns:
+ * * err == 0 gives: EOK -- for the name
+ * 'no error' -- for the <strerror>
+ *
+ * These functions take a 'len' argument, and truncates the result to the given
+ * len (0 => no truncation) -- silently imposing the maximum length allowed by
+ * the strerror_t.
+ *
+ * If has to truncate the <strerror>, places "..." at the end of the message
+ * to show this has happened.
+ */
+
+static void errtox(strerror_t* st, int err, int len, int want) ;
+
+/*------------------------------------------------------------------------------
+ * Construct string to describe the given error of the form:
+ *
+ * ENAME '<strerror>'
+ *
+ * Thread safe extension to strerror. Never returns NULL.
+ */
+extern strerror_t
+errtoa(int err, int len)
+{
+ strerror_t st ;
+
+ errtox(&st, err, len, 3) ; /* name and message */
+
+ return st ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Convert error number to its name
*
- * EOK 'no error'
+ * Thread-safe. Never returns NULL.
+ */
+extern strerror_t
+errtoname(int err, int len)
+{
+ strerror_t st ;
+
+ errtox(&st, err, len, 1) ; /* name */
+
+ return st ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Alternative thread-safe safe_strerror()
*
* Thread safe replacement for strerror. Never returns NULL.
*/
+extern strerror_t
+errtostr(int err, int len)
+{
+ strerror_t st ;
+
+ errtox(&st, err, len, 2) ; /* message */
+
+ return st ;
+} ;
+
+/*-----------------------------------------------------------------------------
+ * Common code for errto<x> above.
+ *
+ * want == 1 -- return just name
+ * want == 2 -- return just the strerror()
+ * want == 3 -- return both, with the strerror() in single quotes.
+ */
static void
errtox(strerror_t* st, int err, int len, int want)
{
@@ -241,86 +307,169 @@ errtox(strerror_t* st, int err, int len, int want)
errno = errno_saved ;
} ;
-/*------------------------------------------------------------------------------
- * Construct string to describe the given error of the form:
+/*==============================================================================
+ * getaddrinfo() and getnameinfo() "EAI_XXXX" error number handling.
*
- * ENAME '<strerror>'
+ * This is similar to the above for errno.
*
- * where <strerror> is given by strerror() or, if pthreads_enabled, strerror_r()
+ * The following provides:
*
- * Truncates the result to the given len (0 => no truncation). But silently
- * imposes the maximum length allowed by the strerror_t.
+ * * eaitoa() -- which maps error number to: EAI_XXX '<gai_strerror>'
+ * or: as errtoa()
*
- * If has to truncate, places "..." at the end of the message to show this has
- * happened.
+ * * eaitoname() -- which maps error number to: EAI_XXX
+ * or: as errtoname()
*
- * If a name is not known then (assuming strerror() won't know it either):
+ * * eaitostr() -- which maps error number to: <gai_strerror>
+ * or: as errtostr()
*
- * ERRNO=999 *unknown error*
+ * where:
*
- * For err==0 returns:
+ * * if given EAI_SYSTEM, and given a non-zero errno type error number,
+ * produce the errno string.
*
- * EOK 'no error'
+ * * if name is not known gives: EAI=999
*
- * Thread safe replacement for strerror. Never returns NULL.
+ * * gai_strerror returns a string saying the error is not known if that is
+ * the case.
+ *
+ * * eai == 0 gives: EAI_OK -- for the name
+ * 'no error' -- for the <sgai_strerror>
+ *
+ * NB: EAI_SYSTEM is an invitation to look at errno to discover the true
+ * error.
+ */
+
+static void eaitox(strerror_t* st, int eai, int err, int len, int want) ;
+
+/*------------------------------------------------------------------------------
+ * Construct string to describe the given EAI_XXX error of the form:
+ *
+ * EAI_XXX '<gai_strerror>'
+ * or: ENAME '<strerror>' -- if EAI_SYSTEM and err != 0
+ *
+ * Thread safe. Never returns NULL.
*/
extern strerror_t
-errtoa(int err, int len)
+eaitoa(int eai, int err, int len)
{
strerror_t st ;
- errtox(&st, err, len, 3) ; /* name and message */
+ eaitox(&st, eai, err, len, 3) ; /* name and message */
return st ;
} ;
/*------------------------------------------------------------------------------
- * Convert error number to its name
-
- * If a name is not known then:
- *
- * ERRNO=999
- *
- * For err==0 returns:
+ * Convert EAI_XXX error number to its name...
*
- * EOK
+ * ...or, if EAI_SYSTEM and err != 0, convert err to its name.
*
- * Truncates the result to the given len (0 => no truncation). But silently
- * imposes the maximum length allowed by the strerror_t.
- *
- * This is thread-safe.
+ * Thread-safe. Never returns NULL.
*/
extern strerror_t
-errtoname(int err, int len)
+eaitoname(int eai, int err, int len)
{
strerror_t st ;
- errtox(&st, err, len, 1) ; /* name */
+ eaitox(&st, eai, err, len, 1) ; /* name */
return st ;
} ;
/*------------------------------------------------------------------------------
- * Alternative thread-safe safe_strerror()
+ * Alternative to gai_strerror()...
*
- * Truncates the result to the given len (0 => no truncation). But silently
- * imposes the maximum length allowed by the strerror_t.
+ * ...or, if EAI_SYSTEM and err != 0, do strerror(err) or strerror_r(err).
*
- * If the <strerror> will not fit in the strerror_t, it is truncated and
- * '...' placed at the end to show this has happened.
- *
- * Thread safe replacement for strerror. Never returns NULL.
+ * Thread-safe. Never returns NULL.
*/
extern strerror_t
-errtostr(int err, int len)
+eaitostr(int eai, int err, int len)
{
strerror_t st ;
- errtox(&st, err, len, 2) ; /* message */
+ eaitox(&st, eai, err, len, 2) ; /* message */
return st ;
} ;
+/*-----------------------------------------------------------------------------
+ * Common code for eaito<x> above.
+ *
+ * want == 1 -- return just name
+ * want == 2 -- return just the gai_strerror()
+ * want == 3 -- return both, with the gai_strerror() in single quotes.
+ *
+ * err != 0 => if EAI_SYSTEM, return result for errno == err instead.
+ */
+static void
+eaitox(strerror_t* st, int eai, int err, int len, int want)
+{
+ qf_str_t qfs ;
+
+ const char* q ;
+ int ql ;
+
+ /* Look out for mapping EAI_SYSTEM */
+ if ((eai == EAI_SYSTEM) && (err != 0))
+ return errtox(st, err, len, want) ;
+
+ /* Prepare. */
+ int errno_saved = errno ;
+
+ if ((len <= 0) || (len >= (int)sizeof(st->str)))
+ len = sizeof(st->str) - 1 ;
+ qfs_init(&qfs, st->str, len + 1) ;
+
+ q = "" ;
+ ql = 0 ;
+
+ /* If want the error name, do that now. */
+ if (want & 1)
+ {
+ const char* name = eaino_name_lookup(eai) ;
+
+ if (name != NULL)
+ qfs_append(&qfs, name) ;
+ else
+ qfs_printf(&qfs, "EAI=%d", eai) ;
+ } ;
+
+ /* name and string ? */
+ if (want == 3)
+ {
+ qfs_append(&qfs, " ") ;
+ q = "'" ;
+ ql = 2 ;
+ } ;
+
+ /* If want the error string, do that now */
+ if (want & 2)
+ {
+ const char* eaim ;
+
+ if (eai == 0)
+ eaim = "no error" ;
+ else
+ eaim = gai_strerror(eai) ;
+
+ /* Add strerror to the result... looking out for overflow. */
+ len = strlen(eaim) ;
+
+ if ((len + ql) <= qfs_left(&qfs)) /* accounting for "quotes" */
+ qfs_printf(&qfs, "%s%s%s", q, eaim, q) ;
+ else
+ qfs_printf(&qfs, "%s%.*s...%s", q, qfs_left(&qfs) - ql - 3, eaim, q) ;
+ /* -ve precision is ignored ! */
+ } ;
+
+ /* Put back errno */
+ errno = errno_saved ;
+} ;
+
+/*============================================================================*/
+
/* Thread safe version of inet_ntoa. Never returns NULL.
* Contents of result remains intact until another call of
* a safe_ function.
diff --git a/lib/pthread_safe.h b/lib/pthread_safe.h
index 0b2d7cdf..9518f3d1 100644
--- a/lib/pthread_safe.h
+++ b/lib/pthread_safe.h
@@ -39,4 +39,8 @@ extern strerror_t errtoa(int err, int len) ;
extern strerror_t errtoname(int err, int len) ;
extern strerror_t errtostr(int err, int len) ;
+extern strerror_t eaitoa(int eai, int err, int len) ;
+extern strerror_t eaitoname(int eai, int err, int len) ;
+extern strerror_t eaitostr(int eai, int err, int len) ;
+
#endif /* PTHREAD_SAFE_H_ */
diff --git a/lib/sockunion.c b/lib/sockunion.c
index 23287cd2..9a063190 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -209,7 +209,7 @@ sockunion_set_addr_any(sockunion su)
*
* For good measure, set the size (if that's required) and return same.
*/
-static int
+extern int
sockunion_set_port(sockunion su, in_port_t port)
{
switch (su->sa.sa_family)
@@ -234,6 +234,8 @@ sockunion_set_port(sockunion su, in_port_t port)
*
* Allocates a sockunion if required.
*
+ * Result is set "any".
+ *
* Advice is to zeroize sockaddr_in6, in particular.
*/
extern sockunion
@@ -455,7 +457,7 @@ sockunion_socket(sa_family_t family, int type, int protocol)
err = errno ;
zlog (NULL, LOG_WARNING,
- "Can't make socket family=%d, type=%d, protocol=%d: %s",
+ "Cannot make socket family=%d, type=%d, protocol=%d: %s",
(int)family, type, protocol, errtoa(err, 0).str) ;
errno = err ;
return -1;
@@ -908,6 +910,15 @@ sockunion_dup (union sockunion *su)
}
/*------------------------------------------------------------------------------
+ * Copy one sockunion to another
+ */
+extern void
+sockunion_copy (sockunion dst, sockunion src)
+{
+ memcpy (dst, src, sizeof(*dst)) ;
+} ;
+
+/*------------------------------------------------------------------------------
* Free given sockunion (if any).
*/
void
diff --git a/lib/sockunion.h b/lib/sockunion.h
index 3823c522..0b23ae63 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -101,6 +101,7 @@ struct sockunion_string
/* Prototypes. */
extern sockunion sockunion_init_new(sockunion su, sa_family_t family) ;
+extern int sockunion_set_port(sockunion su, in_port_t port) ;
extern int str2sockunion (const char *, union sockunion *);
extern const char *sockunion2str (union sockunion *, char *, size_t);
extern sockunion_string_t sutoa(sockunion su) ;
@@ -128,6 +129,7 @@ extern void sockunion_unmap_ipv4 (union sockunion *su) ;
extern void sockunion_map_ipv4 (union sockunion *su) ;
extern union sockunion *sockunion_dup (union sockunion *);
+extern void sockunion_copy (sockunion dst, sockunion src) ;
extern void sockunion_free (union sockunion *);
extern sockunion sockunion_new_prefix(sockunion su, prefix p) ;
diff --git a/lib/vty_io.c b/lib/vty_io.c
index af61e97c..c4b4f528 100644
--- a/lib/vty_io.c
+++ b/lib/vty_io.c
@@ -1983,7 +1983,7 @@ uty_serv_sock_addrinfo (const char *hostname, unsigned short port)
if (ret != 0)
{
- fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (ret));
+ fprintf (stderr, "getaddrinfo failed: %s\n", eaitoa(ret, errno, 0).str);
exit (1);
}