1/*++
2/* NAME
3/*	inet_connect 3
4/* SUMMARY
5/*	connect to TCP listener
6/* SYNOPSIS
7/*	#include <connect.h>
8/*
9/*	int	inet_windowsize;
10/*
11/*	int	inet_connect(addr, block_mode, timeout)
12/*	const char *addr;
13/*	int	block_mode;
14/*	int	timeout;
15/* DESCRIPTION
16/*	inet_connect connects to a TCP listener at
17/*	the specified address, and returns the resulting file descriptor.
18/*
19/*	Specify an inet_windowsize value > 0 to override the TCP
20/*	window size that the client advertises to the server.
21/*
22/*	Arguments:
23/* .IP addr
24/*	The destination to connect to. The format is host:port. If no
25/*	host is specified, a port on the local host is assumed.
26/*	Host and port information may be given in numerical form
27/*	or as symbolical names.
28/* .IP block_mode
29/*	Either NON_BLOCKING for a non-blocking socket, or BLOCKING for
30/*	blocking mode.
31/* .IP timeout
32/*	Bounds the number of seconds that the operation may take. Specify
33/*	a value <= 0 to disable the time limit.
34/* DIAGNOSTICS
35/*	The result is -1 when the connection could not be made.
36/*	The nature of the error is available via the global \fIerrno\fR
37/*	variable.
38/*	Fatal errors: other system call failures.
39/* LICENSE
40/* .ad
41/* .fi
42/*	The Secure Mailer license must be distributed with this software.
43/* AUTHOR(S)
44/*	Wietse Venema
45/*	IBM T.J. Watson Research
46/*	P.O. Box 704
47/*	Yorktown Heights, NY 10598, USA
48/*--*/
49
50/* System interfaces. */
51
52#include <sys_defs.h>
53#include <sys/socket.h>
54#include <netinet/in.h>
55#include <string.h>
56#include <unistd.h>
57#include <errno.h>
58#include <netdb.h>
59
60/* Utility library. */
61
62#include "mymalloc.h"
63#include "msg.h"
64#include "iostuff.h"
65#include "host_port.h"
66#include "sane_connect.h"
67#include "connect.h"
68#include "timed_connect.h"
69#include "myaddrinfo.h"
70#include "sock_addr.h"
71#include "inet_proto.h"
72
73static int inet_connect_one(struct addrinfo *, int, int);
74
75/* inet_connect - connect to TCP listener */
76
77int     inet_connect(const char *addr, int block_mode, int timeout)
78{
79    char   *buf;
80    char   *host;
81    char   *port;
82    const char *parse_err;
83    struct addrinfo *res;
84    struct addrinfo *res0;
85    int     aierr;
86    int     sock;
87    MAI_HOSTADDR_STR hostaddr;
88    INET_PROTO_INFO *proto_info;
89    int     found;
90
91    /*
92     * Translate address information to internal form. No host defaults to
93     * the local host.
94     */
95    buf = mystrdup(addr);
96    if ((parse_err = host_port(buf, &host, "localhost", &port, (char *) 0)) != 0)
97	msg_fatal("%s: %s", addr, parse_err);
98    if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res0)) != 0)
99	msg_fatal("host/service %s/%s not found: %s",
100		  host, port, MAI_STRERROR(aierr));
101    myfree(buf);
102
103    proto_info = inet_proto_info();
104    for (sock = -1, found = 0, res = res0; res != 0; res = res->ai_next) {
105
106	/*
107	 * Safety net.
108	 */
109	if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
110	    msg_info("skipping address family %d for host %s",
111		     res->ai_family, host);
112	    continue;
113	}
114	found++;
115
116	/*
117	 * In case of multiple addresses, show what address we're trying now.
118	 */
119	if (msg_verbose) {
120	    SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen,
121				 &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
122	    msg_info("trying... [%s]", hostaddr.buf);
123	}
124	if ((sock = inet_connect_one(res, block_mode, timeout)) < 0) {
125	    if (msg_verbose)
126		msg_info("%m");
127	} else
128	    break;
129    }
130    if (found == 0)
131	msg_fatal("host not found: %s", addr);
132    freeaddrinfo(res0);
133    return (sock);
134}
135
136/* inet_connect_one - try to connect to one address */
137
138static int inet_connect_one(struct addrinfo * res, int block_mode, int timeout)
139{
140    int     sock;
141
142    /*
143     * Create a client socket.
144     */
145    sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
146    if (sock < 0)
147	return (-1);
148
149    /*
150     * Window scaling workaround.
151     */
152    if (inet_windowsize > 0)
153	set_inet_windowsize(sock, inet_windowsize);
154
155    /*
156     * Timed connect.
157     */
158    if (timeout > 0) {
159	non_blocking(sock, NON_BLOCKING);
160	if (timed_connect(sock, res->ai_addr, res->ai_addrlen, timeout) < 0) {
161	    close(sock);
162	    return (-1);
163	}
164	if (block_mode != NON_BLOCKING)
165	    non_blocking(sock, block_mode);
166	return (sock);
167    }
168
169    /*
170     * Maybe block until connected.
171     */
172    else {
173	non_blocking(sock, block_mode);
174	if (sane_connect(sock, res->ai_addr, res->ai_addrlen) < 0
175	    && errno != EINPROGRESS) {
176	    close(sock);
177	    return (-1);
178	}
179	return (sock);
180    }
181}
182