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