Annotation of embedaddon/miniupnpc/src/connecthostport.c, revision 1.1.1.1

1.1       misho       1: /* $Id: connecthostport.c,v 1.24 2020/11/09 19:26:53 nanard Exp $ */
                      2: /* vim: tabstop=4 shiftwidth=4 noexpandtab
                      3:  * Project : miniupnp
                      4:  * Author : Thomas Bernard
                      5:  * Copyright (c) 2010-2020 Thomas Bernard
                      6:  * This software is subject to the conditions detailed in the
                      7:  * LICENCE file provided in this distribution. */
                      8: 
                      9: /* use getaddrinfo() or gethostbyname()
                     10:  * uncomment the following line in order to use gethostbyname() */
                     11: #ifdef NO_GETADDRINFO
                     12: #define USE_GETHOSTBYNAME
                     13: #endif
                     14: 
                     15: #include <string.h>
                     16: #include <stdio.h>
                     17: #ifdef _WIN32
                     18: #include <winsock2.h>
                     19: #include <ws2tcpip.h>
                     20: #include <io.h>
                     21: #define MAXHOSTNAMELEN 64
                     22: #include "win32_snprintf.h"
                     23: #define herror
                     24: #define socklen_t int
                     25: #else /* #ifdef _WIN32 */
                     26: #include <unistd.h>
                     27: #include <sys/types.h>
                     28: #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
                     29: #include <sys/time.h>
                     30: #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
                     31: #include <sys/param.h>
                     32: #include <sys/select.h>
                     33: #include <errno.h>
                     34: #define closesocket close
                     35: #include <netdb.h>
                     36: #include <netinet/in.h>
                     37: /* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
                     38:  * during the connect() call */
                     39: #define MINIUPNPC_IGNORE_EINTR
                     40: #include <sys/socket.h>
                     41: #include <sys/select.h>
                     42: #endif /* #else _WIN32 */
                     43: 
                     44: #if defined(__amigaos__) || defined(__amigaos4__)
                     45: #define herror(A) printf("%s\n", A)
                     46: #endif
                     47: 
                     48: #include "connecthostport.h"
                     49: 
                     50: #ifndef MAXHOSTNAMELEN
                     51: #define MAXHOSTNAMELEN 64
                     52: #endif
                     53: 
                     54: /* connecthostport()
                     55:  * return a socket connected (TCP) to the host and port
                     56:  * or -1 in case of error */
                     57: SOCKET connecthostport(const char * host, unsigned short port,
                     58:                        unsigned int scope_id)
                     59: {
                     60:    SOCKET s;
                     61:    int n;
                     62: #ifdef USE_GETHOSTBYNAME
                     63:    struct sockaddr_in dest;
                     64:    struct hostent *hp;
                     65: #else /* #ifdef USE_GETHOSTBYNAME */
                     66:    char tmp_host[MAXHOSTNAMELEN+1];
                     67:    char port_str[8];
                     68:    struct addrinfo *ai, *p;
                     69:    struct addrinfo hints;
                     70: #endif /* #ifdef USE_GETHOSTBYNAME */
                     71: #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
                     72:    struct timeval timeout;
                     73: #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
                     74: 
                     75: #ifdef USE_GETHOSTBYNAME
                     76:    hp = gethostbyname(host);
                     77:    if(hp == NULL)
                     78:    {
                     79:        herror(host);
                     80:        return INVALID_SOCKET;
                     81:    }
                     82:    memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
                     83:    memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
                     84:    s = socket(PF_INET, SOCK_STREAM, 0);
                     85:    if(ISINVALID(s))
                     86:    {
                     87:        PRINT_SOCKET_ERROR("socket");
                     88:        return INVALID_SOCKET;
                     89:    }
                     90: #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
                     91:    /* setting a 3 seconds timeout for the connect() call */
                     92:    timeout.tv_sec = 3;
                     93:    timeout.tv_usec = 0;
                     94:    if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
                     95:    {
                     96:        PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO");
                     97:    }
                     98:    timeout.tv_sec = 3;
                     99:    timeout.tv_usec = 0;
                    100:    if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
                    101:    {
                    102:        PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO");
                    103:    }
                    104: #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
                    105:    dest.sin_family = AF_INET;
                    106:    dest.sin_port = htons(port);
                    107:    n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
                    108: #ifdef MINIUPNPC_IGNORE_EINTR
                    109:    /* EINTR The system call was interrupted by a signal that was caught
                    110:     * EINPROGRESS The socket is nonblocking and the connection cannot
                    111:     *             be completed immediately. */
                    112:    while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
                    113:    {
                    114:        socklen_t len;
                    115:        fd_set wset;
                    116:        int err;
                    117:        FD_ZERO(&wset);
                    118:        FD_SET(s, &wset);
                    119: #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
                    120:        timeout.tv_sec = 3;
                    121:        timeout.tv_usec = 0;
                    122:        n = select(s + 1, NULL, &wset, NULL, &timeout);
                    123: #else
                    124:        n = select(s + 1, NULL, &wset, NULL, NULL);
                    125: #endif
                    126:        if(n == -1 && errno == EINTR)
                    127:            continue;
                    128: #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
                    129:        if(n == 0) {
                    130:            errno = ETIMEDOUT;
                    131:            n = -1;
                    132:            break;
                    133:        }
                    134: #endif
                    135:        /*len = 0;*/
                    136:        /*n = getpeername(s, NULL, &len);*/
                    137:        len = sizeof(err);
                    138:        if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
                    139:            PRINT_SOCKET_ERROR("getsockopt");
                    140:            closesocket(s);
                    141:            return INVALID_SOCKET;
                    142:        }
                    143:        if(err != 0) {
                    144:            errno = err;
                    145:            n = -1;
                    146:        }
                    147:    }
                    148: #endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
                    149:    if(n<0)
                    150:    {
                    151:        PRINT_SOCKET_ERROR("connect");
                    152:        closesocket(s);
                    153:        return INVALID_SOCKET;
                    154:    }
                    155: #else /* #ifdef USE_GETHOSTBYNAME */
                    156:    /* use getaddrinfo() instead of gethostbyname() */
                    157:    memset(&hints, 0, sizeof(hints));
                    158:    /* hints.ai_flags = AI_ADDRCONFIG; */
                    159: #ifdef AI_NUMERICSERV
                    160:    hints.ai_flags = AI_NUMERICSERV;
                    161: #endif
                    162:    hints.ai_socktype = SOCK_STREAM;
                    163:    hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
                    164:    /* hints.ai_protocol = IPPROTO_TCP; */
                    165:    snprintf(port_str, sizeof(port_str), "%hu", port);
                    166:    if(host[0] == '[')
                    167:    {
                    168:        /* literal ip v6 address */
                    169:        int i, j;
                    170:        for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++)
                    171:        {
                    172:            tmp_host[i] = host[j];
                    173:            if(0 == strncmp(host+j, "%25", 3))  /* %25 is just url encoding for '%' */
                    174:                j+=2;                           /* skip "25" */
                    175:        }
                    176:        tmp_host[i] = '\0';
                    177:    }
                    178:    else
                    179:    {
                    180:        strncpy(tmp_host, host, MAXHOSTNAMELEN);
                    181:    }
                    182:    tmp_host[MAXHOSTNAMELEN] = '\0';
                    183:    n = getaddrinfo(tmp_host, port_str, &hints, &ai);
                    184:    if(n != 0)
                    185:    {
                    186: #ifdef _WIN32
                    187:        fprintf(stderr, "getaddrinfo() error : %d\n", n);
                    188: #else
                    189:        fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
                    190: #endif
                    191:        return INVALID_SOCKET;
                    192:    }
                    193:    s = INVALID_SOCKET;
                    194:    for(p = ai; p; p = p->ai_next)
                    195:    {
                    196:        if(!ISINVALID(s))
                    197:            closesocket(s);
                    198: #ifdef DEBUG
                    199:        printf("ai_family=%d ai_socktype=%d ai_protocol=%d (PF_INET=%d, PF_INET6=%d)\n",
                    200:               p->ai_family, p->ai_socktype, p->ai_protocol, PF_INET, PF_INET6);
                    201: #endif
                    202:        s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
                    203:        if(ISINVALID(s))
                    204:            continue;
                    205:        if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) {
                    206:            struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr;
                    207:            addr6->sin6_scope_id = scope_id;
                    208:        }
                    209: #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
                    210:        /* setting a 3 seconds timeout for the connect() call */
                    211:        timeout.tv_sec = 3;
                    212:        timeout.tv_usec = 0;
                    213:        if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
                    214:        {
                    215:            PRINT_SOCKET_ERROR("setsockopt");
                    216:        }
                    217:        timeout.tv_sec = 3;
                    218:        timeout.tv_usec = 0;
                    219:        if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
                    220:        {
                    221:            PRINT_SOCKET_ERROR("setsockopt");
                    222:        }
                    223: #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
                    224:        n = connect(s, p->ai_addr, MSC_CAST_INT p->ai_addrlen);
                    225: #ifdef MINIUPNPC_IGNORE_EINTR
                    226:        /* EINTR The system call was interrupted by a signal that was caught
                    227:         * EINPROGRESS The socket is nonblocking and the connection cannot
                    228:         *             be completed immediately. */
                    229:        while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
                    230:        {
                    231:            socklen_t len;
                    232:            fd_set wset;
                    233:            int err;
                    234:            FD_ZERO(&wset);
                    235:            FD_SET(s, &wset);
                    236: #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
                    237:            timeout.tv_sec = 3;
                    238:            timeout.tv_usec = 0;
                    239:            n = select(s + 1, NULL, &wset, NULL, &timeout);
                    240: #else
                    241:            n = select(s + 1, NULL, &wset, NULL, NULL);
                    242: #endif
                    243:            if(n == -1 && errno == EINTR)
                    244:                continue;
                    245: #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
                    246:            if(n == 0) {
                    247:                errno = ETIMEDOUT;
                    248:                n = -1;
                    249:                break;
                    250:            }
                    251: #endif
                    252:            /*len = 0;*/
                    253:            /*n = getpeername(s, NULL, &len);*/
                    254:            len = sizeof(err);
                    255:            if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
                    256:                PRINT_SOCKET_ERROR("getsockopt");
                    257:                closesocket(s);
                    258:                freeaddrinfo(ai);
                    259:                return INVALID_SOCKET;
                    260:            }
                    261:            if(err != 0) {
                    262:                errno = err;
                    263:                n = -1;
                    264:            }
                    265:        }
                    266: #endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
                    267:        if(n >= 0)  /* connect() was successful */
                    268:            break;
                    269:    }
                    270:    freeaddrinfo(ai);
                    271:    if(ISINVALID(s))
                    272:    {
                    273:        PRINT_SOCKET_ERROR("socket");
                    274:        return INVALID_SOCKET;
                    275:    }
                    276:    if(n < 0)
                    277:    {
                    278:        PRINT_SOCKET_ERROR("connect");
                    279:        closesocket(s);
                    280:        return INVALID_SOCKET;
                    281:    }
                    282: #endif /* #ifdef USE_GETHOSTBYNAME */
                    283:    return s;
                    284: }

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