Annotation of embedaddon/iperf/src/net.c, revision 1.1.1.3

1.1       misho       1: /*
1.1.1.2   misho       2:  * iperf, Copyright (c) 2014-2019, The Regents of the University of
1.1       misho       3:  * California, through Lawrence Berkeley National Laboratory (subject
                      4:  * to receipt of any required approvals from the U.S. Dept. of
                      5:  * Energy).  All rights reserved.
                      6:  *
                      7:  * If you have questions about your rights to use or distribute this
                      8:  * software, please contact Berkeley Lab's Technology Transfer
                      9:  * Department at TTD@lbl.gov.
                     10:  *
                     11:  * NOTICE.  This software is owned by the U.S. Department of Energy.
                     12:  * As such, the U.S. Government has been granted for itself and others
                     13:  * acting on its behalf a paid-up, nonexclusive, irrevocable,
                     14:  * worldwide license in the Software to reproduce, prepare derivative
                     15:  * works, and perform publicly and display publicly.  Beginning five
                     16:  * (5) years after the date permission to assert copyright is obtained
                     17:  * from the U.S. Department of Energy, and subject to any subsequent
                     18:  * five (5) year renewals, the U.S. Government is granted for itself
                     19:  * and others acting on its behalf a paid-up, nonexclusive,
                     20:  * irrevocable, worldwide license in the Software to reproduce,
                     21:  * prepare derivative works, distribute copies to the public, perform
                     22:  * publicly and display publicly, and to permit others to do so.
                     23:  *
                     24:  * This code is distributed under a BSD style license, see the LICENSE
                     25:  * file for complete information.
                     26:  */
                     27: #include "iperf_config.h"
                     28: 
                     29: #include <stdio.h>
                     30: #include <unistd.h>
                     31: #include <errno.h>
1.1.1.2   misho      32: #include <arpa/inet.h>
1.1       misho      33: #include <sys/socket.h>
                     34: #include <sys/types.h>
                     35: #include <netinet/in.h>
                     36: #include <assert.h>
                     37: #include <netdb.h>
                     38: #include <string.h>
1.1.1.2   misho      39: #include <fcntl.h>
                     40: #include <limits.h>
1.1       misho      41: 
                     42: #ifdef HAVE_SENDFILE
                     43: #ifdef linux
                     44: #include <sys/sendfile.h>
                     45: #else
                     46: #ifdef __FreeBSD__
                     47: #include <sys/uio.h>
                     48: #else
                     49: #if defined(__APPLE__) && defined(__MACH__)    /* OS X */
                     50: #include <AvailabilityMacros.h>
                     51: #if defined(MAC_OS_X_VERSION_10_6)
                     52: #include <sys/uio.h>
                     53: #endif
                     54: #endif
                     55: #endif
                     56: #endif
                     57: #endif /* HAVE_SENDFILE */
                     58: 
1.1.1.2   misho      59: #ifdef HAVE_POLL_H
                     60: #include <poll.h>
                     61: #endif /* HAVE_POLL_H */
                     62: 
1.1.1.3 ! misho      63: #include "iperf.h"
1.1       misho      64: #include "iperf_util.h"
                     65: #include "net.h"
                     66: #include "timer.h"
                     67: 
1.1.1.3 ! misho      68: static int nread_read_timeout = 10;
        !            69: static int nread_overall_timeout = 30;
        !            70: 
