From ea7882c881de995b5c237609207ee58bcae40186 Mon Sep 17 00:00:00 2001 From: Marco van Wieringen Date: Thu, 19 Apr 2012 14:04:52 +0200 Subject: [PATCH] Add support for new POSIX getaddrinfo interface. Support the new POSIX getaddrinfo interface which replaces the dated gethostbyname interface which is obsoleted in the POSIX 1003.1 standard. When getaddrinfo is supported by the OS its the prefered interface as gethostbyname is not thread-safe and we don't support the gethostbyname_r thread-safe version of gethostbyname. The bnet code is changed to support the absolute mimimum set of the getaddrinfo interface replacing only the core functions normally performed by gethostbyname. We could add more generic support for the getaddrinfo function but have not for now. bstmp is changed to use the full getaddrinfo functionality so it now can also connect to ipv6 mailhost when getaddrinfo is used. Also all sprintf calls in bsmtp are replaced with snprintf calls. --- bacula/autoconf/configure.in | 52 +++++++++++++++ bacula/src/lib/bnet.c | 112 +++++++++++++++++++++++-------- bacula/src/lib/bsock.c | 2 +- bacula/src/tools/bsmtp.c | 124 ++++++++++++++++++++++++++--------- 4 files changed, 233 insertions(+), 57 deletions(-) diff --git a/bacula/autoconf/configure.in b/bacula/autoconf/configure.in index acbdf2f908..fb9bb57487 100644 --- a/bacula/autoconf/configure.in +++ b/bacula/autoconf/configure.in @@ -2415,6 +2415,58 @@ if test $ac_cv_struct_sockaddr_sa_len = yes; then AC_DEFINE(HAVE_SA_LEN, 1, [Define if sa_len field exists in struct sockaddr]) fi +dnl +dnl check for working getaddrinfo() +dnl +dnl Note that if the system doesn't have gai_strerror(), we +dnl can't use getaddrinfo() because we can't get strings +dnl describing the error codes. +dnl +AC_CACHE_CHECK(for working getaddrinfo, ac_cv_working_getaddrinfo, + [ + AC_TRY_RUN( + [ + #include + #include + #include + #include + + void main(void) { + struct addrinfo hints, *ai; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo("127.0.0.1", NULL, &hints, &ai); + if (error) { + exit(1); + } + if (ai->ai_addr->sa_family != AF_INET) { + exit(1); + } + exit(0); + } + ],[ + ac_cv_working_getaddrinfo="yes" + ],[ + ac_cv_working_getaddrinfo="no" + ],[ + ac_cv_working_getaddrinfo="yes" + ] + ) + ] +) +AC_CHECK_FUNC(gai_strerror, [AC_DEFINE(HAVE_GAI_STRERROR, 1, [Define to 1 if you have the 'gai_strerror' function.])]) + +if test "$ac_cv_working_getaddrinfo" = "yes"; then + if test "$ac_cv_func_gai_strerror" != "yes"; then + ac_cv_working_getaddrinfo="no" + else + AC_DEFINE(HAVE_GETADDRINFO, 1, [Define to 1 if getaddrinfo exists and works]) + fi +fi + AC_FUNC_STRFTIME AC_FUNC_VPRINTF AC_FUNC_ALLOCA diff --git a/bacula/src/lib/bnet.c b/bacula/src/lib/bnet.c index b2a63b9c6c..1e8b8f4d3e 100644 --- a/bacula/src/lib/bnet.c +++ b/bacula/src/lib/bnet.c @@ -35,7 +35,6 @@ * */ - #include "bacula.h" #include "jcr.h" #include @@ -54,14 +53,15 @@ #define socketClose(fd) close(fd) #endif +#ifndef HAVE_GETADDRINFO static pthread_mutex_t ip_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif /* * Read a nbytes from the network. * It is possible that the total bytes require in several * read requests */ - int32_t read_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) { int32_t nleft, nread; @@ -408,6 +408,64 @@ int bnet_wait_data_intr(BSOCK * bsock, int sec) #define NO_DATA 4 /* Valid name, no data record of requested type. */ #endif +#if HAVE_GETADDRINFO +const char *resolv_host(int family, const char *host, dlist *addr_list) +{ + int res; + struct addrinfo hints; + struct addrinfo *ai, *rp; + IPADDR *addr; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = family; + hints.ai_socktype = 0; + hints.ai_protocol = 0; + hints.ai_flags = 0; + + res = getaddrinfo(host, NULL, &hints, &ai); + if (res != 0) { + return gai_strerror(res); + } + + for (rp = ai; rp != NULL; rp = rp->ai_next) { + switch (rp->ai_addr->sa_family) { + case AF_INET: + addr = New(IPADDR(rp->ai_addr->sa_family)); + addr->set_type(IPADDR::R_MULTIPLE); + /* + * Some serious casting to get the struct in_addr * + * rp->ai_addr == struct sockaddr + * as this is AF_INET family we can cast that + * to struct_sockaddr_in. Of that we need the + * address of the sin_addr member which contains a + * struct in_addr + */ + addr->set_addr4(&(((struct sockaddr_in *)rp->ai_addr)->sin_addr)); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + addr = New(IPADDR(rp->ai_addr->sa_family)); + addr->set_type(IPADDR::R_MULTIPLE); + /* + * Some serious casting to get the struct in6_addr * + * rp->ai_addr == struct sockaddr + * as this is AF_INET6 family we can cast that + * to struct_sockaddr_in6. Of that we need the + * address of the sin6_addr member which contains a + * struct in6_addr + */ + addr->set_addr6(&(((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr)); + break; +#endif + default: + continue; + } + addr_list->append(addr); + } + freeaddrinfo(ai); + return NULL; +} +#else /* * Get human readable error for gethostbyname() */ @@ -440,21 +498,12 @@ static const char *gethost_strerror() return msg; } - - - -static IPADDR *add_any(int family) -{ - IPADDR *addr = New(IPADDR(family)); - addr->set_type(IPADDR::R_MULTIPLE); - addr->set_addr_any(); - return addr; -} - -static const char *resolv_host(int family, const char *host, dlist * addr_list) +static const char *resolv_host(int family, const char *host, dlist *addr_list) { struct hostent *hp; const char *errmsg; + char **p; + IPADDR *addr; P(ip_mutex); /* gethostbyname() is not thread safe */ #ifdef HAVE_GETHOSTBYNAME2 @@ -467,24 +516,38 @@ static const char *resolv_host(int family, const char *host, dlist * addr_list) V(ip_mutex); return errmsg; } else { - char **p; for (p = hp->h_addr_list; *p != 0; p++) { - IPADDR *addr = New(IPADDR(hp->h_addrtype)); - addr->set_type(IPADDR::R_MULTIPLE); - if (addr->get_family() == AF_INET) { - addr->set_addr4((struct in_addr*)*p); - } + switch (hp->h_addrtype) { + case AF_INET: + addr = New(IPADDR(hp->h_addrtype)); + addr->set_type(IPADDR::R_MULTIPLE); + addr->set_addr4((struct in_addr *)*p); + break; #ifdef HAVE_IPV6 - else { - addr->set_addr6((struct in6_addr*)*p); - } + case AF_INET6: + addr = New(IPADDR(hp->h_addrtype)); + addr->set_type(IPADDR::R_MULTIPLE); + addr->set_addr6((struct in6_addr *)*p); + break; #endif + default: + continue; + } addr_list->append(addr); } V(ip_mutex); } return NULL; } +#endif + +static IPADDR *add_any(int family) +{ + IPADDR *addr = New(IPADDR(family)); + addr->set_type(IPADDR::R_MULTIPLE); + addr->set_addr_any(); + return addr; +} /* * i host = 0 mean INADDR_ANY only ipv4 @@ -569,8 +632,6 @@ BSOCK *bnet_connect(JCR * jcr, int retry_interval, utime_t max_retry_time, return bsock; } - - /* * Return the string for the error that occurred * on the socket. Only the first error is retained. @@ -654,7 +715,6 @@ void bnet_restore_blocking (BSOCK *bsock, int flags) bsock->restore_blocking(flags); } - /* * Send a network "signal" to the other end * This consists of sending a negative packet length diff --git a/bacula/src/lib/bsock.c b/bacula/src/lib/bsock.c index 2fee52365d..845e63ecff 100644 --- a/bacula/src/lib/bsock.c +++ b/bacula/src/lib/bsock.c @@ -199,7 +199,7 @@ bool BSOCK::open(JCR *jcr, const char *name, char *host, char *service, */ if ((addr_list = bnet_host2ipaddrs(host, 0, &errstr)) == NULL) { /* Note errstr is not malloc'ed */ - Qmsg2(jcr, M_ERROR, 0, _("gethostbyname() for host \"%s\" failed: ERR=%s\n"), + Qmsg2(jcr, M_ERROR, 0, _("bnet_host2ipaddrs() for host \"%s\" failed: ERR=%s\n"), host, errstr); Dmsg2(100, "bnet_host2ipaddrs() for host %s failed: ERR=%s\n", host, errstr); diff --git a/bacula/src/tools/bsmtp.c b/bacula/src/tools/bsmtp.c index 2cbf007b29..c8f90768c4 100644 --- a/bacula/src/tools/bsmtp.c +++ b/bacula/src/tools/bsmtp.c @@ -63,12 +63,8 @@ copyright. See: http://archives.neohapsis.com/archives/postfix/2000-05/1520.html - - Version $Id$ - */ - #include "bacula.h" #include "jcr.h" #define MY_NAME "bsmtp" @@ -77,9 +73,13 @@ #include #endif -/* Dummy functions */ +/* + * Dummy functions + */ int generate_daemon_event(JCR *jcr, const char *event) - { return 1; } +{ + return 1; +} #ifndef MAXSTRING #define MAXSTRING 254 @@ -202,10 +202,9 @@ _("\n" /* * Return the offset west from localtime to UTC in minutes - * Same as timezone.tz_minuteswest - * Unix tz_offset coded by: Attila Fülöp - */ - + * Same as timezone.tz_minuteswest + * Unix tz_offset coded by: Attila Fülöp + */ static long tz_offset(time_t lnow, struct tm &tm) { #if defined(HAVE_WIN32) @@ -247,31 +246,37 @@ static void get_date_string(char *buf, int buf_len) my_timezone = tz_offset(now, tm); strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S", &tm); - sprintf(tzbuf, " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60); + snprintf(tzbuf, sizeof(tzbuf), " %+2.2ld%2.2u", -my_timezone / 60, abs(my_timezone) % 60); strcat(buf, tzbuf); /* add +0100 */ strftime(tzbuf, sizeof(tzbuf), " (%Z)", &tm); strcat(buf, tzbuf); /* add (CEST) */ } - /********************************************************************* * * Program to send email */ int main (int argc, char *argv[]) { - char buf[1000]; - struct sockaddr_in sin; - struct hostent *hp; - int i, ch; - unsigned long maxlines, lines; + char buf[1000]; + int i, ch; + unsigned long maxlines, lines; #if defined(HAVE_WIN32) - SOCKET s; + SOCKET s; +#else + int s, r; + struct passwd *pwd; +#endif + char *cp, *p; +#ifdef HAVE_GETADDRINFO + int res; + struct addrinfo hints; + struct addrinfo *ai, *rp; + char mail_port[10]; #else - int s, r; - struct passwd *pwd; + struct hostent *hp; + struct sockaddr_in sin; #endif - char *cp, *p; setlocale(LC_ALL, "en_US"); bindtextdomain("bacula", LOCALEDIR); @@ -345,7 +350,6 @@ int main (int argc, char *argv[]) exit(1); } - /* * Determine SMTP server */ @@ -372,12 +376,28 @@ int main (int argc, char *argv[]) Pmsg1(0, _("Fatal gethostname error: ERR=%s\n"), strerror(errno)); exit(1); } +#ifdef HAVE_GETADDRINFO + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = 0; + hints.ai_protocol = 0; + hints.ai_flags = AI_CANONNAME; + + if ((res = getaddrinfo(my_hostname, NULL, &hints, &ai)) != 0) { + Pmsg2(0, _("Fatal getaddrinfo for myself failed \"%s\": ERR=%s\n"), + my_hostname, gai_strerror(res)); + exit(1); + } + strcpy(my_hostname, ai->ai_canonname); + freeaddrinfo(ai); +#else if ((hp = gethostbyname(my_hostname)) == NULL) { - Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), my_hostname, - strerror(errno)); + Pmsg2(0, _("Fatal gethostbyname for myself failed \"%s\": ERR=%s\n"), + my_hostname, strerror(errno)); exit(1); } strcpy(my_hostname, hp->h_name); +#endif Dmsg1(20, "My hostname is: %s\n", my_hostname); /* @@ -389,15 +409,15 @@ int main (int argc, char *argv[]) LPSTR lpszBuffer = (LPSTR)alloca(dwSize); if (GetUserName(lpszBuffer, &dwSize)) { - sprintf(buf, "%s@%s", lpszBuffer, my_hostname); + snprintf(buf, sizeof(buf), "%s@%s", lpszBuffer, my_hostname); } else { - sprintf(buf, "unknown-user@%s", my_hostname); + snprintf(buf, sizeof(buf), "unknown-user@%s", my_hostname); } #else if ((pwd = getpwuid(getuid())) == 0) { - sprintf(buf, "userid-%d@%s", (int)getuid(), my_hostname); + snprintf(buf, sizeof(buf), "userid-%d@%s", (int)getuid(), my_hostname); } else { - sprintf(buf, "%s@%s", pwd->pw_name, my_hostname); + snprintf(buf, sizeof(buf), "%s@%s", pwd->pw_name, my_hostname); } #endif from_addr = bstrdup(buf); @@ -407,14 +427,57 @@ int main (int argc, char *argv[]) /* * Connect to smtp daemon on mailhost. */ -hp: +lookup_host: +#ifdef HAVE_GETADDRINFO + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = 0; + snprintf(mail_port, sizeof(mail_port), "%d", mailport); + + if ((res = getaddrinfo(mailhost, mail_port, &hints, &ai)) != 0) { + Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), + mailhost, gai_strerror(res)); + if (!strcasecmp(mailhost, "localhost")) { + Pmsg0(0, _("Retrying connection using \"localhost\".\n")); + mailhost = "localhost"; + goto lookup_host; + } + exit(1); + } + + for (rp = ai; rp != NULL; rp = rp->ai_next) { +#if defined(HAVE_WIN32) + s = WSASocket(rp->ai_family, rp->ai_socktype, rp->ai_protocol, NULL, 0, 0); +#else + s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); +#endif + if (s < 0) { + continue; + } + + if (connect(s, rp->ai_addr, rp->ai_addrlen) != -1) { + break; + } + + close(s); + } + + if (!rp) { + Pmsg1(0, _("Failed to connect to mailhost %s\n"), mailhost); + exit(1); + } + + freeaddrinfo(ai); +#else if ((hp = gethostbyname(mailhost)) == NULL) { Pmsg2(0, _("Error unknown mail host \"%s\": ERR=%s\n"), mailhost, strerror(errno)); if (strcasecmp(mailhost, "localhost") != 0) { Pmsg0(0, _("Retrying connection using \"localhost\".\n")); mailhost = "localhost"; - goto hp; + goto lookup_host; } exit(1); } @@ -443,6 +506,7 @@ hp: exit(1); } Dmsg0(20, "Connected\n"); +#endif #if defined(HAVE_WIN32) int fdSocket = _open_osfhandle(s, _O_RDWR | _O_BINARY); -- 2.39.5