File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpc / src / connecthostport.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Sep 27 11:21:37 2023 UTC (8 months, 4 weeks ago) by misho
Branches: miniupnpc, MAIN
CVS tags: v2_2_5p0, HEAD
Version 2.2.5p0

    1: /* $Id: connecthostport.c,v 1.1.1.1 2023/09/27 11:21:37 misho 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>