1/* $NetBSD: inet_connect.c,v 1.3 2023/12/23 20:30:46 christos Exp $ */ 2 3/*++ 4/* NAME 5/* inet_connect 3 6/* SUMMARY 7/* connect to TCP listener 8/* SYNOPSIS 9/* #include <connect.h> 10/* 11/* int inet_windowsize; 12/* 13/* int inet_connect(addr, block_mode, timeout) 14/* const char *addr; 15/* int block_mode; 16/* int timeout; 17/* DESCRIPTION 18/* inet_connect connects to a TCP listener at 19/* the specified address, and returns the resulting file descriptor. 20/* 21/* Specify an inet_windowsize value > 0 to override the TCP 22/* window size that the client advertises to the server. 23/* 24/* Arguments: 25/* .IP addr 26/* The destination to connect to. The format is host:port. If no 27/* host is specified, a port on the local host is assumed. 28/* Host and port information may be given in numerical form 29/* or as symbolical names. 30/* .IP block_mode 31/* Either NON_BLOCKING for a non-blocking socket, or BLOCKING for 32/* blocking mode. 33/* .IP timeout 34/* Bounds the number of seconds that the operation may take. Specify 35/* a value <= 0 to disable the time limit. 36/* DIAGNOSTICS 37/* The result is -1 when the connection could not be made. 38/* The nature of the error is available via the global \fIerrno\fR 39/* variable. 40/* Fatal errors: other system call failures. 41/* LICENSE 42/* .ad 43/* .fi 44/* The Secure Mailer license must be distributed with this software. 45/* AUTHOR(S) 46/* Wietse Venema 47/* IBM T.J. Watson Research 48/* P.O. Box 704 49/* Yorktown Heights, NY 10598, USA 50/* 51/* Wietse Venema 52/* Google, Inc. 53/* 111 8th Avenue 54/* New York, NY 10011, USA 55/*--*/ 56 57/* System interfaces. */ 58 59#include <sys_defs.h> 60#include <sys/socket.h> 61#include <netinet/in.h> 62#include <string.h> 63#include <unistd.h> 64#include <errno.h> 65#include <netdb.h> 66 67/* Utility library. */ 68 69#include "mymalloc.h" 70#include "msg.h" 71#include "iostuff.h" 72#include "host_port.h" 73#include "sane_connect.h" 74#include "connect.h" 75#include "timed_connect.h" 76#include "myaddrinfo.h" 77#include "sock_addr.h" 78#include "inet_proto.h" 79 80static int inet_connect_one(struct addrinfo *, int, int); 81 82/* inet_connect - connect to TCP listener */ 83 84int inet_connect(const char *addr, int block_mode, int timeout) 85{ 86 char *buf; 87 char *host; 88 char *port; 89 const char *parse_err; 90 struct addrinfo *res; 91 struct addrinfo *res0; 92 int aierr; 93 int sock; 94 MAI_HOSTADDR_STR hostaddr; 95 const INET_PROTO_INFO *proto_info; 96 int found; 97 98 /* 99 * Translate address information to internal form. No host defaults to 100 * the local host. 101 */ 102 buf = mystrdup(addr); 103 if ((parse_err = host_port(buf, &host, "localhost", &port, (char *) 0)) != 0) 104 msg_fatal("%s: %s", addr, parse_err); 105 if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res0)) != 0) 106 msg_warn("host or service %s not found: %s", 107 addr, MAI_STRERROR(aierr)); 108 myfree(buf); 109 if (aierr) { 110 errno = EADDRNOTAVAIL; /* for up-stream "%m" */ 111 return (-1); 112 } 113 114 proto_info = inet_proto_info(); 115 for (sock = -1, found = 0, res = res0; res != 0; res = res->ai_next) { 116 117 /* 118 * Safety net. 119 */ 120 if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) { 121 msg_info("skipping address family %d for host %s", 122 res->ai_family, host); 123 continue; 124 } 125 found++; 126 127 /* 128 * In case of multiple addresses, show what address we're trying now. 129 */ 130 if (msg_verbose) { 131 SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen, 132 &hostaddr, (MAI_SERVPORT_STR *) 0, 0); 133 msg_info("trying... [%s]", hostaddr.buf); 134 } 135 if ((sock = inet_connect_one(res, block_mode, timeout)) < 0) { 136 if (msg_verbose) 137 msg_info("%m"); 138 } else 139 break; 140 } 141 if (found == 0) 142 msg_fatal("host not found: %s", addr); 143 freeaddrinfo(res0); 144 return (sock); 145} 146 147/* inet_connect_one - try to connect to one address */ 148 149static int inet_connect_one(struct addrinfo * res, int block_mode, int timeout) 150{ 151 int sock; 152 153 /* 154 * Create a client socket. 155 */ 156 sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 157 if (sock < 0) 158 return (-1); 159 160 /* 161 * Window scaling workaround. 162 */ 163 if (inet_windowsize > 0) 164 set_inet_windowsize(sock, inet_windowsize); 165 166 /* 167 * Timed connect. 168 */ 169 if (timeout > 0) { 170 non_blocking(sock, NON_BLOCKING); 171 if (timed_connect(sock, res->ai_addr, res->ai_addrlen, timeout) < 0) { 172 close(sock); 173 return (-1); 174 } 175 if (block_mode != NON_BLOCKING) 176 non_blocking(sock, block_mode); 177 return (sock); 178 } 179 180 /* 181 * Maybe block until connected. 182 */ 183 else { 184 non_blocking(sock, block_mode); 185 if (sane_connect(sock, res->ai_addr, res->ai_addrlen) < 0 186 && errno != EINPROGRESS) { 187 close(sock); 188 return (-1); 189 } 190 return (sock); 191 } 192} 193