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 80 static int inet_connect_one(struct addrinfo *, int, int); 81 82 /* inet_connect - connect to TCP listener */ 83 84 int 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 149 static 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