1.1.1.2   misho      71: /*
                     72:  * Declaration of gerror in iperf_error.c.  Most other files in iperf3 can get this
                     73:  * by including "iperf.h", but net.c lives "below" this layer.  Clearly the
                     74:  * presence of this declaration is a sign we need to revisit this layering.
                     75:  */
                     76: extern int gerror;
                     77: 
                     78: /*
                     79:  * timeout_connect adapted from netcat, via OpenBSD and FreeBSD
                     80:  * Copyright (c) 2001 Eric Jackson <ericj@monkey.org>
                     81:  */
                     82: int
                     83: timeout_connect(int s, const struct sockaddr *name, socklen_t namelen,
                     84:     int timeout)
                     85: {
                     86:        struct pollfd pfd;
                     87:        socklen_t optlen;
                     88:        int flags, optval;
                     89:        int ret;
                     90: 
                     91:        flags = 0;
                     92:        if (timeout != -1) {
                     93:                flags = fcntl(s, F_GETFL, 0);
                     94:                if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1)
                     95:                        return -1;
                     96:        }
                     97: 
                     98:        if ((ret = connect(s, name, namelen)) != 0 && errno == EINPROGRESS) {
                     99:                pfd.fd = s;
                    100:                pfd.events = POLLOUT;
                    101:                if ((ret = poll(&pfd, 1, timeout)) == 1) {
                    102:                        optlen = sizeof(optval);
                    103:                        if ((ret = getsockopt(s, SOL_SOCKET, SO_ERROR,
                    104:                            &optval, &optlen)) == 0) {
                    105:                                errno = optval;
                    106:                                ret = optval == 0 ? 0 : -1;
                    107:                        }
                    108:                } else if (ret == 0) {
                    109:                        errno = ETIMEDOUT;
                    110:                        ret = -1;
                    111:                } else
                    112:                        ret = -1;
                    113:        }
                    114: 
                    115:        if (timeout != -1 && fcntl(s, F_SETFL, flags) == -1)
                    116:                ret = -1;
                    117: 
                    118:        return (ret);
                    119: }
                    120: 
1.1       misho     121: /* netdial and netannouce code comes from libtask: http://swtch.com/libtask/
                    122:  * Copyright: http://swtch.com/libtask/COPYRIGHT
                    123: */
                    124: 
1.1.1.3 ! misho     125: /* create a socket */
1.1       misho     126: int
1.1.1.3 ! misho     127: create_socket(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, struct addrinfo **server_res_out)
1.1       misho     128: {
1.1.1.3 ! misho     129:     struct addrinfo hints, *local_res = NULL, *server_res = NULL;
1.1.1.2   misho     130:     int s, saved_errno;
1.1.1.3 ! misho     131:     char portstr[6];
1.1       misho     132: 
                    133:     if (local) {
                    134:         memset(&hints, 0, sizeof(hints));
                    135:         hints.ai_family = domain;
                    136:         hints.ai_socktype = proto;
1.1.1.2   misho     137:         if ((gerror = getaddrinfo(local, NULL, &hints, &local_res)) != 0)
1.1       misho     138:             return -1;
                    139:     }
                    140: 
                    141:     memset(&hints, 0, sizeof(hints));
                    142:     hints.ai_family = domain;
                    143:     hints.ai_socktype = proto;
1.1.1.3 ! misho     144:     snprintf(portstr, sizeof(portstr), "%d", port);
        !           145:     if ((gerror = getaddrinfo(server, portstr, &hints, &server_res)) != 0) {
        !           146:        if (local)
        !           147:            freeaddrinfo(local_res);
1.1       misho     148:         return -1;
1.1.1.3 ! misho     149:     }
1.1       misho     150: 
                    151:     s = socket(server_res->ai_family, proto, 0);
                    152:     if (s < 0) {
                    153:        if (local)
                    154:            freeaddrinfo(local_res);
                    155:        freeaddrinfo(server_res);
                    156:         return -1;
                    157:     }
                    158: 
1.1.1.3 ! misho     159:     if (bind_dev) {
        !           160: #if defined(HAVE_SO_BINDTODEVICE)
        !           161:         if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
        !           162:                        bind_dev, IFNAMSIZ) < 0)
        !           163: #endif // HAVE_SO_BINDTODEVICE
        !           164:         {
        !           165:             saved_errno = errno;
        !           166:             close(s);
        !           167:             freeaddrinfo(local_res);
        !           168:             freeaddrinfo(server_res);
        !           169:             errno = saved_errno;
        !           170:             return -1;
        !           171:         }
        !           172:     }
        !           173: 
1.1.1.2   misho     174:     /* Bind the local address if given a name (with or without --cport) */
1.1       misho     175:     if (local) {
                    176:         if (local_port) {
                    177:             struct sockaddr_in *lcladdr;
                    178:             lcladdr = (struct sockaddr_in *)local_res->ai_addr;
                    179:             lcladdr->sin_port = htons(local_port);
                    180:         }
                    181: 
                    182:         if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
1.1.1.2   misho     183:            saved_errno = errno;
1.1       misho     184:            close(s);
                    185:            freeaddrinfo(local_res);
                    186:            freeaddrinfo(server_res);
1.1.1.2   misho     187:            errno = saved_errno;
1.1       misho     188:             return -1;
                    189:        }
                    190:         freeaddrinfo(local_res);
                    191:     }
