]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/bnet.c
Restore win32 dir from Branch-5.2 and update it
[bacula/bacula] / bacula / src / lib / bnet.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2018 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * Network Utility Routines
21  *
22  *  by Kern Sibbald
23  *
24  * Adapted and enhanced for Bacula, originally written
25  * for inclusion in the Apcupsd package
26  *
27  */
28
29
30 #include "bacula.h"
31 #include "jcr.h"
32 #include <netdb.h>
33
34 #ifndef   INADDR_NONE
35 #define   INADDR_NONE    -1
36 #endif
37
38 #ifdef HAVE_WIN32
39 #undef inet_pton
40 #define inet_pton binet_pton
41 #define socketRead(fd, buf, len)  recv(fd, buf, len, 0)
42 #define socketWrite(fd, buf, len) send(fd, buf, len, 0)
43 #define socketClose(fd)           closesocket(fd)
44 #else
45 #define socketRead(fd, buf, len)  read(fd, buf, len)
46 #define socketWrite(fd, buf, len) write(fd, buf, len)
47 #define socketClose(fd)           close(fd)
48 #endif
49
50 #ifndef HAVE_GETADDRINFO
51 static pthread_mutex_t ip_mutex = PTHREAD_MUTEX_INITIALIZER;
52 #endif
53
54 /*
55  * Read a nbytes from the network.
56  * It is possible that the total bytes require in several
57  * read requests
58  */
59
60 int32_t read_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes)
61 {
62    int32_t nleft, nread;
63
64 #ifdef HAVE_TLS
65    if (bsock->tls) {
66       /* TLS enabled */
67       return (tls_bsock_readn(bsock, ptr, nbytes));
68    }
69 #endif /* HAVE_TLS */
70
71    nleft = nbytes;
72    while (nleft > 0) {
73       errno = 0;
74       nread = socketRead(bsock->m_fd, ptr, nleft);
75       if (bsock->is_timed_out() || bsock->is_terminated()) {
76          return -1;
77       }
78
79 #ifdef HAVE_WIN32
80       /*
81        * We simulate errno on Windows for a socket
82        *  error in order to handle errors correctly.
83        */
84       if (nread == SOCKET_ERROR) {
85         DWORD err = WSAGetLastError();
86         nread = -1;
87         if (err == WSAEINTR) {
88            errno = EINTR;
89         } else if (err == WSAEWOULDBLOCK) {
90            errno = EAGAIN;
91         } else {
92            errno = EIO;            /* some other error */
93         }
94      }
95 #endif
96
97       if (nread == -1) {
98          if (errno == EINTR) {
99             continue;
100          }
101          if (errno == EAGAIN) {
102             bmicrosleep(0, 20000);  /* try again in 20ms */
103             continue;
104          }
105       }
106       if (nread <= 0) {
107          return -1;                /* error, or EOF */
108       }
109       nleft -= nread;
110       ptr += nread;
111       if (bsock->use_bwlimit()) {
112          bsock->control_bwlimit(nread);
113       }
114    }
115    return nbytes - nleft;          /* return >= 0 */
116 }
117
118 /*
119  * Write nbytes to the network.
120  * It may require several writes.
121  */
122
123 int32_t write_nbytes(BSOCK * bsock, char *ptr, int32_t nbytes)
124 {
125    int32_t nleft, nwritten;
126
127    if (bsock->is_spooling()) {
128       nwritten = fwrite(ptr, 1, nbytes, bsock->m_spool_fd);
129       if (nwritten != nbytes) {
130          berrno be;
131          bsock->b_errno = errno;
132          Qmsg3(bsock->jcr(), M_FATAL, 0, _("Attr spool write error. wrote=%d wanted=%d bytes. ERR=%s\n"),
133                nbytes, nwritten, be.bstrerror());
134          Dmsg2(400, "nwritten=%d nbytes=%d.\n", nwritten, nbytes);
135          errno = bsock->b_errno;
136          return -1;
137       }
138       return nbytes;
139    }
140
141 #ifdef HAVE_TLS
142    if (bsock->tls) {
143       /* TLS enabled */
144       return (tls_bsock_writen(bsock, ptr, nbytes));
145    }
146 #endif /* HAVE_TLS */
147
148    nleft = nbytes;
149    while (nleft > 0) {
150       do {
151          errno = 0;
152          nwritten = socketWrite(bsock->m_fd, ptr, nleft);
153          if (bsock->is_timed_out() || bsock->is_terminated()) {
154             return -1;
155          }
156
157 #ifdef HAVE_WIN32
158          /*
159           * We simulate errno on Windows for a socket
160           *  error in order to handle errors correctly.
161           */
162          if (nwritten == SOCKET_ERROR) {
163             DWORD err = WSAGetLastError();
164             nwritten = -1;
165             if (err == WSAEINTR) {
166                errno = EINTR;
167             } else if (err == WSAEWOULDBLOCK) {
168                errno = EAGAIN;
169             } else {
170                errno = EIO;        /* some other error */
171             }
172          }
173 #endif
174
175       } while (nwritten == -1 && errno == EINTR);
176       /*
177        * If connection is non-blocking, we will get EAGAIN, so
178        * use select()/poll to keep from consuming all the CPU
179        * and try again.
180        */
181       if (nwritten == -1 && errno == EAGAIN) {
182          fd_wait_data(bsock->m_fd, WAIT_WRITE, 1, 0);
183          continue;
184       }
185       if (nwritten <= 0) {
186          return -1;                /* error */
187       }
188       nleft -= nwritten;
189       ptr += nwritten;
190       if (bsock->use_bwlimit()) {
191          bsock->control_bwlimit(nwritten);
192       }
193    }
194    return nbytes - nleft;
195 }
196
197 /*
198  * Establish a TLS connection -- server side
199  *  Returns: true  on success
200  *           false on failure
201  */
202 #ifdef HAVE_TLS
203 bool bnet_tls_server(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list)
204 {
205    TLS_CONNECTION *tls;
206    JCR *jcr = bsock->jcr();
207
208    tls = new_tls_connection(ctx, bsock->m_fd);
209    if (!tls) {
210       Qmsg0(bsock->jcr(), M_FATAL, 0, _("TLS connection initialization failed.\n"));
211       return false;
212    }
213
214    bsock->tls = tls;
215
216    /* Initiate TLS Negotiation */
217    if (!tls_bsock_accept(bsock)) {
218       Qmsg0(bsock->jcr(), M_FATAL, 0, _("TLS Negotiation failed.\n"));
219       goto err;
220    }
221
222    if (verify_list) {
223       if (!tls_postconnect_verify_cn(jcr, tls, verify_list)) {
224          Qmsg1(bsock->jcr(), M_FATAL, 0, _("TLS certificate verification failed."
225                                          " Peer certificate did not match a required commonName\n"),
226                                          bsock->host());
227          goto err;
228       }
229    }
230    Dmsg0(50, "TLS server negotiation established.\n");
231    return true;
232
233 err:
234    free_tls_connection(tls);
235    bsock->tls = NULL;
236    return false;
237 }
238
239 /*
240  * Establish a TLS connection -- client side
241  * Returns: true  on success
242  *          false on failure
243  */
244 bool bnet_tls_client(TLS_CONTEXT *ctx, BSOCK *bsock, alist *verify_list)
245 {
246    TLS_CONNECTION *tls;
247    JCR *jcr = bsock->jcr();
248
249    tls  = new_tls_connection(ctx, bsock->m_fd);
250    if (!tls) {
251       Qmsg0(bsock->jcr(), M_FATAL, 0, _("TLS connection initialization failed.\n"));
252       return false;
253    }
254
255    bsock->tls = tls;
256
257    /* Initiate TLS Negotiation */
258    if (!tls_bsock_connect(bsock)) {
259       goto err;
260    }
261
262    /* If there's an Allowed CN verify list, use that to validate the remote
263     * certificate's CN. Otherwise, we use standard host/CN matching. */
264    if (verify_list) {
265       if (!tls_postconnect_verify_cn(jcr, tls, verify_list)) {
266          Qmsg1(bsock->jcr(), M_FATAL, 0, _("TLS certificate verification failed."
267                                          " Peer certificate did not match a required commonName\n"),
268                                          bsock->host());
269          goto err;
270       }
271    } else if (!tls_postconnect_verify_host(jcr, tls, bsock->host())) {
272       /* If host is 127.0.0.1, try localhost */
273       if (strcmp(bsock->host(), "127.0.0.1") != 0 ||
274              !tls_postconnect_verify_host(jcr, tls, "localhost")) {
275          Qmsg1(bsock->jcr(), M_FATAL, 0, _("TLS host certificate verification failed. Host name \"%s\" did not match presented certificate\n"),
276                bsock->host());
277          goto err;
278       }
279    }
280    Dmsg0(50, "TLS client negotiation established.\n");
281    return true;
282
283 err:
284    free_tls_connection(tls);
285    bsock->tls = NULL;
286    return false;
287 }
288 #else
289
290 bool bnet_tls_server(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list)
291 {
292    Jmsg(bsock->jcr(), M_ABORT, 0, _("TLS enabled but not configured.\n"));
293    return false;
294 }
295
296 bool bnet_tls_client(TLS_CONTEXT *ctx, BSOCK * bsock, alist *verify_list)
297 {
298    Jmsg(bsock->jcr(), M_ABORT, 0, _("TLS enable but not configured.\n"));
299    return false;
300 }
301
302 #endif /* HAVE_TLS */
303
304 #ifndef NETDB_INTERNAL
305 #define NETDB_INTERNAL  -1         /* See errno. */
306 #endif
307 #ifndef NETDB_SUCCESS
308 #define NETDB_SUCCESS   0          /* No problem. */
309 #endif
310 #ifndef HOST_NOT_FOUND
311 #define HOST_NOT_FOUND  1          /* Authoritative Answer Host not found. */
312 #endif
313 #ifndef TRY_AGAIN
314 #define TRY_AGAIN       2          /* Non-Authoritative Host not found, or SERVERFAIL. */
315 #endif
316 #ifndef NO_RECOVERY
317 #define NO_RECOVERY     3          /* Non recoverable errors, FORMERR, REFUSED, NOTIMP. */
318 #endif
319 #ifndef NO_DATA
320 #define NO_DATA         4          /* Valid name, no data record of requested type. */
321 #endif
322
323 #if defined(HAVE_GETADDRINFO)
324 /* 
325  * getaddrinfo.c - Simple example of using getaddrinfo(3) function.
326  * 
327  * Michal Ludvig <michal@logix.cz> (c) 2002, 2003
328  * http://www.logix.cz/michal/devel/
329  *
330  * License: public domain.
331  */
332 const char *resolv_host(int family, const char *host, dlist *addr_list) 
333
334    IPADDR *ipaddr;
335    struct addrinfo hints, *res, *rp;
336    int errcode;
337    //char addrstr[100];
338    void *ptr;
339
340    memset (&hints, 0, sizeof(hints));
341    hints.ai_family = family; 
342    hints.ai_socktype = SOCK_STREAM; 
343    //hints.ai_flags |= AI_CANONNAME;
344
345    errcode = getaddrinfo (host, NULL, &hints, &res);
346    if (errcode != 0) return gai_strerror(errcode);
347
348    for (rp=res; res; res=res->ai_next) {
349       //inet_ntop (res->ai_family, res->ai_addr->sa_data, addrstr, 100);
350       switch (res->ai_family) {
351       case AF_INET: 
352          ipaddr = New(IPADDR(rp->ai_addr->sa_family));
353          ipaddr->set_type(IPADDR::R_MULTIPLE);
354          ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr;
355          ipaddr->set_addr4((in_addr *)ptr);
356          break; 
357 #if defined(HAVE_IPV6)
358       case AF_INET6: 
359          ipaddr = New(IPADDR(rp->ai_addr->sa_family));
360          ipaddr->set_type(IPADDR::R_MULTIPLE);
361          ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr;
362          ipaddr->set_addr6((in6_addr *)ptr);
363          break; 
364 #endif 
365       default: 
366          continue; 
367       } 
368       //inet_ntop (res->ai_family, ptr, addrstr, 100);
369       //Pmsg3(000, "IPv%d address: %s (%s)\n", res->ai_family == PF_INET6 ? 6 : 4,
370       //         addrstr, res->ai_canonname);
371       addr_list->append(ipaddr);
372    } 
373    freeaddrinfo(rp);
374    return NULL; 
375
376
377 #else 
378
379 /*
380  * Get human readable error for gethostbyname()
381  */
382 static const char *gethost_strerror()
383 {
384    const char *msg;
385    berrno be;
386    switch (h_errno) {
387    case NETDB_INTERNAL:
388       msg = be.bstrerror();
389       break;
390    case NETDB_SUCCESS:
391       msg = _("No problem.");
392       break;
393    case HOST_NOT_FOUND:
394       msg = _("Authoritative answer for host not found.");
395       break;
396    case TRY_AGAIN:
397       msg = _("Non-authoritative for host not found, or ServerFail.");
398       break;
399    case NO_RECOVERY:
400       msg = _("Non-recoverable errors, FORMERR, REFUSED, or NOTIMP.");
401       break;
402    case NO_DATA:
403       msg = _("Valid name, no data record of resquested type.");
404       break;
405    default:
406       msg = _("Unknown error.");
407    }
408    return msg;
409 }
410
411 /*
412  * Note: this is the old way of resolving a host
413  *  that does not use the new getaddrinfo() above.
414  */
415 static const char *resolv_host(int family, const char *host, dlist * addr_list)
416 {
417    struct hostent *hp;
418    const char *errmsg;
419
420    P(ip_mutex);                       /* gethostbyname() is not thread safe */
421 #ifdef HAVE_GETHOSTBYNAME2
422    if ((hp = gethostbyname2(host, family)) == NULL) {
423 #else
424    if ((hp = gethostbyname(host)) == NULL) {
425 #endif
426       /* may be the strerror give not the right result -:( */
427       errmsg = gethost_strerror();
428       V(ip_mutex);
429       return errmsg;
430    } else {
431       char **p;
432       for (p = hp->h_addr_list; *p != 0; p++) {
433          IPADDR *addr =  New(IPADDR(hp->h_addrtype));
434          addr->set_type(IPADDR::R_MULTIPLE);
435          if (addr->get_family() == AF_INET) {
436              addr->set_addr4((struct in_addr*)*p);
437          }
438 #ifdef HAVE_IPV6
439          else {
440              addr->set_addr6((struct in6_addr*)*p);
441          }
442 #endif
443          addr_list->append(addr);
444       }
445       V(ip_mutex);
446    }
447    return NULL;
448 }
449 #endif
450
451 static IPADDR *add_any(int family)
452 {
453    IPADDR *addr = New(IPADDR(family));
454    addr->set_type(IPADDR::R_MULTIPLE);
455    addr->set_addr_any();
456    return addr;
457 }
458
459 /*
460  * i host = 0 means INADDR_ANY only for IPv4
461  */
462 dlist *bnet_host2ipaddrs(const char *host, int family, const char **errstr)
463 {
464    struct in_addr inaddr;
465    IPADDR *addr = 0;
466    const char *errmsg;
467 #ifdef HAVE_IPV6
468    struct in6_addr inaddr6;
469 #endif
470
471    dlist *addr_list = New(dlist(addr, &addr->link));
472    if (!host || host[0] == '\0') {
473       if (family != 0) {
474          addr_list->append(add_any(family));
475       } else {
476          addr_list->append(add_any(AF_INET));
477 #ifdef HAVE_IPV6
478          addr_list->append(add_any(AF_INET6));
479 #endif
480       }
481    } else if (inet_aton(host, &inaddr)) { /* MA Bug 4 */
482       addr = New(IPADDR(AF_INET));
483       addr->set_type(IPADDR::R_MULTIPLE);
484       addr->set_addr4(&inaddr);
485       addr_list->append(addr);
486 #ifdef HAVE_IPV6
487    } else if (inet_pton(AF_INET6, host, &inaddr6) == 1) { 
488       addr = New(IPADDR(AF_INET6));
489       addr->set_type(IPADDR::R_MULTIPLE);
490       addr->set_addr6(&inaddr6);
491       addr_list->append(addr);
492 #endif
493    } else { 
494       if (family != 0) {
495          errmsg = resolv_host(family, host, addr_list);
496          if (errmsg) {
497             *errstr = errmsg;
498             free_addresses(addr_list);
499             return 0;
500          }
501       } else {
502 #ifdef HAVE_IPV6
503          /* We try to resolv host for ipv6 and ipv4, the connection procedure
504           * will try to reach the host for each protocols. We report only "Host
505           * not found" ipv4 message (no need to have ipv6 and ipv4 messages).
506           */
507          resolv_host(AF_INET6, host, addr_list);
508 #endif
509          errmsg = resolv_host(AF_INET, host, addr_list);
510
511          if (addr_list->size() == 0) {
512             *errstr = errmsg;
513             free_addresses(addr_list);
514             return 0;
515          }
516       }
517    }
518    return addr_list;
519 }
520
521 /*
522  * Convert a network "signal" code into
523  * human readable ASCII.
524  */
525 const char *bnet_sig_to_ascii(int32_t msglen)
526 {
527    static char buf[30];
528    switch (msglen) {
529    case BNET_EOD:
530       return "BNET_EOD";        /* End of data stream, new data may follow */
531    case BNET_EOD_POLL:
532       return "BNET_EOD_POLL";   /* End of data and poll all in one */
533    case BNET_STATUS:
534       return "BNET_STATUS";     /* Send full status */
535    case BNET_TERMINATE:
536       return "BNET_TERMINATE";  /* Conversation terminated, doing close() */
537    case BNET_POLL:
538       return "BNET_POLL";       /* Poll request, I'm hanging on a read */
539    case BNET_HEARTBEAT:
540       return "BNET_HEARTBEAT";  /* Heartbeat Response requested */
541    case BNET_HB_RESPONSE:
542       return "BNET_HB_RESPONSE"; /* Only response permited to HB */
543    case BNET_BTIME:
544       return "BNET_BTIME";      /* Send UTC btime */
545    case BNET_BREAK:
546       return "BNET_BREAK";      /* Stop current command -- ctl-c */
547    case BNET_START_SELECT:
548       return "BNET_START_SELECT"; /* Start of a selection list */
549    case BNET_END_SELECT:
550       return "BNET_END_SELECT"; /* End of a select list */
551    case BNET_INVALID_CMD:
552       return "BNET_INVALID_CMD"; /* Invalid command sent */
553    case BNET_CMD_FAILED:
554       return "BNET_CMD_FAILED"; /* Command failed */
555    case BNET_CMD_OK:
556       return "BNET_CMD_OK";     /* Command succeeded */
557    case BNET_CMD_BEGIN:
558       return "BNET_CMD_BEGIN";  /* Start command execution */
559    case BNET_MSGS_PENDING:
560       return "BNET_MSGS_PENDING"; /* Messages pending */
561    case BNET_MAIN_PROMPT:
562       return "BNET_MAIN_PROMPT"; /* Server ready and waiting */
563    case BNET_SELECT_INPUT:
564       return "BNET_SELECT_INPUT"; /* Return selection input */
565    case BNET_WARNING_MSG:
566       return "BNET_WARNING_MSG"; /* Warning message */
567    case BNET_ERROR_MSG:
568       return "BNET_ERROR_MSG";  /* Error message -- command failed */
569    case BNET_INFO_MSG:
570       return "BNET_INFO_MSG";   /* Info message -- status line */
571    case BNET_RUN_CMD:
572       return "BNET_RUN_CMD";    /* Run command follows */
573    case BNET_YESNO:
574       return "BNET_YESNO";      /* Request yes no response */
575    case BNET_START_RTREE:
576       return "BNET_START_RTREE"; /* Start restore tree mode */
577    case BNET_END_RTREE:
578       return "BNET_END_RTREE";  /* End restore tree mode */
579    case BNET_SUB_PROMPT:
580       return "BNET_SUB_PROMPT"; /* Indicate we are at a subprompt */
581    case BNET_TEXT_INPUT:
582       return "BNET_TEXT_INPUT"; /* Get text input from user */
583    case BNET_EXT_TERMINATE:
584        return "BNET_EXT_TERMINATE"; /* A Terminate condition has been met and
585                                already reported somewhere else */
586    case BNET_FDCALLED      :
587       return "BNET_FDCALLED"; /* The FD should keep the connection for a new job */
588    default:
589       bsnprintf(buf, sizeof(buf), _("Unknown sig %d"), (int)msglen);
590       return buf;
591    }
592 }
593
594 /* Initialize internal socket structure.
595  *  This probably should be done in bsock.c
596  */
597 BSOCK *init_bsock(JCR *jcr, int sockfd, const char *who,
598                    const char *host, int port, struct sockaddr *client_addr)
599 {
600    Dmsg4(100, "socket=%d who=%s host=%s port=%d\n", sockfd, who, host, port);
601    BSOCK *bsock = (BSOCK *)malloc(sizeof(BSOCK));
602    bmemzero(bsock, sizeof(BSOCK));
603    bsock->m_master=bsock; /* don't use set_master() here */
604    bsock->m_fd = sockfd;
605    bsock->tls = NULL;
606    bsock->errors = 0;
607    bsock->m_blocking = 1;
608    bsock->pout_msg_no = &bsock->out_msg_no;
609    bsock->uninstall_send_hook_cb();
610    bsock->msg = get_pool_memory(PM_BSOCK);
611    bsock->cmsg = get_pool_memory(PM_BSOCK);
612    bsock->errmsg = get_pool_memory(PM_MESSAGE);
613    bsock->set_who(bstrdup(who));
614    bsock->set_host(bstrdup(host));
615    bsock->set_port(port);
616    bmemzero(&bsock->peer_addr, sizeof(bsock->peer_addr));
617    memcpy(&bsock->client_addr, client_addr, sizeof(bsock->client_addr));
618    bsock->timeout = BSOCK_TIMEOUT;
619    bsock->set_jcr(jcr);
620    return bsock;
621 }
622
623 BSOCK *dup_bsock(BSOCK *osock)
624 {
625    BSOCK *bsock = (BSOCK *)malloc(sizeof(BSOCK));
626    osock->set_locking();
627    memcpy(bsock, osock, sizeof(BSOCK));
628    bsock->msg = get_pool_memory(PM_BSOCK);
629    bsock->cmsg = get_pool_memory(PM_BSOCK);
630    bsock->errmsg = get_pool_memory(PM_MESSAGE);
631    if (osock->who()) {
632       bsock->set_who(bstrdup(osock->who()));
633    }
634    if (osock->host()) {
635       bsock->set_host(bstrdup(osock->host()));
636    }
637    if (osock->src_addr) {
638       bsock->src_addr = New( IPADDR( *(osock->src_addr)) );
639    }
640    bsock->set_duped();
641    bsock->set_master(osock);
642    return bsock;
643 }
644
645 int set_socket_errno(int sockstat)
646 {
647 #ifdef HAVE_WIN32
648    /*
649     * For Windows, we must simulate Unix errno on a socket
650     *  error in order to handle errors correctly.
651     */
652    if (sockstat == SOCKET_ERROR) {
653       berrno be;
654       DWORD err = WSAGetLastError();
655       if (err == WSAEINTR) {
656          errno = EINTR;
657          return sockstat;
658       } else if (err == WSAEWOULDBLOCK) {
659          errno = EAGAIN;
660          return sockstat;
661       } else {
662          errno = b_errno_win32 | b_errno_WSA;
663       }
664       Dmsg2(20, "Socket error: err=%d %s\n", err, be.bstrerror(err));
665    }
666 #else
667    if (sockstat == SOCKET_ERROR) {
668       /* Handle errrors from prior connections as EAGAIN */
669       switch (errno) {
670          case ENETDOWN:
671          case EPROTO:
672          case ENOPROTOOPT:
673          case EHOSTDOWN:
674 #ifdef ENONET
675          case ENONET:
676 #endif
677          case EHOSTUNREACH:
678          case EOPNOTSUPP:
679          case ENETUNREACH:
680             errno = EAGAIN;
681             break;
682          default:
683             break;
684       }
685    }
686 #endif
687    return sockstat;
688 }