X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Flib%2Fbnet.c;h=584388d1656f71fdf0224ddb05764bbd81f020e8;hb=10cfd798ced2d27f61ead2de6fe9b1bcc8e3468d;hp=43b71c8136b0c2f837761aa387e96d81bead914a;hpb=ef5e6d3586cb406724f5f866b589a4422634ebb7;p=bacula%2Fbacula diff --git a/bacula/src/lib/bnet.c b/bacula/src/lib/bnet.c index 43b71c8136..584388d165 100644 --- a/bacula/src/lib/bnet.c +++ b/bacula/src/lib/bnet.c @@ -1,29 +1,20 @@ /* - Bacula® - The Network Backup Solution - - Copyright (C) 2000-2006 Free Software Foundation Europe e.V. - - The main author of Bacula is Kern Sibbald, with contributions from - many others, a complete list can be found in the file AUTHORS. - This program is Free Software; you can redistribute it and/or - modify it under the terms of version two of the GNU General Public - License as published by the Free Software Foundation plus additions - that are listed in the file LICENSE. - - This program 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 - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - - Bacula® is a registered trademark of John Walker. - The licensor of Bacula is the Free Software Foundation Europe - (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, - Switzerland, email:ftf@fsfeurope.org. + 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 @@ -33,7 +24,6 @@ * Adapted and enhanced for Bacula, originally written * for inclusion in the Apcupsd package * - * Version $Id$ */ @@ -41,13 +31,13 @@ #include "jcr.h" #include -extern time_t watchdog_time; - #ifndef INADDR_NONE #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) @@ -57,7 +47,9 @@ extern time_t watchdog_time; #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. @@ -79,24 +71,46 @@ int32_t read_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) nleft = nbytes; while (nleft > 0) { errno = 0; - nread = socketRead(bsock->fd, ptr, nleft); - if (bsock->timed_out || bsock->terminated) { - return nread; + 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, 200000); /* try again in 200ms */ + 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 */ } @@ -110,13 +124,13 @@ 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()); + 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; @@ -135,100 +149,51 @@ int32_t write_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) while (nleft > 0) { do { errno = 0; - nwritten = socketWrite(bsock->fd, ptr, nleft); - if (bsock->timed_out || bsock->terminated) { - return nwritten; + 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); + 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. - */ -int32_t bnet_recv(BSOCK * bsock) -{ - return bsock->recv(); -} - - -/* - * 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) -{ - errno = bsock->b_errno; - 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; -} - -/* - * 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 - */ -bool bnet_send(BSOCK *bsock) -{ - return bsock->send(); -} - - /* * Establish a TLS connection -- server side * Returns: true on success @@ -238,8 +203,9 @@ bool bnet_send(BSOCK *bsock) bool bnet_tls_server(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list) { TLS_CONNECTION *tls; - - tls = new_tls_connection(ctx, bsock->fd); + JCR *jcr = bsock->jcr(); + + tls = new_tls_connection(ctx, bsock->m_fd); if (!tls) { Qmsg0(bsock->jcr(), M_FATAL, 0, _("TLS connection initialization failed.\n")); return false; @@ -254,13 +220,14 @@ bool bnet_tls_server(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list) } if (verify_list) { - if (!tls_postconnect_verify_cn(tls, 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; } } + Dmsg0(50, "TLS server negotiation established.\n"); return true; err: @@ -274,11 +241,12 @@ err: * Returns: true on success * false on failure */ -bool bnet_tls_client(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list) +bool bnet_tls_client(TLS_CONTEXT *ctx, BSOCK *bsock, alist *verify_list) { TLS_CONNECTION *tls; + JCR *jcr = bsock->jcr(); - tls = new_tls_connection(ctx, bsock->fd); + tls = new_tls_connection(ctx, bsock->m_fd); if (!tls) { Qmsg0(bsock->jcr(), M_FATAL, 0, _("TLS connection initialization failed.\n")); return false; @@ -294,20 +262,22 @@ bool bnet_tls_client(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list) /* 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(tls, 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; } - } else { - if (!tls_postconnect_verify_host(tls, bsock->host())) { - Qmsg1(bsock->jcr(), M_FATAL, 0, _("TLS host certificate verification failed. Host %s did not match presented certificate\n"), + } 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; } } - + Dmsg0(50, "TLS client negotiation established.\n"); return true; err: @@ -331,66 +301,6 @@ bool bnet_tls_client(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list) #endif /* HAVE_TLS */ -/* - * 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) -{ - fd_set fdset; - struct timeval tv; - - FD_ZERO(&fdset); - FD_SET((unsigned)bsock->fd, &fdset); - for (;;) { - tv.tv_sec = sec; - tv.tv_usec = 0; - 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) { - continue; - } - return -1; /* error return */ - default: - bsock->b_errno = 0; - return 1; - } - } -} - -/* - * 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; - 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; -} - #ifndef NETDB_INTERNAL #define NETDB_INTERNAL -1 /* See errno. */ #endif @@ -410,6 +320,62 @@ int bnet_wait_data_intr(BSOCK * bsock, int sec) #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() */ @@ -419,7 +385,7 @@ static const char *gethost_strerror() berrno be; switch (h_errno) { case NETDB_INTERNAL: - msg = be.strerror(); + msg = be.bstrerror(); break; case NETDB_SUCCESS: msg = _("No problem."); @@ -442,17 +408,10 @@ 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; -} - +/* + * 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; @@ -487,9 +446,18 @@ static const char *resolv_host(int family, const char *host, dlist * addr_list) } 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) { @@ -515,16 +483,14 @@ 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) { @@ -533,13 +499,16 @@ dlist *bnet_host2ipaddrs(const char *host, int family, const char **errstr) 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) { + errmsg = resolv_host(AF_INET, host, addr_list); + + if (addr_list->size() == 0) { *errstr = errmsg; free_addresses(addr_list); return 0; @@ -549,454 +518,115 @@ dlist *bnet_host2ipaddrs(const char *host, int family, const char **errstr) 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, utime_t heart_beat, 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; - } - - 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 defined(TCP_KEEPIDLE) - if (heart_beat) { - int opt = heart_beat - if (setsockopt(sockfd, IPPROTO_IP, TCP_KEEPIDLE, (sockopt_val_t)&opt, sizeof(opt)) < 0) { - berrno be; - Qmsg1(jcr, M_WARNING, 0, _("Cannot set SO_KEEPIDLE on socket: %s\n"), - be.strerror()); - } - } -#endif - - /* connect to server */ - if (connect(sockfd, ipaddr->get_sockaddr(), ipaddr->get_sockaddr_len()) < 0) { - save_errno = errno; - socketClose(sockfd); - continue; - } - *fatal = 0; - connected = true; - break; - } - - if (!connected) { - free_addresses(addr_list); - errno = save_errno | b_errno_win32; - 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, utime_t max_retry_time, - utime_t heart_beat, - const char *name, char *host, char *service, int port, - int verbose) -{ - int i; - BSOCK *bsock; - int fatal = 0; - time_t begin_time = time(NULL); - time_t now; - btimer_t *tid = NULL; - - /* Try to trap out of OS call when time expires */ - tid = start_thread_timer(pthread_self(), (uint32_t)max_retry_time); - - for (i = 0; (bsock = bnet_open(jcr, name, host, service, port, heart_beat, &fatal)) == NULL; - i -= retry_interval) { - berrno be; - if (fatal || (jcr && job_canceled(jcr))) { - bsock = NULL; - goto bail_out; - } - 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); - now = time(NULL); - if (begin_time + max_retry_time <= now) { - Qmsg4(jcr, M_FATAL, 0, _("Unable to connect to %s on %s:%d. ERR=%s\n"), - name, host, port, be.strerror()); - bsock = NULL; - goto bail_out; - } - } - -bail_out: - if (tid) { - stop_thread_timer(tid); - } - 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 bs->send(); -} - -int bnet_get_peer(BSOCK *bs, char *buf, socklen_t buflen) -{ -#if !defined(HAVE_WIN32) - if (bs->peer_addr.sin_family == 0) { - socklen_t salen = sizeof(bs->peer_addr); - int rval = (getpeername)(bs->fd, (struct sockaddr *)&bs->peer_addr, &salen); - if (rval < 0) return rval; - } - if (!inet_ntop(bs->peer_addr.sin_family, &bs->peer_addr.sin_addr, buf, buflen)) - return -1; - - return 0; -#else - return -1; -#endif -} -/* - * 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(900, "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; -} - -/* - * Set socket non-blocking - * Returns previous socket flag - */ -int bnet_set_nonblocking (BSOCK *bsock) { -#ifndef HAVE_WIN32 - int oflags; - - /* Get current flags */ - if ((oflags = fcntl(bsock->fd, F_GETFL, 0)) < 0) { - berrno be; - Jmsg1(bsock->jcr(), M_ABORT, 0, _("fcntl F_GETFL error. ERR=%s\n"), be.strerror()); - } - - /* Set O_NONBLOCK flag */ - if ((fcntl(bsock->fd, F_SETFL, oflags|O_NONBLOCK)) < 0) { - berrno be; - Jmsg1(bsock->jcr(), M_ABORT, 0, _("fcntl F_SETFL error. ERR=%s\n"), be.strerror()); - } - - bsock->blocking = 0; - return oflags; -#else - int flags; - u_long ioctlArg = 1; - - flags = bsock->blocking; - ioctlsocket(bsock->fd, FIONBIO, &ioctlArg); - bsock->blocking = 0; - - return flags; -#endif -} - -/* - * Set socket blocking - * Returns previous socket flags - */ -int bnet_set_blocking (BSOCK *bsock) -{ -#ifndef HAVE_WIN32 - int oflags; - /* Get current flags */ - if ((oflags = fcntl(bsock->fd, F_GETFL, 0)) < 0) { - berrno be; - Jmsg1(bsock->jcr(), M_ABORT, 0, _("fcntl F_GETFL error. ERR=%s\n"), be.strerror()); - } - - /* Set O_NONBLOCK flag */ - if ((fcntl(bsock->fd, F_SETFL, oflags & ~O_NONBLOCK)) < 0) { - berrno be; - Jmsg1(bsock->jcr(), M_ABORT, 0, _("fcntl F_SETFL error. ERR=%s\n"), be.strerror()); - } - - bsock->blocking = 1; - return oflags; -#else - int flags; - u_long ioctlArg = 0; - - flags = bsock->blocking; - ioctlsocket(bsock->fd, FIONBIO, &ioctlArg); - bsock->blocking = 1; - - return flags; -#endif -} - -/* - * Restores socket flags - */ -void bnet_restore_blocking (BSOCK *bsock, int flags) -{ -#ifndef HAVE_WIN32 - if ((fcntl(bsock->fd, F_SETFL, flags)) < 0) { - berrno be; - Jmsg1(bsock->jcr(), M_ABORT, 0, _("fcntl F_SETFL error. ERR=%s\n"), be.strerror()); - } - - bsock->blocking = (flags & O_NONBLOCK); -#else - u_long ioctlArg = flags; - - ioctlsocket(bsock->fd, FIONBIO, &ioctlArg); - bsock->blocking = 1; -#endif -} - - -/* - * 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 signal) -{ - return bs->signal(signal); -} - /* * 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"), (int)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->blocking = 1; - 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->set_who(bstrdup(who)); bsock->set_host(bstrdup(host)); bsock->set_port(port); - memset(&bsock->peer_addr, 0, sizeof(bsock->peer_addr)); + 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->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)); + 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->set_who(bstrdup(osock->who())); @@ -1004,17 +634,55 @@ BSOCK *dup_bsock(BSOCK * osock) if (osock->host()) { bsock->set_host(bstrdup(osock->host())); } - bsock->duped = true; + if (osock->src_addr) { + bsock->src_addr = New( IPADDR( *(osock->src_addr)) ); + } + bsock->set_duped(); + bsock->set_master(osock); return bsock; } -/* Close the network connection */ -void bnet_close(BSOCK * bsock) -{ - bsock->close(); /* this calls destroy */ -} - -void term_bsock(BSOCK * bsock) +int set_socket_errno(int sockstat) { - bsock->destroy(); +#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; + } + Dmsg2(20, "Socket error: err=%d %s\n", err, be.bstrerror(err)); + } +#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; + } + } +#endif + return sockstat; }