xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/inet_connect.c (revision c48c605c14fd8622b523d1d6a3f0c0bad133ea89)
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