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