1.1.1.2   misho     192:     /* No local name, but --cport given */
                    193:     else if (local_port) {
                    194:        size_t addrlen;
                    195:        struct sockaddr_storage lcl;
                    196: 
                    197:        /* IPv4 */
                    198:        if (server_res->ai_family == AF_INET) {
                    199:            struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl;
                    200:            lcladdr->sin_family = AF_INET;
                    201:            lcladdr->sin_port = htons(local_port);
                    202:            lcladdr->sin_addr.s_addr = INADDR_ANY;
                    203:            addrlen = sizeof(struct sockaddr_in);
                    204:        }
                    205:        /* IPv6 */
                    206:        else if (server_res->ai_family == AF_INET6) {
                    207:            struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl;
                    208:            lcladdr->sin6_family = AF_INET6;
                    209:            lcladdr->sin6_port = htons(local_port);
                    210:            lcladdr->sin6_addr = in6addr_any;
                    211:            addrlen = sizeof(struct sockaddr_in6);
                    212:        }
                    213:        /* Unknown protocol */
                    214:        else {
1.1.1.3 ! misho     215:            close(s);
        !           216:            freeaddrinfo(server_res);
1.1.1.2   misho     217:            errno = EAFNOSUPPORT;
                    218:             return -1;
                    219:        }
                    220: 
                    221:         if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) {
                    222:            saved_errno = errno;
                    223:            close(s);
                    224:            freeaddrinfo(server_res);
                    225:            errno = saved_errno;
                    226:             return -1;
                    227:         }
                    228:     }
1.1       misho     229: 
1.1.1.3 ! misho     230:     *server_res_out = server_res;
        !           231:     return s;
        !           232: }
        !           233: 
        !           234: /* make connection to server */
        !           235: int
        !           236: netdial(int domain, int proto, const char *local, const char *bind_dev, int local_port, const char *server, int port, int timeout)
        !           237: {
        !           238:     struct addrinfo *server_res = NULL;
        !           239:     int s, saved_errno;
        !           240: 
        !           241:     s = create_socket(domain, proto, local, bind_dev, local_port, server, port, &server_res);
        !           242:     if (s < 0) {
        !           243:       return -1;
        !           244:     }
        !           245: 
1.1.1.2   misho     246:     if (timeout_connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen, timeout) < 0 && errno != EINPROGRESS) {
                    247:        saved_errno = errno;
1.1       misho     248:        close(s);
                    249:        freeaddrinfo(server_res);
1.1.1.2   misho     250:        errno = saved_errno;
1.1       misho     251:         return -1;
                    252:     }
                    253: 
                    254:     freeaddrinfo(server_res);
                    255:     return s;
                    256: }
                    257: 
                    258: /***************************************************************/
                    259: 
                    260: int
