Path: blob/master/thirdparty/miniupnpc/src/connecthostport.c
9904 views
/* $Id: connecthostport.c,v 1.25 2025/05/24 15:59:08 nanard Exp $ */1/* vim: tabstop=4 shiftwidth=4 noexpandtab2* Project : miniupnp3* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/4* Author : Thomas Bernard5* Copyright (c) 2010-2025 Thomas Bernard6* This software is subject to the conditions detailed in the7* LICENCE file provided in this distribution. */89/* use getaddrinfo() or gethostbyname()10* uncomment the following line in order to use gethostbyname() */11#ifdef NO_GETADDRINFO12#define USE_GETHOSTBYNAME13#endif1415#include <string.h>16#include <stdio.h>17#ifdef _WIN3218#define WIN32_LEAN_AND_MEAN19#include <winsock2.h>20#include <ws2tcpip.h>21#include <io.h>22#define MAXHOSTNAMELEN 6423#include "win32_snprintf.h"24#define herror25#define socklen_t int26#else /* #ifdef _WIN32 */27#include <unistd.h>28#include <sys/types.h>29#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT30#include <sys/time.h>31#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */32#include <sys/param.h>33#include <sys/select.h>34#include <errno.h>35#define closesocket close36#include <netdb.h>37#include <netinet/in.h>38/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions39* during the connect() call */40#define MINIUPNPC_IGNORE_EINTR41#include <sys/socket.h>42#include <sys/select.h>43#endif /* #else _WIN32 */4445#if defined(__amigaos__) || defined(__amigaos4__)46#define herror(A) printf("%s\n", A)47#endif4849#include "connecthostport.h"5051#ifndef MAXHOSTNAMELEN52#define MAXHOSTNAMELEN 6453#endif5455/* connecthostport()56* return a socket connected (TCP) to the host and port57* or -1 in case of error */58SOCKET connecthostport(const char * host, unsigned short port,59unsigned int scope_id)60{61SOCKET s;62int n;63#ifdef USE_GETHOSTBYNAME64struct sockaddr_in dest;65struct hostent *hp;66#else /* #ifdef USE_GETHOSTBYNAME */67char tmp_host[MAXHOSTNAMELEN+1];68char port_str[8];69struct addrinfo *ai, *p;70struct addrinfo hints;71#endif /* #ifdef USE_GETHOSTBYNAME */72#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT73struct timeval timeout;74#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */7576#ifdef USE_GETHOSTBYNAME77hp = gethostbyname(host);78if(hp == NULL)79{80herror(host);81return INVALID_SOCKET;82}83memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));84memset(dest.sin_zero, 0, sizeof(dest.sin_zero));85s = socket(PF_INET, SOCK_STREAM, 0);86if(ISINVALID(s))87{88PRINT_SOCKET_ERROR("socket");89return INVALID_SOCKET;90}91#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT92/* setting a 3 seconds timeout for the connect() call */93timeout.tv_sec = 3;94timeout.tv_usec = 0;95if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)96{97PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO");98}99timeout.tv_sec = 3;100timeout.tv_usec = 0;101if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)102{103PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO");104}105#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */106dest.sin_family = AF_INET;107dest.sin_port = htons(port);108n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));109#ifdef MINIUPNPC_IGNORE_EINTR110/* EINTR The system call was interrupted by a signal that was caught111* EINPROGRESS The socket is nonblocking and the connection cannot112* be completed immediately. */113while(n < 0 && (errno == EINTR || errno == EINPROGRESS))114{115socklen_t len;116fd_set wset;117int err;118FD_ZERO(&wset);119FD_SET(s, &wset);120#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT121timeout.tv_sec = 3;122timeout.tv_usec = 0;123n = select(s + 1, NULL, &wset, NULL, &timeout);124#else125n = select(s + 1, NULL, &wset, NULL, NULL);126#endif127if(n < 0) {128if (errno == EINTR)129continue; /* try again */130else131break; /* EBADF, EFAULT, EINVAL */132}133#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT134if(n == 0) {135errno = ETIMEDOUT;136n = -1;137break;138}139#endif140len = sizeof(err);141if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {142PRINT_SOCKET_ERROR("getsockopt");143closesocket(s);144return INVALID_SOCKET;145}146if(err != 0) {147errno = err;148n = -1;149}150}151#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */152if(n<0)153{154PRINT_SOCKET_ERROR("connect");155closesocket(s);156return INVALID_SOCKET;157}158#else /* #ifdef USE_GETHOSTBYNAME */159/* use getaddrinfo() instead of gethostbyname() */160memset(&hints, 0, sizeof(hints));161/* hints.ai_flags = AI_ADDRCONFIG; */162#ifdef AI_NUMERICSERV163hints.ai_flags = AI_NUMERICSERV;164#endif165hints.ai_socktype = SOCK_STREAM;166hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */167/* hints.ai_protocol = IPPROTO_TCP; */168snprintf(port_str, sizeof(port_str), "%hu", port);169if(host[0] == '[')170{171/* literal ip v6 address */172int i, j;173for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++)174{175tmp_host[i] = host[j];176if(0 == strncmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */177j+=2; /* skip "25" */178}179tmp_host[i] = '\0';180}181else182{183strncpy(tmp_host, host, MAXHOSTNAMELEN);184}185tmp_host[MAXHOSTNAMELEN] = '\0';186n = getaddrinfo(tmp_host, port_str, &hints, &ai);187if(n != 0)188{189#ifdef _WIN32190fprintf(stderr, "getaddrinfo() error : %d\n", n);191#else192fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));193#endif194return INVALID_SOCKET;195}196s = INVALID_SOCKET;197for(p = ai; p; p = p->ai_next)198{199if(!ISINVALID(s))200closesocket(s);201#ifdef DEBUG202printf("ai_family=%d ai_socktype=%d ai_protocol=%d (PF_INET=%d, PF_INET6=%d)\n",203p->ai_family, p->ai_socktype, p->ai_protocol, PF_INET, PF_INET6);204#endif205s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);206if(ISINVALID(s))207continue;208if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) {209struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr;210addr6->sin6_scope_id = scope_id;211}212#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT213/* setting a 3 seconds timeout for the connect() call */214timeout.tv_sec = 3;215timeout.tv_usec = 0;216if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)217{218PRINT_SOCKET_ERROR("setsockopt");219}220timeout.tv_sec = 3;221timeout.tv_usec = 0;222if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)223{224PRINT_SOCKET_ERROR("setsockopt");225}226#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */227n = connect(s, p->ai_addr, MSC_CAST_INT p->ai_addrlen);228#ifdef MINIUPNPC_IGNORE_EINTR229/* EINTR The system call was interrupted by a signal that was caught230* EINPROGRESS The socket is nonblocking and the connection cannot231* be completed immediately. */232while(n < 0 && (errno == EINTR || errno == EINPROGRESS))233{234socklen_t len;235fd_set wset;236int err;237FD_ZERO(&wset);238FD_SET(s, &wset);239#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT240timeout.tv_sec = 3;241timeout.tv_usec = 0;242n = select(s + 1, NULL, &wset, NULL, &timeout);243#else244n = select(s + 1, NULL, &wset, NULL, NULL);245#endif246if(n < 0) {247if (errno == EINTR)248continue; /* try again */249else250break; /* EBADF, EFAULT, EINVAL */251}252#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT253if(n == 0) {254errno = ETIMEDOUT;255n = -1;256break;257}258#endif259len = sizeof(err);260if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {261PRINT_SOCKET_ERROR("getsockopt");262closesocket(s);263freeaddrinfo(ai);264return INVALID_SOCKET;265}266if(err != 0) {267errno = err;268n = -1;269}270}271#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */272if(n >= 0) /* connect() was successful */273break;274}275freeaddrinfo(ai);276if(ISINVALID(s))277{278PRINT_SOCKET_ERROR("socket");279return INVALID_SOCKET;280}281if(n < 0)282{283PRINT_SOCKET_ERROR("connect");284closesocket(s);285return INVALID_SOCKET;286}287#endif /* #ifdef USE_GETHOSTBYNAME */288return s;289}290291292