1 /*	$NetBSD: inet_connect.c,v 1.1.1.1 2009/06/23 10:09:00 tron 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 
52 /* System interfaces. */
53 
54 #include <sys_defs.h>
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <string.h>
58 #include <unistd.h>
59 #include <errno.h>
60 #include <netdb.h>
61 
62 /* Utility library. */
63 
64 #include "mymalloc.h"
65 #include "msg.h"
66 #include "iostuff.h"
67 #include "host_port.h"
68 #include "sane_connect.h"
69 #include "connect.h"
70 #include "timed_connect.h"
71 #include "myaddrinfo.h"
72 #include "sock_addr.h"
73 #include "inet_proto.h"
74 
75 static int inet_connect_one(struct addrinfo *, int, int);
76 
77 /* inet_connect - connect to TCP listener */
78 
79 int     inet_connect(const char *addr, int block_mode, int timeout)
80 {
81     char   *buf;
82     char   *host;
83     char   *port;
84     const char *parse_err;
85     struct addrinfo *res;
86     struct addrinfo *res0;
87     int     aierr;
88     int     sock;
89     MAI_HOSTADDR_STR hostaddr;
90     INET_PROTO_INFO *proto_info;
91     int     found;
92 
93     /*
94      * Translate address information to internal form. No host defaults to
95      * the local host.
96      */
97     buf = mystrdup(addr);
98     if ((parse_err = host_port(buf, &host, "localhost", &port, (char *) 0)) != 0)
99 	msg_fatal("%s: %s", addr, parse_err);
100     if ((aierr = hostname_to_sockaddr(host, port, SOCK_STREAM, &res0)) != 0)
101 	msg_fatal("host/service %s/%s not found: %s",
102 		  host, port, MAI_STRERROR(aierr));
103     myfree(buf);
104 
105     proto_info = inet_proto_info();
106     for (sock = -1, found = 0, res = res0; res != 0; res = res->ai_next) {
107 
108 	/*
109 	 * Safety net.
110 	 */
111 	if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
112 	    msg_info("skipping address family %d for host %s",
113 		     res->ai_family, host);
114 	    continue;
115 	}
116 	found++;
117 
118 	/*
119 	 * In case of multiple addresses, show what address we're trying now.
120 	 */
121 	if (msg_verbose) {
122 	    SOCKADDR_TO_HOSTADDR(res->ai_addr, res->ai_addrlen,
123 				 &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
124 	    msg_info("trying... [%s]", hostaddr.buf);
125 	}
126 	if ((sock = inet_connect_one(res, block_mode, timeout)) < 0) {
127 	    if (msg_verbose)
128 		msg_info("%m");
129 	} else
130 	    break;
131     }
132     if (found == 0)
133 	msg_fatal("host not found: %s", addr);
134     freeaddrinfo(res0);
135     return (sock);
136 }
137 
138 /* inet_connect_one - try to connect to one address */
139 
140 static int inet_connect_one(struct addrinfo * res, int block_mode, int timeout)
141 {
142     int     sock;
143 
144     /*
145      * Create a client socket.
146      */
147     sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
148     if (sock < 0)
149 	return (-1);
150 
151     /*
152      * Window scaling workaround.
153      */
154     if (inet_windowsize > 0)
155 	set_inet_windowsize(sock, inet_windowsize);
156 
157     /*
158      * Timed connect.
159      */
160     if (timeout > 0) {
161 	non_blocking(sock, NON_BLOCKING);
162 	if (timed_connect(sock, res->ai_addr, res->ai_addrlen, timeout) < 0) {
163 	    close(sock);
164 	    return (-1);
165 	}
166 	if (block_mode != NON_BLOCKING)
167 	    non_blocking(sock, block_mode);
168 	return (sock);
169     }
170 
171     /*
172      * Maybe block until connected.
173      */
174     else {
175 	non_blocking(sock, block_mode);
176 	if (sane_connect(sock, res->ai_addr, res->ai_addrlen) < 0
177 	    && errno != EINPROGRESS) {
178 	    close(sock);
179 	    return (-1);
180 	}
181 	return (sock);
182     }
183 }
184