Annotation of embedaddon/miniupnpc/src/connecthostport.c, revision 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>