/*
- Bacula® - The Network Backup Solution
-
- Copyright (C) 2000-2008 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 and included
- 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 Kern Sibbald.
- 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
* Adapted and enhanced for Bacula, originally written
* for inclusion in the Apcupsd package
*
- * Version $Id$
*/
#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) close(fd)
#endif
+#ifndef HAVE_GETADDRINFO
static pthread_mutex_t ip_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
/*
* Read a nbytes from the network.
errno = 0;
nread = socketRead(bsock->m_fd, ptr, nleft);
if (bsock->is_timed_out() || bsock->is_terminated()) {
- return nread;
+ 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 */
}
if (nwritten != nbytes) {
berrno be;
bsock->b_errno = errno;
- Qmsg1(bsock->jcr(), M_FATAL, 0, _("Attr spool write error. ERR=%s\n"),
- be.bstrerror());
+ 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 = 0;
nwritten = socketWrite(bsock->m_fd, ptr, nleft);
if (bsock->is_timed_out() || bsock->is_terminated()) {
- return nwritten;
+ 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->m_fd, &fdset);
- tv.tv_sec = 10;
- tv.tv_usec = 0;
- select(bsock->m_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->is_stop();
-}
-
-/*
- * Return number of errors on socket
- */
-int is_bnet_error(BSOCK * bsock)
-{
- return bsock->is_error();
-}
-
-/*
- * 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->m_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
{
TLS_CONNECTION *tls;
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"));
* 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();
bsock->host());
goto err;
}
- } else {
- if (!tls_postconnect_verify_host(jcr, tls, bsock->host())) {
- Qmsg1(bsock->jcr(), M_FATAL, 0, _("TLS host certificate verification failed. Host name \"%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;
}
#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)
-{
- return bsock->wait_data(sec);
-}
-
-/*
- * As above, but returns on interrupt
- */
-int bnet_wait_data_intr(BSOCK * bsock, int sec)
-{
- return bsock->wait_data_intr(sec);
-}
-
#ifndef NETDB_INTERNAL
#define NETDB_INTERNAL -1 /* See errno. */
#endif
#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 <michal@logix.cz> (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()
*/
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;
}
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)
{
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) {
return addr_list;
}
-/*
- * This is the "old" way of opening a connection. The preferred way is
- * now to do what this subroutine does, but inline. That allows the
- * connect() call to return error status, ...
- */
-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)
-{
- BSOCK *bsock = new_bsock();
- if (!bsock->connect(jcr, retry_interval, max_retry_time, heart_beat,
- name, host, service, port, verbose)) {
- bsock->destroy();
- bsock = 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)
-{
- return bsock->bstrerror();
-}
-
-/*
- * 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->is_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)
-{
- return bs->get_peer(buf, buflen);
-}
-
-/*
- * 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)
-{
- return bs->set_buffer_size(size, rw);
-}
-
-/*
- * Set socket non-blocking
- * Returns previous socket flag
- */
-int bnet_set_nonblocking(BSOCK *bsock)
-{
- return bsock->set_nonblocking();
-}
-
-/*
- * Set socket blocking
- * Returns previous socket flags
- */
-int bnet_set_blocking(BSOCK *bsock)
-{
- return bsock->set_blocking();
-}
-
-/*
- * Restores socket flags
- */
-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
- *
- * 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));
+ 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->m_blocking = 1;
- bsock->msg = get_pool_memory(PM_MESSAGE);
+ 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 *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()));
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)
+int set_socket_errno(int sockstat)
{
- bsock->close();
-}
-
-void term_bsock(BSOCK * bsock)
-{
- 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;
}