]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/lib/bnet.c
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / lib / bnet.c
index b783136b2afe3e03ace0c29a8afc0e727dfb05f7..584388d1656f71fdf0224ddb05764bbd81f020e8 100644 (file)
@@ -1,29 +1,20 @@
 /*
-   Bacula® - The Network Backup Solution
-
-   Copyright (C) 2000-2011 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 three of the GNU Affero 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 Affero 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
@@ -45,6 +36,8 @@
 #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)
@@ -54,7 +47,9 @@
 #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.
@@ -83,7 +78,7 @@ int32_t read_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes)
 
 #ifdef HAVE_WIN32
       /*
-       * For Windows, we must simulate Unix erro on a socket
+       * We simulate errno on Windows for a socket
        *  error in order to handle errors correctly.
        */
       if (nread == SOCKET_ERROR) {
@@ -113,6 +108,9 @@ int32_t read_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes)
       }
       nleft -= nread;
       ptr += nread;
+      if (bsock->use_bwlimit()) {
+         bsock->control_bwlimit(nread);
+      }
    }
    return nbytes - nleft;          /* return >= 0 */
 }
@@ -131,8 +129,8 @@ int32_t write_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes)
       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;
@@ -158,7 +156,7 @@ int32_t write_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes)
 
 #ifdef HAVE_WIN32
          /*
-          * For Windows, we must simulate Unix erro on a socket
+          * We simulate errno on Windows for a socket
           *  error in order to handle errors correctly.
           */
          if (nwritten == SOCKET_ERROR) {
@@ -177,18 +175,11 @@ int32_t write_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes)
       } 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 = 1;
-         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) {
@@ -196,73 +187,13 @@ int32_t write_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes)
       }
       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
@@ -273,7 +204,7 @@ bool bnet_tls_server(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list)
 {
    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"));
@@ -310,7 +241,7 @@ 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();
@@ -337,9 +268,11 @@ bool bnet_tls_client(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list)
                                          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;
       }
@@ -368,27 +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)
-{
-   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
@@ -408,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 <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()
  */
@@ -440,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;
@@ -485,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)
 {
@@ -513,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) {
@@ -550,180 +518,104 @@ dlist *bnet_host2ipaddrs(const char *host, int family, const char **errstr)
    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";
+      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";
+      return "BNET_SUB_PROMPT"; /* Indicate we are at a subprompt */
    case BNET_TEXT_INPUT:
-      return "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->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;
 }
@@ -731,8 +623,10 @@ BSOCK *init_bsock(JCR * jcr, int sockfd, const char *who, const char *host, int
 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_BSOCK);
+   bsock->cmsg = get_pool_memory(PM_BSOCK);
    bsock->errmsg = get_pool_memory(PM_MESSAGE);
    if (osock->who()) {
       bsock->set_who(bstrdup(osock->who()));
@@ -744,16 +638,51 @@ BSOCK *dup_bsock(BSOCK *osock)
       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;
 }