X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Flib%2Fbnet.c;h=584388d1656f71fdf0224ddb05764bbd81f020e8;hb=10cfd798ced2d27f61ead2de6fe9b1bcc8e3468d;hp=8eca9c9464b781abc062f281029f76cf8aa4bc85;hpb=6578d8b841a889a842f1bd3d4e5d10201d81e492;p=bacula%2Fbacula diff --git a/bacula/src/lib/bnet.c b/bacula/src/lib/bnet.c index 8eca9c9464..584388d165 100644 --- a/bacula/src/lib/bnet.c +++ b/bacula/src/lib/bnet.c @@ -1,3 +1,21 @@ +/* + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2018 Kern Sibbald + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. +*/ /* * Network Utility Routines * @@ -6,26 +24,6 @@ * Adapted and enhanced for Bacula, originally written * for inclusion in the Apcupsd package * - * Version $Id$ - */ -/* - Copyright (C) 2000-2004 Kern Sibbald - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, - MA 02111-1307, USA. - */ @@ -33,27 +31,25 @@ #include "jcr.h" #include -extern time_t watchdog_time; - #ifndef INADDR_NONE -#define INADDR_NONE -1 -#endif - -#ifndef ENODATA /* not defined on BSD systems */ -#define ENODATA EPIPE +#define INADDR_NONE -1 #endif #ifdef HAVE_WIN32 +#undef inet_pton +#define inet_pton binet_pton #define socketRead(fd, buf, len) recv(fd, buf, len, 0) #define socketWrite(fd, buf, len) send(fd, buf, len, 0) -#define socketClose(fd) closesocket(fd) +#define socketClose(fd) closesocket(fd) #else #define socketRead(fd, buf, len) read(fd, buf, len) #define socketWrite(fd, buf, len) write(fd, buf, len) -#define socketClose(fd) close(fd) +#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. @@ -61,26 +57,62 @@ static pthread_mutex_t ip_mutex = PTHREAD_MUTEX_INITIALIZER; * read requests */ -static int32_t read_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) +int32_t read_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) { int32_t nleft, nread; +#ifdef HAVE_TLS + if (bsock->tls) { + /* TLS enabled */ + return (tls_bsock_readn(bsock, ptr, nbytes)); + } +#endif /* HAVE_TLS */ + nleft = nbytes; while (nleft > 0) { - do { - errno = 0; - nread = socketRead(bsock->fd, ptr, nleft); - if (bsock->timed_out || bsock->terminated) { - return nread; - } - } while (nread == -1 && (errno == EINTR || errno == EAGAIN)); + errno = 0; + nread = socketRead(bsock->m_fd, ptr, nleft); + if (bsock->is_timed_out() || bsock->is_terminated()) { + return -1; + } + +#ifdef HAVE_WIN32 + /* + * We simulate errno on Windows for a socket + * error in order to handle errors correctly. + */ + if (nread == SOCKET_ERROR) { + DWORD err = WSAGetLastError(); + nread = -1; + if (err == WSAEINTR) { + errno = EINTR; + } else if (err == WSAEWOULDBLOCK) { + errno = EAGAIN; + } else { + errno = EIO; /* some other error */ + } + } +#endif + + if (nread == -1) { + if (errno == EINTR) { + continue; + } + if (errno == EAGAIN) { + bmicrosleep(0, 20000); /* try again in 20ms */ + continue; + } + } if (nread <= 0) { - return nread; /* error, or EOF */ + return -1; /* error, or EOF */ } nleft -= nread; ptr += nread; + if (bsock->use_bwlimit()) { + bsock->control_bwlimit(nread); + } } - return nbytes - nleft; /* return >= 0 */ + return nbytes - nleft; /* return >= 0 */ } /* @@ -88,484 +120,304 @@ static int32_t read_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) * It may require several writes. */ -static int32_t write_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) +int32_t write_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) { int32_t nleft, nwritten; - if (bsock->spool) { - nwritten = fwrite(ptr, 1, nbytes, bsock->spool_fd); + if (bsock->is_spooling()) { + nwritten = fwrite(ptr, 1, nbytes, bsock->m_spool_fd); if (nwritten != nbytes) { - berrno be; - bsock->b_errno = errno; - Qmsg1(bsock->jcr, M_FATAL, 0, _("Attr spool write error. ERR=%s\n"), - be.strerror()); + berrno be; + bsock->b_errno = errno; + Qmsg3(bsock->jcr(), M_FATAL, 0, _("Attr spool write error. wrote=%d wanted=%d bytes. ERR=%s\n"), + nbytes, nwritten, be.bstrerror()); Dmsg2(400, "nwritten=%d nbytes=%d.\n", nwritten, nbytes); - errno = bsock->b_errno; - return -1; + errno = bsock->b_errno; + return -1; } return nbytes; } + +#ifdef HAVE_TLS + if (bsock->tls) { + /* TLS enabled */ + return (tls_bsock_writen(bsock, ptr, nbytes)); + } +#endif /* HAVE_TLS */ + nleft = nbytes; while (nleft > 0) { do { - errno = 0; - nwritten = socketWrite(bsock->fd, ptr, nleft); - if (bsock->timed_out || bsock->terminated) { - return nwritten; - } + errno = 0; + nwritten = socketWrite(bsock->m_fd, ptr, nleft); + if (bsock->is_timed_out() || bsock->is_terminated()) { + return -1; + } + +#ifdef HAVE_WIN32 + /* + * We simulate errno on Windows for a socket + * error in order to handle errors correctly. + */ + if (nwritten == SOCKET_ERROR) { + DWORD err = WSAGetLastError(); + nwritten = -1; + if (err == WSAEINTR) { + errno = EINTR; + } else if (err == WSAEWOULDBLOCK) { + errno = EAGAIN; + } else { + errno = EIO; /* some other error */ + } + } +#endif + } while (nwritten == -1 && errno == EINTR); /* * If connection is non-blocking, we will get EAGAIN, so - * use select() to keep from consuming all the CPU + * use select()/poll to keep from consuming all the CPU * and try again. */ if (nwritten == -1 && errno == EAGAIN) { - fd_set fdset; - struct timeval tv; - - FD_ZERO(&fdset); - FD_SET((unsigned)bsock->fd, &fdset); - tv.tv_sec = 10; - tv.tv_usec = 0; - select(bsock->fd + 1, NULL, &fdset, NULL, &tv); - continue; + fd_wait_data(bsock->m_fd, WAIT_WRITE, 1, 0); + continue; } if (nwritten <= 0) { - return nwritten; /* error */ + return -1; /* error */ } nleft -= nwritten; ptr += nwritten; + if (bsock->use_bwlimit()) { + bsock->control_bwlimit(nwritten); + } } return nbytes - nleft; } /* - * Receive a message from the other end. Each message consists of - * two packets. The first is a header that contains the size - * of the data that follows in the second packet. - * Returns number of bytes read (may return zero) - * Returns -1 on signal (BNET_SIGNAL) - * Returns -2 on hard end of file (BNET_HARDEOF) - * Returns -3 on error (BNET_ERROR) - * - * Unfortunately, it is a bit complicated because we have these - * four return types: - * 1. Normal data - * 2. Signal including end of data stream - * 3. Hard end of file - * 4. Error - * Using is_bnet_stop() and is_bnet_error() you can figure this all out. + * Establish a TLS connection -- server side + * Returns: true on success + * false on failure */ -int32_t bnet_recv(BSOCK * bsock) +#ifdef HAVE_TLS +bool bnet_tls_server(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list) { - int32_t nbytes; - int32_t pktsiz; - - ASSERT(bsock != NULL); - bsock->msg[0] = 0; - bsock->msglen = 0; - if (bsock->errors || bsock->terminated) { - return BNET_HARDEOF; - } + TLS_CONNECTION *tls; + JCR *jcr = bsock->jcr(); - bsock->read_seqno++; /* bump sequence number */ - bsock->timer_start = watchdog_time; /* set start wait time */ - bsock->timed_out = 0; - /* get data size -- in int32_t */ - if ((nbytes = read_nbytes(bsock, (char *)&pktsiz, sizeof(int32_t))) <= 0) { - bsock->timer_start = 0; /* clear timer */ - /* probably pipe broken because client died */ - if (errno == 0) { - bsock->b_errno = ENODATA; - } else { - bsock->b_errno = errno; - } - bsock->errors++; - return BNET_HARDEOF; /* assume hard EOF received */ - } - bsock->timer_start = 0; /* clear timer */ - if (nbytes != sizeof(int32_t)) { - bsock->errors++; - bsock->b_errno = EIO; - Qmsg5(bsock->jcr, M_ERROR, 0, _("Read expected %d got %d from %s:%s:%d\n"), - sizeof(int32_t), nbytes, bsock->who, bsock->host, bsock->port); - return BNET_ERROR; + tls = new_tls_connection(ctx, bsock->m_fd); + if (!tls) { + Qmsg0(bsock->jcr(), M_FATAL, 0, _("TLS connection initialization failed.\n")); + return false; } - pktsiz = ntohl(pktsiz); /* decode no. of bytes that follow */ + bsock->tls = tls; - if (pktsiz == 0) { /* No data transferred */ - bsock->timer_start = 0; /* clear timer */ - bsock->in_msg_no++; - bsock->msglen = 0; - return 0; /* zero bytes read */ + /* Initiate TLS Negotiation */ + if (!tls_bsock_accept(bsock)) { + Qmsg0(bsock->jcr(), M_FATAL, 0, _("TLS Negotiation failed.\n")); + goto err; } - /* If signal or packet size too big */ - if (pktsiz < 0 || pktsiz > 1000000) { - if (pktsiz > 0) { /* if packet too big */ - Qmsg3(bsock->jcr, M_FATAL, 0, - _("Packet size too big from \"%s:%s:%d. Terminating connection.\n"), - bsock->who, bsock->host, bsock->port); - pktsiz = BNET_TERMINATE; /* hang up */ + if (verify_list) { + if (!tls_postconnect_verify_cn(jcr, tls, verify_list)) { + Qmsg1(bsock->jcr(), M_FATAL, 0, _("TLS certificate verification failed." + " Peer certificate did not match a required commonName\n"), + bsock->host()); + goto err; } - if (pktsiz == BNET_TERMINATE) { - bsock->terminated = 1; - } - bsock->timer_start = 0; /* clear timer */ - bsock->b_errno = ENODATA; - bsock->msglen = pktsiz; /* signal code */ - return BNET_SIGNAL; /* signal */ - } - - /* Make sure the buffer is big enough + one byte for EOS */ - if (pktsiz >= (int32_t) sizeof_pool_memory(bsock->msg)) { - bsock->msg = realloc_pool_memory(bsock->msg, pktsiz + 100); } + Dmsg0(50, "TLS server negotiation established.\n"); + return true; - bsock->timer_start = watchdog_time; /* set start wait time */ - bsock->timed_out = 0; - /* now read the actual data */ - if ((nbytes = read_nbytes(bsock, bsock->msg, pktsiz)) <= 0) { - bsock->timer_start = 0; /* clear timer */ - if (errno == 0) { - bsock->b_errno = ENODATA; - } else { - bsock->b_errno = errno; - } - bsock->errors++; - Qmsg4(bsock->jcr, M_ERROR, 0, _("Read error from %s:%s:%d: ERR=%s\n"), - bsock->who, bsock->host, bsock->port, bnet_strerror(bsock)); - return BNET_ERROR; - } - bsock->timer_start = 0; /* clear timer */ - bsock->in_msg_no++; - bsock->msglen = nbytes; - if (nbytes != pktsiz) { - bsock->b_errno = EIO; - bsock->errors++; - Qmsg5(bsock->jcr, M_ERROR, 0, _("Read expected %d got %d from %s:%s:%d\n"), - pktsiz, nbytes, bsock->who, bsock->host, bsock->port); - return BNET_ERROR; - } - /* always add a zero by to properly terminate any - * string that was send to us. Note, we ensured above that the - * buffer is at least one byte longer than the message length. - */ - bsock->msg[nbytes] = 0; /* terminate in case it is a string */ - sm_check(__FILE__, __LINE__, false); - return nbytes; /* return actual length of message */ -} - - -/* - * Return 1 if there are errors on this bsock or it is closed, - * i.e. stop communicating on this line. - */ -bool is_bnet_stop(BSOCK * bsock) -{ - return bsock->errors || bsock->terminated; -} - -/* - * Return number of errors on socket - */ -int is_bnet_error(BSOCK * bsock) -{ - return bsock->errors; -} - -/* - * Call here after error during closing to suppress error - * messages which are due to the other end shutting down too. - */ -void bnet_suppress_error_messages(BSOCK * bsock, bool flag) -{ - bsock->suppress_error_msgs = flag; -} - - -/* - * Transmit spooled data now to a BSOCK - */ -int bnet_despool_to_bsock(BSOCK * bsock, void update_attr_spool_size(ssize_t size), - ssize_t tsize) -{ - int32_t pktsiz; - size_t nbytes; - ssize_t last = 0, size = 0; - int count = 0; - - rewind(bsock->spool_fd); - while (fread((char *)&pktsiz, 1, sizeof(int32_t), bsock->spool_fd) == - sizeof(int32_t)) { - size += sizeof(int32_t); - bsock->msglen = ntohl(pktsiz); - if (bsock->msglen > 0) { - if (bsock->msglen > (int32_t) sizeof_pool_memory(bsock->msg)) { - bsock->msg = realloc_pool_memory(bsock->msg, bsock->msglen + 1); - } - nbytes = fread(bsock->msg, 1, bsock->msglen, bsock->spool_fd); - if (nbytes != (size_t) bsock->msglen) { - berrno be; - Dmsg2(400, "nbytes=%d msglen=%d\n", nbytes, bsock->msglen); - Qmsg1(bsock->jcr, M_FATAL, 0, _("fread attr spool error. ERR=%s\n"), - be.strerror()); - update_attr_spool_size(tsize - last); - return 0; - } - size += nbytes; - if ((++count & 0x3F) == 0) { - update_attr_spool_size(size - last); - last = size; - } - } - bnet_send(bsock); - } - update_attr_spool_size(tsize - last); - if (ferror(bsock->spool_fd)) { - berrno be; - Qmsg1(bsock->jcr, M_FATAL, 0, _("fread attr spool error. ERR=%s\n"), - be.strerror()); - return 0; - } - return 1; +err: + free_tls_connection(tls); + bsock->tls = NULL; + return false; } - /* - * Send a message over the network. The send consists of - * two network packets. The first is sends a 32 bit integer containing - * the length of the data packet which follows. - * - * Returns: false on failure - * true on success + * Establish a TLS connection -- client side + * Returns: true on success + * false on failure */ -bool bnet_send(BSOCK * bsock) +bool bnet_tls_client(TLS_CONTEXT *ctx, BSOCK *bsock, alist *verify_list) { - int32_t rc; - int32_t pktsiz; + TLS_CONNECTION *tls; + JCR *jcr = bsock->jcr(); - if (bsock->errors || bsock->terminated || bsock->msglen > 1000000) { - return false; - } - pktsiz = htonl((int32_t) bsock->msglen); - /* send int32_t containing size of data packet */ - bsock->timer_start = watchdog_time; /* start timer */ - bsock->timed_out = 0; - rc = write_nbytes(bsock, (char *)&pktsiz, sizeof(int32_t)); - bsock->timer_start = 0; /* clear timer */ - if (rc != sizeof(int32_t)) { - if (bsock->msglen == BNET_TERMINATE) { /* if we were terminating */ - bsock->terminated = 1; - return false; /* ignore any errors */ - } - bsock->errors++; - if (errno == 0) { - bsock->b_errno = EIO; - } else { - bsock->b_errno = errno; - } - if (rc < 0) { - if (!bsock->suppress_error_msgs && !bsock->timed_out) { - Qmsg4(bsock->jcr, M_ERROR, 0, - _("Write error sending to %s:%s:%d: ERR=%s\n"), bsock->who, - bsock->host, bsock->port, bnet_strerror(bsock)); - } - } else { - Qmsg5(bsock->jcr, M_ERROR, 0, - _("Wrote %d bytes to %s:%s:%d, but only %d accepted.\n"), bsock->who, - bsock->host, bsock->port, bsock->msglen, rc); - } + tls = new_tls_connection(ctx, bsock->m_fd); + if (!tls) { + Qmsg0(bsock->jcr(), M_FATAL, 0, _("TLS connection initialization failed.\n")); return false; } - bsock->out_msg_no++; /* increment message number */ - if (bsock->msglen <= 0) { /* length only? */ - return true; /* yes, no data */ + bsock->tls = tls; + + /* Initiate TLS Negotiation */ + if (!tls_bsock_connect(bsock)) { + goto err; } - /* send data packet */ - bsock->timer_start = watchdog_time; /* start timer */ - bsock->timed_out = 0; - rc = write_nbytes(bsock, bsock->msg, bsock->msglen); - bsock->timer_start = 0; /* clear timer */ - if (rc != bsock->msglen) { - bsock->errors++; - if (errno == 0) { - bsock->b_errno = EIO; - } else { - bsock->b_errno = errno; + /* If there's an Allowed CN verify list, use that to validate the remote + * certificate's CN. Otherwise, we use standard host/CN matching. */ + if (verify_list) { + if (!tls_postconnect_verify_cn(jcr, tls, verify_list)) { + Qmsg1(bsock->jcr(), M_FATAL, 0, _("TLS certificate verification failed." + " Peer certificate did not match a required commonName\n"), + bsock->host()); + goto err; } - if (rc < 0) { - if (!bsock->suppress_error_msgs) { - Qmsg4(bsock->jcr, M_ERROR, 0, - _("Write error sending to %s:%s:%d: ERR=%s\n"), bsock->who, - bsock->host, bsock->port, bnet_strerror(bsock)); - } - } else { - Qmsg5(bsock->jcr, M_ERROR, 0, - _("Wrote %d bytes to %s:%s:%d, but only %d accepted.\n"), - bsock->msglen, bsock->who, bsock->host, bsock->port, rc); + } else if (!tls_postconnect_verify_host(jcr, tls, bsock->host())) { + /* If host is 127.0.0.1, try localhost */ + if (strcmp(bsock->host(), "127.0.0.1") != 0 || + !tls_postconnect_verify_host(jcr, tls, "localhost")) { + Qmsg1(bsock->jcr(), M_FATAL, 0, _("TLS host certificate verification failed. Host name \"%s\" did not match presented certificate\n"), + bsock->host()); + goto err; } - return false; } + Dmsg0(50, "TLS client negotiation established.\n"); return true; -} -/* - * Establish an SSL connection -- server side - * Codes that ssl_need and ssl_has can take - * BNET_SSL_NONE I cannot do ssl - * BNET_SSL_OK I can do ssl, but it is not required on my end - * BNET_SSL_REQUIRED ssl is required on my end - */ -int bnet_ssl_server(BSOCK * bsock, char *password, int ssl_need, int ssl_has) -{ - /* Check to see if what we need (ssl_need) corresponds to what he has (ssl_has) */ - /* The other side expects a response from us */ - return 1; +err: + free_tls_connection(tls); + bsock->tls = NULL; + return false; } +#else -/* - * Establish an SSL connection -- client side - */ -int bnet_ssl_client(BSOCK * bsock, char *password, int ssl_need) +bool bnet_tls_server(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list) { - /* We are the client so we must wait for the server to notify us */ - return 1; + Jmsg(bsock->jcr(), M_ABORT, 0, _("TLS enabled but not configured.\n")); + return false; } - -/* - * Wait for a specified time for data to appear on - * the BSOCK connection. - * - * Returns: 1 if data available - * 0 if timeout - * -1 if error - */ -int bnet_wait_data(BSOCK * bsock, int sec) +bool bnet_tls_client(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list) { - fd_set fdset; - struct timeval tv; - - FD_ZERO(&fdset); - FD_SET((unsigned)bsock->fd, &fdset); - tv.tv_sec = sec; - tv.tv_usec = 0; - for (;;) { - switch (select(bsock->fd + 1, &fdset, NULL, NULL, &tv)) { - case 0: /* timeout */ - bsock->b_errno = 0; - return 0; - case -1: - bsock->b_errno = errno; - if (errno == EINTR || errno == EAGAIN) { - continue; - } - return -1; /* error return */ - default: - bsock->b_errno = 0; - return 1; - } - } + Jmsg(bsock->jcr(), M_ABORT, 0, _("TLS enable but not configured.\n")); + return false; } -/* - * As above, but returns on interrupt - */ -int bnet_wait_data_intr(BSOCK * bsock, int sec) -{ - fd_set fdset; - struct timeval tv; - - FD_ZERO(&fdset); - FD_SET((unsigned)bsock->fd, &fdset); - tv.tv_sec = sec; - tv.tv_usec = 0; - for (;;) { - switch (select(bsock->fd + 1, &fdset, NULL, NULL, &tv)) { - case 0: /* timeout */ - bsock->b_errno = 0; - return 0; - case -1: - bsock->b_errno = errno; - return -1; /* error return */ - default: - bsock->b_errno = 0; - return 1; - } - } -} +#endif /* HAVE_TLS */ #ifndef NETDB_INTERNAL -#define NETDB_INTERNAL -1 /* See errno. */ +#define NETDB_INTERNAL -1 /* See errno. */ #endif #ifndef NETDB_SUCCESS -#define NETDB_SUCCESS 0 /* No problem. */ +#define NETDB_SUCCESS 0 /* No problem. */ #endif #ifndef HOST_NOT_FOUND -#define HOST_NOT_FOUND 1 /* Authoritative Answer Host not found. */ +#define HOST_NOT_FOUND 1 /* Authoritative Answer Host not found. */ #endif #ifndef TRY_AGAIN -#define TRY_AGAIN 2 /* Non-Authoritative Host not found, or SERVERFAIL. */ +#define TRY_AGAIN 2 /* Non-Authoritative Host not found, or SERVERFAIL. */ #endif #ifndef NO_RECOVERY -#define NO_RECOVERY 3 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP. */ +#define NO_RECOVERY 3 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP. */ #endif #ifndef NO_DATA -#define NO_DATA 4 /* Valid name, no data record of requested type. */ +#define NO_DATA 4 /* Valid name, no data record of requested type. */ #endif +#if defined(HAVE_GETADDRINFO) +/* + * getaddrinfo.c - Simple example of using getaddrinfo(3) function. + * + * Michal Ludvig (c) 2002, 2003 + * http://www.logix.cz/michal/devel/ + * + * License: public domain. + */ +const char *resolv_host(int family, const char *host, dlist *addr_list) +{ + IPADDR *ipaddr; + struct addrinfo hints, *res, *rp; + int errcode; + //char addrstr[100]; + void *ptr; + + memset (&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + //hints.ai_flags |= AI_CANONNAME; + + errcode = getaddrinfo (host, NULL, &hints, &res); + if (errcode != 0) return gai_strerror(errcode); + + for (rp=res; res; res=res->ai_next) { + //inet_ntop (res->ai_family, res->ai_addr->sa_data, addrstr, 100); + switch (res->ai_family) { + case AF_INET: + ipaddr = New(IPADDR(rp->ai_addr->sa_family)); + ipaddr->set_type(IPADDR::R_MULTIPLE); + ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr; + ipaddr->set_addr4((in_addr *)ptr); + break; +#if defined(HAVE_IPV6) + case AF_INET6: + ipaddr = New(IPADDR(rp->ai_addr->sa_family)); + ipaddr->set_type(IPADDR::R_MULTIPLE); + ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; + ipaddr->set_addr6((in6_addr *)ptr); + break; +#endif + default: + continue; + } + //inet_ntop (res->ai_family, ptr, addrstr, 100); + //Pmsg3(000, "IPv%d address: %s (%s)\n", res->ai_family == PF_INET6 ? 6 : 4, + // addrstr, res->ai_canonname); + addr_list->append(ipaddr); + } + freeaddrinfo(rp); + return NULL; +} + +#else + /* * Get human readable error for gethostbyname() */ static const char *gethost_strerror() { const char *msg; + berrno be; switch (h_errno) { case NETDB_INTERNAL: - msg = strerror(errno); + msg = be.bstrerror(); break; case NETDB_SUCCESS: - msg = "No problem."; + msg = _("No problem."); break; case HOST_NOT_FOUND: - msg = "Authoritative answer for host not found."; + msg = _("Authoritative answer for host not found."); break; case TRY_AGAIN: - msg = "Non-authoritative for host not found, or ServerFail."; + msg = _("Non-authoritative for host not found, or ServerFail."); break; case NO_RECOVERY: - msg = "Non-recoverable errors, FORMERR, REFUSED, or NOTIMP."; + msg = _("Non-recoverable errors, FORMERR, REFUSED, or NOTIMP."); break; case NO_DATA: - msg = "Valid name, no data record of resquested type."; + msg = _("Valid name, no data record of resquested type."); break; default: - msg = "Unknown error."; + msg = _("Unknown error."); } 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; -} - +/* + * Note: this is the old way of resolving a host + * that does not use the new getaddrinfo() above. + */ static const char *resolv_host(int family, const char *host, dlist * addr_list) { struct hostent *hp; const char *errmsg; - P(ip_mutex); + P(ip_mutex); /* gethostbyname() is not thread safe */ #ifdef HAVE_GETHOSTBYNAME2 if ((hp = gethostbyname2(host, family)) == NULL) { #else @@ -578,25 +430,34 @@ static const char *resolv_host(int family, const char *host, dlist * addr_list) } 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); - } + 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); + } #ifdef HAVE_IPV6 - else { - addr->set_addr6((struct in6_addr*)*p); - } + else { + addr->set_addr6((struct in6_addr*)*p); + } #endif - addr_list->append(addr); + 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 + * i host = 0 means INADDR_ANY only for IPv4 */ dlist *bnet_host2ipaddrs(const char *host, int family, const char **errstr) { @@ -610,11 +471,11 @@ dlist *bnet_host2ipaddrs(const char *host, int family, const char **errstr) dlist *addr_list = New(dlist(addr, &addr->link)); if (!host || host[0] == '\0') { if (family != 0) { - addr_list->append(add_any(family)); + addr_list->append(add_any(family)); } else { - addr_list->append(add_any(AF_INET)); + addr_list->append(add_any(AF_INET)); #ifdef HAVE_IPV6 - addr_list->append(add_any(AF_INET6)); + addr_list->append(add_any(AF_INET6)); #endif } } else if (inet_aton(host, &inaddr)) { /* MA Bug 4 */ @@ -622,404 +483,206 @@ dlist *bnet_host2ipaddrs(const char *host, int family, const char **errstr) addr->set_type(IPADDR::R_MULTIPLE); addr->set_addr4(&inaddr); addr_list->append(addr); - } else #ifdef HAVE_IPV6 - if (inet_pton(AF_INET6, host, &inaddr6) > 1) { + } else if (inet_pton(AF_INET6, host, &inaddr6) == 1) { addr = New(IPADDR(AF_INET6)); addr->set_type(IPADDR::R_MULTIPLE); addr->set_addr6(&inaddr6); addr_list->append(addr); - } else #endif - { + } else { if (family != 0) { - errmsg = resolv_host(family, host, addr_list); - if (errmsg) { - *errstr = errmsg; - free_addresses(addr_list); - return 0; - } + errmsg = resolv_host(family, host, addr_list); + if (errmsg) { + *errstr = errmsg; + free_addresses(addr_list); + return 0; + } } else { - errmsg = resolv_host(AF_INET, host, addr_list); #ifdef HAVE_IPV6 - if (errmsg) { - errmsg = resolv_host(AF_INET6, host, addr_list); - } + /* We try to resolv host for ipv6 and ipv4, the connection procedure + * will try to reach the host for each protocols. We report only "Host + * not found" ipv4 message (no need to have ipv6 and ipv4 messages). + */ + resolv_host(AF_INET6, host, addr_list); #endif - if (errmsg) { - *errstr = errmsg; - free_addresses(addr_list); - return 0; - } - } - } - return addr_list; -} - -/* - * Open a TCP connection to the UPS network server - * Returns NULL - * Returns BSOCK * pointer on success - * - */ -static BSOCK *bnet_open(JCR * jcr, const char *name, char *host, char *service, - int port, int *fatal) -{ - int sockfd = -1; - dlist *addr_list; - IPADDR *ipaddr; - bool connected = false; - int turnon = 1; - const char *errstr; - int save_errno = 0; - - /* - * Fill in the structure serv_addr with the address of - * the server that we want to connect with. - */ - 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", - host, errstr); - Dmsg2(100, "bnet_host2ipaddrs() for host %s failed: ERR=%s\n", - host, errstr); - *fatal = 1; - return NULL; - } + errmsg = resolv_host(AF_INET, host, addr_list); - foreach_dlist(ipaddr, addr_list) { - ipaddr->set_port_net(htons(port)); - char allbuf[256 * 10]; - char curbuf[256]; - Dmsg2(100, "Current %sAll %s\n", - ipaddr->build_address_str(curbuf, sizeof(curbuf)), - build_addresses_str(addr_list, allbuf, sizeof(allbuf))); - /* Open a TCP socket */ - if ((sockfd = socket(ipaddr->get_family(), SOCK_STREAM, 0)) < 0) { - berrno be; - save_errno = errno; - *fatal = 1; - Pmsg3(000, "Socket open error. proto=%d port=%d. ERR=%s\n", - ipaddr->get_family(), ipaddr->get_port_host_order(), be.strerror()); - continue; - } - /* - * Keep socket from timing out from inactivity - */ - if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_val_t)&turnon, sizeof(turnon)) < 0) { - berrno be; - Qmsg1(jcr, M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"), - be.strerror()); + if (addr_list->size() == 0) { + *errstr = errmsg; + free_addresses(addr_list); + return 0; + } } - /* connect to server */ - if (connect(sockfd, ipaddr->get_sockaddr(), ipaddr->get_sockaddr_len()) < 0) { - save_errno = errno; - close(sockfd); - continue; - } - *fatal = 0; - connected = true; - break; - } - - if (!connected) { - free_addresses(addr_list); - errno = save_errno; - return NULL; - } - /* - * Keep socket from timing out from inactivity - * Do this a second time out of paranoia - */ - if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_val_t)&turnon, sizeof(turnon)) < 0) { - berrno be; - Qmsg1(jcr, M_WARNING, 0, _("Cannot set SO_KEEPALIVE on socket: %s\n"), - be.strerror()); } - BSOCK* ret = init_bsock(jcr, sockfd, name, host, port, ipaddr->get_sockaddr()); - free_addresses(addr_list); - return ret; -} - -/* - * Try to connect to host for max_retry_time at retry_time intervals. - */ -BSOCK *bnet_connect(JCR * jcr, int retry_interval, int max_retry_time, - const char *name, char *host, char *service, int port, - int verbose) -{ - int i; - BSOCK *bsock; - int fatal = 0; - - for (i = 0; (bsock = bnet_open(jcr, name, host, service, port, &fatal)) == NULL; - i -= retry_interval) { - berrno be; - if (fatal || (jcr && job_canceled(jcr))) { - return NULL; - } - Dmsg4(100, "Unable to connect to %s on %s:%d. ERR=%s\n", - name, host, port, be.strerror()); - if (i < 0) { - i = 60 * 5; /* complain again in 5 minutes */ - if (verbose) - Qmsg4(jcr, M_WARNING, 0, "Could not connect to %s on %s:%d. ERR=%s\n" -"Retrying ...\n", name, host, port, be.strerror()); - } - bmicrosleep(retry_interval, 0); - max_retry_time -= retry_interval; - if (max_retry_time <= 0) { - Qmsg4(jcr, M_FATAL, 0, _("Unable to connect to %s on %s:%d. ERR=%s\n"), - name, host, port, be.strerror()); - return NULL; - } - } - return bsock; -} - - -/* - * Return the string for the error that occurred - * on the socket. Only the first error is retained. - */ -const char *bnet_strerror(BSOCK * bsock) -{ - berrno be; - if (bsock->errmsg == NULL) { - bsock->errmsg = get_pool_memory(PM_MESSAGE); - } - pm_strcpy(bsock->errmsg, be.strerror(bsock->b_errno)); - return bsock->errmsg; -} - -/* - * Format and send a message - * Returns: false on error - * true on success - */ -bool bnet_fsend(BSOCK * bs, const char *fmt, ...) -{ - va_list arg_ptr; - int maxlen; - - if (bs->errors || bs->terminated) { - return false; - } - /* This probably won't work, but we vsnprintf, then if we - * get a negative length or a length greater than our buffer - * (depending on which library is used), the printf was truncated, so - * get a bigger buffer and try again. - */ - for (;;) { - maxlen = sizeof_pool_memory(bs->msg) - 1; - va_start(arg_ptr, fmt); - bs->msglen = bvsnprintf(bs->msg, maxlen, fmt, arg_ptr); - va_end(arg_ptr); - if (bs->msglen > 0 && bs->msglen < (maxlen - 5)) { - break; - } - bs->msg = realloc_pool_memory(bs->msg, maxlen + maxlen / 2); - } - return bnet_send(bs); -} - -/* - * Set the network buffer size, suggested size is in size. - * Actual size obtained is returned in bs->msglen - * - * Returns: 0 on failure - * 1 on success - */ -bool bnet_set_buffer_size(BSOCK * bs, uint32_t size, int rw) -{ - uint32_t dbuf_size, start_size; -#if defined(IP_TOS) && defined(IPTOS_THROUGHPUT) - int opt; - - opt = IPTOS_THROUGHPUT; - setsockopt(bs->fd, IPPROTO_IP, IP_TOS, (sockopt_val_t) & opt, sizeof(opt)); -#endif - - if (size != 0) { - dbuf_size = size; - } else { - dbuf_size = DEFAULT_NETWORK_BUFFER_SIZE; - } - start_size = dbuf_size; - if ((bs->msg = realloc_pool_memory(bs->msg, dbuf_size + 100)) == NULL) { - Qmsg0(bs->jcr, M_FATAL, 0, _("Could not malloc BSOCK data buffer\n")); - return false; - } - if (rw & BNET_SETBUF_READ) { - while ((dbuf_size > TAPE_BSIZE) && (setsockopt(bs->fd, SOL_SOCKET, - SO_RCVBUF, (sockopt_val_t) & dbuf_size, sizeof(dbuf_size)) < 0)) { - berrno be; - Qmsg1(bs->jcr, M_ERROR, 0, _("sockopt error: %s\n"), be.strerror()); - dbuf_size -= TAPE_BSIZE; - } - Dmsg1(200, "set network buffer size=%d\n", dbuf_size); - if (dbuf_size != start_size) { - Qmsg1(bs->jcr, M_WARNING, 0, - _("Warning network buffer = %d bytes not max size.\n"), dbuf_size); - } - if (dbuf_size % TAPE_BSIZE != 0) { - Qmsg1(bs->jcr, M_ABORT, 0, - _("Network buffer size %d not multiple of tape block size.\n"), - dbuf_size); - } - } - if (size != 0) { - dbuf_size = size; - } else { - dbuf_size = DEFAULT_NETWORK_BUFFER_SIZE; - } - start_size = dbuf_size; - if (rw & BNET_SETBUF_WRITE) { - while ((dbuf_size > TAPE_BSIZE) && (setsockopt(bs->fd, SOL_SOCKET, - SO_SNDBUF, (sockopt_val_t) & dbuf_size, sizeof(dbuf_size)) < 0)) { - berrno be; - Qmsg1(bs->jcr, M_ERROR, 0, _("sockopt error: %s\n"), be.strerror()); - dbuf_size -= TAPE_BSIZE; - } - Dmsg1(200, "set network buffer size=%d\n", dbuf_size); - if (dbuf_size != start_size) { - Qmsg1(bs->jcr, M_WARNING, 0, - _("Warning network buffer = %d bytes not max size.\n"), dbuf_size); - } - if (dbuf_size % TAPE_BSIZE != 0) { - Qmsg1(bs->jcr, M_ABORT, 0, - _("Network buffer size %d not multiple of tape block size.\n"), - dbuf_size); - } - } - - bs->msglen = dbuf_size; - return true; -} - -/* - * Send a network "signal" to the other end - * This consists of sending a negative packet length - * - * Returns: false on failure - * true on success - */ -bool bnet_sig(BSOCK * bs, int sig) -{ - bs->msglen = sig; - return bnet_send(bs); + return addr_list; } /* * Convert a network "signal" code into * human readable ASCII. */ -const char *bnet_sig_to_ascii(BSOCK * bs) +const char *bnet_sig_to_ascii(int32_t msglen) { static char buf[30]; - switch (bs->msglen) { + switch (msglen) { case BNET_EOD: - return "BNET_EOD"; /* end of data stream */ + return "BNET_EOD"; /* End of data stream, new data may follow */ case BNET_EOD_POLL: - return "BNET_EOD_POLL"; + return "BNET_EOD_POLL"; /* End of data and poll all in one */ case BNET_STATUS: - return "BNET_STATUS"; + return "BNET_STATUS"; /* Send full status */ case BNET_TERMINATE: - return "BNET_TERMINATE"; /* terminate connection */ + return "BNET_TERMINATE"; /* Conversation terminated, doing close() */ case BNET_POLL: - return "BNET_POLL"; + return "BNET_POLL"; /* Poll request, I'm hanging on a read */ case BNET_HEARTBEAT: - return "BNET_HEARTBEAT"; + return "BNET_HEARTBEAT"; /* Heartbeat Response requested */ case BNET_HB_RESPONSE: - return "BNET_HB_RESPONSE"; - case BNET_PROMPT: - return "BNET_PROMPT"; + return "BNET_HB_RESPONSE"; /* Only response permited to HB */ + case BNET_BTIME: + return "BNET_BTIME"; /* Send UTC btime */ + case BNET_BREAK: + return "BNET_BREAK"; /* Stop current command -- ctl-c */ + case BNET_START_SELECT: + return "BNET_START_SELECT"; /* Start of a selection list */ + case BNET_END_SELECT: + return "BNET_END_SELECT"; /* End of a select list */ + case BNET_INVALID_CMD: + return "BNET_INVALID_CMD"; /* Invalid command sent */ + case BNET_CMD_FAILED: + return "BNET_CMD_FAILED"; /* Command failed */ + case BNET_CMD_OK: + return "BNET_CMD_OK"; /* Command succeeded */ + case BNET_CMD_BEGIN: + return "BNET_CMD_BEGIN"; /* Start command execution */ + case BNET_MSGS_PENDING: + return "BNET_MSGS_PENDING"; /* Messages pending */ + case BNET_MAIN_PROMPT: + return "BNET_MAIN_PROMPT"; /* Server ready and waiting */ + case BNET_SELECT_INPUT: + return "BNET_SELECT_INPUT"; /* Return selection input */ + case BNET_WARNING_MSG: + return "BNET_WARNING_MSG"; /* Warning message */ + case BNET_ERROR_MSG: + return "BNET_ERROR_MSG"; /* Error message -- command failed */ + case BNET_INFO_MSG: + return "BNET_INFO_MSG"; /* Info message -- status line */ + case BNET_RUN_CMD: + return "BNET_RUN_CMD"; /* Run command follows */ + case BNET_YESNO: + return "BNET_YESNO"; /* Request yes no response */ + case BNET_START_RTREE: + return "BNET_START_RTREE"; /* Start restore tree mode */ + case BNET_END_RTREE: + return "BNET_END_RTREE"; /* End restore tree mode */ + case BNET_SUB_PROMPT: + return "BNET_SUB_PROMPT"; /* Indicate we are at a subprompt */ + case BNET_TEXT_INPUT: + return "BNET_TEXT_INPUT"; /* Get text input from user */ + case BNET_EXT_TERMINATE: + return "BNET_EXT_TERMINATE"; /* A Terminate condition has been met and + already reported somewhere else */ + case BNET_FDCALLED : + return "BNET_FDCALLED"; /* The FD should keep the connection for a new job */ default: - sprintf(buf, "Unknown sig %d", bs->msglen); + bsnprintf(buf, sizeof(buf), _("Unknown sig %d"), (int)msglen); return buf; } } - /* Initialize internal socket structure. - * This probably should be done in net_open + * This probably should be done in bsock.c */ -BSOCK *init_bsock(JCR * jcr, int sockfd, const char *who, const char *host, int port, - struct sockaddr *client_addr) +BSOCK *init_bsock(JCR *jcr, int sockfd, const char *who, + const char *host, int port, struct sockaddr *client_addr) { - Dmsg3(100, "who=%s host=%s port=%d\n", who, host, port); + Dmsg4(100, "socket=%d who=%s host=%s port=%d\n", sockfd, who, host, port); BSOCK *bsock = (BSOCK *)malloc(sizeof(BSOCK)); - memset(bsock, 0, sizeof(BSOCK)); - bsock->fd = sockfd; + bmemzero(bsock, sizeof(BSOCK)); + bsock->m_master=bsock; /* don't use set_master() here */ + bsock->m_fd = sockfd; + bsock->tls = NULL; bsock->errors = 0; - bsock->msg = get_pool_memory(PM_MESSAGE); + bsock->m_blocking = 1; + bsock->pout_msg_no = &bsock->out_msg_no; + bsock->uninstall_send_hook_cb(); + bsock->msg = get_pool_memory(PM_BSOCK); + bsock->cmsg = get_pool_memory(PM_BSOCK); bsock->errmsg = get_pool_memory(PM_MESSAGE); - bsock->who = bstrdup(who); - bsock->host = bstrdup(host); - bsock->port = port; + bsock->set_who(bstrdup(who)); + bsock->set_host(bstrdup(host)); + bsock->set_port(port); + bmemzero(&bsock->peer_addr, sizeof(bsock->peer_addr)); memcpy(&bsock->client_addr, client_addr, sizeof(bsock->client_addr)); - /* - * ****FIXME**** reduce this to a few hours once - * heartbeats are implemented - */ - bsock->timeout = 60 * 60 * 6 * 24; /* 6 days timeout */ - bsock->jcr = jcr; + bsock->timeout = BSOCK_TIMEOUT; + bsock->set_jcr(jcr); return bsock; } -BSOCK *dup_bsock(BSOCK * osock) +BSOCK *dup_bsock(BSOCK *osock) { - BSOCK *bsock = (BSOCK *) malloc(sizeof(BSOCK)); + BSOCK *bsock = (BSOCK *)malloc(sizeof(BSOCK)); + osock->set_locking(); memcpy(bsock, osock, sizeof(BSOCK)); - bsock->msg = get_pool_memory(PM_MESSAGE); + bsock->msg = get_pool_memory(PM_BSOCK); + bsock->cmsg = get_pool_memory(PM_BSOCK); bsock->errmsg = get_pool_memory(PM_MESSAGE); - if (osock->who) { - bsock->who = bstrdup(osock->who); + if (osock->who()) { + bsock->set_who(bstrdup(osock->who())); + } + if (osock->host()) { + bsock->set_host(bstrdup(osock->host())); } - if (osock->host) { - bsock->host = bstrdup(osock->host); + if (osock->src_addr) { + bsock->src_addr = New( IPADDR( *(osock->src_addr)) ); } - bsock->duped = true; + bsock->set_duped(); + bsock->set_master(osock); return bsock; } -/* Close the network connection */ -void bnet_close(BSOCK * bsock) +int set_socket_errno(int sockstat) { - BSOCK *next; - - for (; bsock != NULL; bsock = next) { - next = bsock->next; - if (!bsock->duped) { - if (bsock->timed_out) { - shutdown(bsock->fd, 2); /* discard any pending I/O */ - } - socketClose(bsock->fd); /* normal close */ +#ifdef HAVE_WIN32 + /* + * For Windows, we must simulate Unix errno on a socket + * error in order to handle errors correctly. + */ + if (sockstat == SOCKET_ERROR) { + berrno be; + DWORD err = WSAGetLastError(); + if (err == WSAEINTR) { + errno = EINTR; + return sockstat; + } else if (err == WSAEWOULDBLOCK) { + errno = EAGAIN; + return sockstat; + } else { + errno = b_errno_win32 | b_errno_WSA; } - term_bsock(bsock); + Dmsg2(20, "Socket error: err=%d %s\n", err, be.bstrerror(err)); } - return; -} - -void term_bsock(BSOCK * bsock) -{ - if (bsock->msg) { - free_pool_memory(bsock->msg); - bsock->msg = NULL; - } else { - ASSERT(1 == 0); /* double close */ - } - if (bsock->errmsg) { - free_pool_memory(bsock->errmsg); - bsock->errmsg = NULL; - } - if (bsock->who) { - free(bsock->who); - bsock->who = NULL; - } - if (bsock->host) { - free(bsock->host); - bsock->host = NULL; +#else + if (sockstat == SOCKET_ERROR) { + /* Handle errrors from prior connections as EAGAIN */ + switch (errno) { + case ENETDOWN: + case EPROTO: + case ENOPROTOOPT: + case EHOSTDOWN: +#ifdef ENONET + case ENONET: +#endif + case EHOSTUNREACH: + case EOPNOTSUPP: + case ENETUNREACH: + errno = EAGAIN; + break; + default: + break; + } } - free(bsock); +#endif + return sockstat; }