1.1.1.3 ! misho     261: netannounce(int domain, int proto, const char *local, const char *bind_dev, int port)
1.1       misho     262: {
                    263:     struct addrinfo hints, *res;
                    264:     char portstr[6];
1.1.1.2   misho     265:     int s, opt, saved_errno;
1.1       misho     266: 
                    267:     snprintf(portstr, 6, "%d", port);
                    268:     memset(&hints, 0, sizeof(hints));
1.1.1.3 ! misho     269:     /*
1.1       misho     270:      * If binding to the wildcard address with no explicit address
                    271:      * family specified, then force us to get an AF_INET6 socket.  On
                    272:      * CentOS 6 and MacOS, getaddrinfo(3) with AF_UNSPEC in ai_family,
                    273:      * and ai_flags containing AI_PASSIVE returns a result structure
                    274:      * with ai_family set to AF_INET, with the result that we create
                    275:      * and bind an IPv4 address wildcard address and by default, we
                    276:      * can't accept IPv6 connections.
                    277:      *
                    278:      * On FreeBSD, under the above circumstances, ai_family in the
                    279:      * result structure is set to AF_INET6.
                    280:      */
                    281:     if (domain == AF_UNSPEC && !local) {
                    282:        hints.ai_family = AF_INET6;
                    283:     }
                    284:     else {
                    285:        hints.ai_family = domain;
                    286:     }
                    287:     hints.ai_socktype = proto;
                    288:     hints.ai_flags = AI_PASSIVE;
1.1.1.2   misho     289:     if ((gerror = getaddrinfo(local, portstr, &hints, &res)) != 0)
1.1.1.3 ! misho     290:         return -1;
1.1       misho     291: 
                    292:     s = socket(res->ai_family, proto, 0);
                    293:     if (s < 0) {
                    294:        freeaddrinfo(res);
                    295:         return -1;
                    296:     }
                    297: 
1.1.1.3 ! misho     298:     if (bind_dev) {
        !           299: #if defined(HAVE_SO_BINDTODEVICE)
        !           300:         if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
        !           301:                        bind_dev, IFNAMSIZ) < 0)
        !           302: #endif // HAVE_SO_BINDTODEVICE
        !           303:         {
        !           304:             saved_errno = errno;
        !           305:             close(s);
        !           306:             freeaddrinfo(res);
        !           307:             errno = saved_errno;
        !           308:             return -1;
        !           309:         }
        !           310:     }
        !           311: 
1.1       misho     312:     opt = 1;
1.1.1.3 ! misho     313:     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
1.1       misho     314:                   (char *) &opt, sizeof(opt)) < 0) {
1.1.1.2   misho     315:        saved_errno = errno;
1.1       misho     316:        close(s);
                    317:        freeaddrinfo(res);
1.1.1.2   misho     318:        errno = saved_errno;
1.1       misho     319:        return -1;
                    320:     }
                    321:     /*
                    322:      * If we got an IPv6 socket, figure out if it should accept IPv4
                    323:      * connections as well.  We do that if and only if no address
                    324:      * family was specified explicitly.  Note that we can only
                    325:      * do this if the IPV6_V6ONLY socket option is supported.  Also,
                    326:      * OpenBSD explicitly omits support for IPv4-mapped addresses,
                    327:      * even though it implements IPV6_V6ONLY.
                    328:      */
                    329: #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
                    330:     if (res->ai_family == AF_INET6 && (domain == AF_UNSPEC || domain == AF_INET6)) {
                    331:        if (domain == AF_UNSPEC)
                    332:            opt = 0;
                    333:        else
                    334:            opt = 1;
1.1.1.3 ! misho     335:        if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
1.1       misho     336:                       (char *) &opt, sizeof(opt)) < 0) {
1.1.1.2   misho     337:            saved_errno = errno;
1.1       misho     338:            close(s);
                    339:            freeaddrinfo(res);
1.1.1.2   misho     340:            errno = saved_errno;
1.1       misho     341:            return -1;
                    342:        }
                    343:     }
                    344: #endif /* IPV6_V6ONLY */
                    345: 
                    346:     if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
1.1.1.2   misho     347:         saved_errno = errno;
1.1       misho     348:         close(s);
                    349:        freeaddrinfo(res);
1.1.1.2   misho     350:         errno = saved_errno;
1.1       misho     351:         return -1;
                    352:     }
                    353: 
                    354:     freeaddrinfo(res);
