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
inet_connect(const char * addr,int block_mode,int timeout)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
inet_connect_one(struct addrinfo * res,int block_mode,int timeout)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