diff options
author | Chris Hall <GMCH@hestia.halldom.com> | 2010-04-11 11:17:37 +0100 |
---|---|---|
committer | Chris Hall <GMCH@hestia.halldom.com> | 2010-04-11 11:17:37 +0100 |
commit | b878aa88eb8bb7ab74e24314e87c88586f15598d (patch) | |
tree | b51690eff9070c419dbe99602b9a974d82f8aaed | |
parent | 83447a051fbcc88b33fcea6670520687668d3ba1 (diff) | |
download | quagga-b878aa88eb8bb7ab74e24314e87c88586f15598d.tar.bz2 quagga-b878aa88eb8bb7ab74e24314e87c88586f15598d.tar.xz |
Support gai_strerror() and tidy bgp_listener code.
Added support for EAI_XXX error names and gai_strerror() error
messages.
Tidied up bgp_listener set up to remove #if skips around the
"old" way -- so that the older code doesn't simply rot away.
-rw-r--r-- | bgpd/bgp_network.c | 345 | ||||
-rw-r--r-- | lib/errno_names.c | 60 | ||||
-rw-r--r-- | lib/errno_names.h | 5 | ||||
-rw-r--r-- | lib/pthread_safe.c | 249 | ||||
-rw-r--r-- | lib/pthread_safe.h | 4 | ||||
-rw-r--r-- | lib/sockunion.c | 15 | ||||
-rw-r--r-- | lib/sockunion.h | 2 | ||||
-rw-r--r-- | lib/vty_io.c | 2 |
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); } |