1.1.1.3 ! misho     355: 
1.1       misho     356:     if (proto == SOCK_STREAM) {
1.1.1.2   misho     357:         if (listen(s, INT_MAX) < 0) {
                    358:            saved_errno = errno;
1.1       misho     359:            close(s);
1.1.1.2   misho     360:            errno = saved_errno;
1.1       misho     361:             return -1;
                    362:         }
                    363:     }
                    364: 
                    365:     return s;
                    366: }
                    367: 
                    368: 
                    369: /*******************************************************************/
                    370: /* reads 'count' bytes from a socket  */
                    371: /********************************************************************/
                    372: 
                    373: int
                    374: Nread(int fd, char *buf, size_t count, int prot)
                    375: {
                    376:     register ssize_t r;
                    377:     register size_t nleft = count;
1.1.1.3 ! misho     378:     struct iperf_time ftimeout = { 0, 0 };
        !           379: 
        !           380:     fd_set rfdset;
        !           381:     struct timeval timeout = { nread_read_timeout, 0 };
        !           382: 
        !           383:     /*
        !           384:      * fd might not be ready for reading on entry. Check for this
        !           385:      * (with timeout) first.
        !           386:      *
        !           387:      * This check could go inside the while() loop below, except we're
        !           388:      * currently considering whether it might make sense to support a
        !           389:      * codepath that bypassese this check, for situations where we
        !           390:      * already know that fd has data on it (for example if we'd gotten
        !           391:      * to here as the result of a select() call.
        !           392:      */
        !           393:     {
        !           394:         FD_ZERO(&rfdset);
        !           395:         FD_SET(fd, &rfdset);
        !           396:         r = select(fd + 1, &rfdset, NULL, NULL, &timeout);
        !           397:         if (r < 0) {
        !           398:             return NET_HARDERROR;
        !           399:         }
        !           400:         if (r == 0) {
        !           401:             return 0;
        !           402:         }
        !           403:     }
1.1       misho     404: 
                    405:     while (nleft > 0) {
                    406:         r = read(fd, buf, nleft);
                    407:         if (r < 0) {
                    408:             if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
                    409:                 break;
                    410:             else
                    411:                 return NET_HARDERROR;
                    412:         } else if (r == 0)
                    413:             break;
                    414: 
                    415:         nleft -= r;
                    416:         buf += r;
1.1.1.3 ! misho     417: 
        !           418:         /*
        !           419:          * We need some more bytes but don't want to wait around
        !           420:          * forever for them. In the case of partial results, we need
        !           421:          * to be able to read some bytes every nread_timeout seconds.
        !           422:          */
        !           423:         if (nleft > 0) {
        !           424:             struct iperf_time now;
        !           425: 
        !           426:             /*
        !           427:              * Also, we have an approximate upper limit for the total time
        !           428:              * that a Nread call is supposed to take. We trade off accuracy
        !           429:              * of this timeout for a hopefully lower performance impact.
        !           430:              */
        !           431:             iperf_time_now(&now);
        !           432:             if (ftimeout.secs == 0) {
        !           433:                 ftimeout = now;
        !           434:                 iperf_time_add_usecs(&ftimeout, nread_overall_timeout * 1000000L);
        !           435:             }
        !           436:             if (iperf_time_compare(&ftimeout, &now) < 0) {
        !           437:                 break;
        !           438:             }
        !           439: 
        !           440:             FD_ZERO(&rfdset);
        !           441:             FD_SET(fd, &rfdset);
        !           442:             r = select(fd + 1, &rfdset, NULL, NULL, &timeout);
        !           443:             if (r < 0) {
        !           444:                 return NET_HARDERROR;
        !           445:             }
        !           446:             if (r == 0) {
        !           447:                 break;
        !           448:             }
        !           449:         }
1.1       misho     450:     }
                    451:     return count - nleft;
                    452: }
                    453: 
                    454: 
                    455: /*
                    456:  *                      N W R I T E
                    457:  */
                    458: 
                    459: int
                    460: Nwrite(int fd, const char *buf, size_t count, int prot)
                    461: {
                    462:     register ssize_t r;
                    463:     register size_t nleft = count;
                    464: 
                    465:     while (nleft > 0) {
                    466:        r = write(fd, buf, nleft);
                    467:        if (r < 0) {
                    468:            switch (errno) {
                    469:                case EINTR:
                    470:                case EAGAIN:
                    471: #if (EAGAIN != EWOULDBLOCK)
                    472:                case EWOULDBLOCK:
                    473: #endif
                    474:                return count - nleft;
                    475: 
                    476:                case ENOBUFS:
                    477:                return NET_SOFTERROR;
                    478: 
                    479:                default:
                    480:                return NET_HARDERROR;
                    481:            }
                    482:        } else if (r == 0)
                    483:            return NET_SOFTERROR;
                    484:        nleft -= r;
                    485:        buf += r;
                    486:     }
                    487:     return count;
                    488: }
                    489: 
                    490: 
                    491: int
                    492: has_sendfile(void)
                    493: {
                    494: #if defined(HAVE_SENDFILE)
                    495:     return 1;
                    496: #else /* HAVE_SENDFILE */
                    497:     return 0;
                    498: #endif /* HAVE_SENDFILE */
                    499: 
                    500: }
                    501: 
                    502: 
                    503: /*
                    504:  *                      N S E N D F I L E
                    505:  */
                    506: 
                    507: int
                    508: Nsendfile(int fromfd, int tofd, const char *buf, size_t count)
                    509: {
                    510: #if defined(HAVE_SENDFILE)
1.1.1.3 ! misho     511:     off_t offset;
1.1       misho     512: #if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6))
                    513:     off_t sent;
                    514: #endif
                    515:     register size_t nleft;
                    516:     register ssize_t r;
                    517: 
                    518:     nleft = count;
                    519:     while (nleft > 0) {
                    520:        offset = count - nleft;
                    521: #ifdef linux
                    522:        r = sendfile(tofd, fromfd, &offset, nleft);
                    523:        if (r > 0)
                    524:            nleft -= r;
                    525: #elif defined(__FreeBSD__)
                    526:        r = sendfile(fromfd, tofd, offset, nleft, NULL, &sent, 0);
                    527:        nleft -= sent;
                    528: #elif defined(__APPLE__) && defined(__MACH__) && defined(MAC_OS_X_VERSION_10_6)        /* OS X */
                    529:        sent = nleft;
                    530:        r = sendfile(fromfd, tofd, offset, &sent, NULL, 0);
                    531:        nleft -= sent;
                    532: #else
                    533:        /* Shouldn't happen. */
                    534:        r = -1;
                    535:        errno = ENOSYS;
                    536: #endif
                    537:        if (r < 0) {
                    538:            switch (errno) {
                    539:                case EINTR:
                    540:                case EAGAIN:
                    541: #if (EAGAIN != EWOULDBLOCK)
                    542:                case EWOULDBLOCK:
                    543: #endif
                    544:                if (count == nleft)
                    545:                    return NET_SOFTERROR;
                    546:                return count - nleft;
                    547: 
                    548:                case ENOBUFS:
                    549:                case ENOMEM:
                    550:                return NET_SOFTERROR;
                    551: 
                    552:                default:
                    553:                return NET_HARDERROR;
                    554:            }
                    555:        }
                    556: #ifdef linux
                    557:        else if (r == 0)
                    558:            return NET_SOFTERROR;
                    559: #endif
                    560:     }
                    561:     return count;
                    562: #else /* HAVE_SENDFILE */
                    563:     errno = ENOSYS;    /* error if somehow get called without HAVE_SENDFILE */
                    564:     return NET_HARDERROR;
                    565: #endif /* HAVE_SENDFILE */
                    566: }
                    567: 
                    568: /*************************************************************************/
                    569: 
                    570: int
                    571: setnonblocking(int fd, int nonblocking)
                    572: {
                    573:     int flags, newflags;
                    574: 
                    575:     flags = fcntl(fd, F_GETFL, 0);
                    576:     if (flags < 0) {
                    577:         perror("fcntl(F_GETFL)");
                    578:         return -1;
                    579:     }
                    580:     if (nonblocking)
                    581:        newflags = flags | (int) O_NONBLOCK;
                    582:     else
                    583:        newflags = flags & ~((int) O_NONBLOCK);
                    584:     if (newflags != flags)
                    585:        if (fcntl(fd, F_SETFL, newflags) < 0) {
                    586:            perror("fcntl(F_SETFL)");
                    587:            return -1;
                    588:        }
                    589:     return 0;
                    590: }
                    591: 
                    592: /****************************************************************************/
                    593: 
                    594: int
                    595: getsockdomain(int sock)
                    596: {
                    597:     struct sockaddr_storage sa;
                    598:     socklen_t len = sizeof(sa);
                    599: 
                    600:     if (getsockname(sock, (struct sockaddr *)&sa, &len) < 0) {
                    601:         return -1;
                    602:     }
                    603:     return ((struct sockaddr *) &sa)->sa_family;
                    604: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>