X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Flib%2Fbnet.c;h=584388d1656f71fdf0224ddb05764bbd81f020e8;hb=10cfd798ced2d27f61ead2de6fe9b1bcc8e3468d;hp=f1d2dad151a814b12e905b8bfcb3295cfb9b4af6;hpb=53f3f6e4ca4b44166668ca9a7647f67fd934162f;p=bacula%2Fbacula diff --git a/bacula/src/lib/bnet.c b/bacula/src/lib/bnet.c index f1d2dad151..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-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 @@ -33,7 +24,6 @@ * Adapted and enhanced for Bacula, originally written * for inclusion in the Apcupsd package * - * Version $Id$ */ @@ -46,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) @@ -55,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. @@ -79,8 +73,27 @@ int32_t read_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) 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; @@ -91,10 +104,13 @@ int32_t read_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) } } 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 */ } @@ -113,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; @@ -135,97 +151,49 @@ int32_t write_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes) 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 = 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) { - 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 @@ -236,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")); @@ -273,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(); @@ -300,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; } @@ -331,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 @@ -371,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() */ @@ -403,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; @@ -448,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) { @@ -476,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) { @@ -513,178 +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"; - 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; } @@ -692,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_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())); @@ -705,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; }