xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/inet_connect.c (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /*	$NetBSD: inet_connect.c,v 1.2 2022/10/08 16:12:50 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     proto_info = inet_proto_info();
114     for (sock = -1, found = 0, res = res0; res != 0; res = res->ai_next) {
115 
116 	/*
117 	 * Safety net.
118 	 */
119 	if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
120 	    msg_info("skipping address family %d for host %s",
121 		     res->ai_family, host);
122 	    continue;
123 	}
124 	found++;
125 
126 	/*
127 	 * In case of multiple addresses, show what address we're trying now.
128 	 */
129 	if (msg_verbose) {
130 	    SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen,
131 				 &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
132 	    msg_info("trying... [%s]", hostaddr.buf);
133 	}
134 	if ((sock = inet_connect_one(res, block_mode, timeout)) < 0) {
135 	    if (msg_verbose)
136 		msg_info("%m");
137 	} else
138 	    break;
139     }
140     if (found == 0)
141 	msg_fatal("host not found: %s", addr);
142     freeaddrinfo(res0);
143     return (sock);
144 }
145 
146 /* inet_connect_one - try to connect to one address */
147 
148 static int inet_connect_one(struct addrinfo * res, int block_mode, int timeout)
149 {
150     int     sock;
151 
152     /*
153      * Create a client socket.
154      */
155     sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
156     if (sock < 0)
157 	return (-1);
158 
159     /*
160      * Window scaling workaround.
161      */
162     if (inet_windowsize > 0)
163 	set_inet_windowsize(sock, inet_windowsize);
164 
165     /*
166      * Timed connect.
167      */
168     if (timeout > 0) {
169 	non_blocking(sock, NON_BLOCKING);
170 	if (timed_connect(sock, res->ai_addr, res->ai_addrlen, timeout) < 0) {
171 	    close(sock);
172 	    return (-1);
173 	}
174 	if (block_mode != NON_BLOCKING)
175 	    non_blocking(sock, block_mode);
176 	return (sock);
177     }
178 
179     /*
180      * Maybe block until connected.
181      */
182     else {
183 	non_blocking(sock, block_mode);
184 	if (sane_connect(sock, res->ai_addr, res->ai_addrlen) < 0
185 	    && errno != EINPROGRESS) {
186 	    close(sock);
187 	    return (-1);
188 	}
189 	return (sock);
190     }
191 }
192