Return to connecthostport.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / miniupnpc / src |
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: }