1 /*
2 * libEtPan! -- a mail stuff library
3 *
4 * Copyright (C) 2001, 2005 - DINH Viet Hoa
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the libEtPan! project nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * $Id: connect.c,v 1.29 2011/02/27 01:11:50 hoa Exp $
34 */
35
36 #ifdef HAVE_CONFIG_H
37 # include <config.h>
38 #endif
39
40 #include "connect.h"
41
42 #include "mailstream.h"
43
44 #include <sys/types.h>
45 #include <string.h>
46 #include <stdio.h>
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 #include <errno.h>
51 #include <fcntl.h>
52
53 #ifdef WIN32
54 # include "win_etpan.h"
55 #else
56 # include <netdb.h>
57 # include <netinet/in.h>
58 # include <sys/socket.h>
59 # ifdef HAVE_SYS_POLL_H
60 # include <sys/poll.h>
61 # endif
62 # include <unistd.h>
63 # include <arpa/inet.h>
64 #endif
65
mail_get_service_port(const char * name,char * protocol)66 uint16_t mail_get_service_port(const char * name, char * protocol)
67 {
68 struct servent * service;
69
70 service = getservbyname(name, protocol);
71
72 if (service == NULL)
73 return 0;
74
75 return ntohs(service->s_port);
76 }
77
prepare_fd(int fd)78 static int prepare_fd(int fd)
79 {
80 #ifndef WIN32
81 int fd_flags;
82 int r;
83
84 fd_flags = fcntl(fd, F_GETFL, 0);
85 fd_flags |= O_NDELAY;
86 r = fcntl(fd, F_SETFL, fd_flags);
87 if (r < 0)
88 return -1;
89 #endif
90
91 return 0;
92 }
93
94 #ifdef HAVE_IPV6
verify_sock_errors(int s)95 static int verify_sock_errors(int s)
96 {
97 socklen_t len;
98 int val;
99 len = sizeof(val);
100 if (getsockopt(s, SOL_SOCKET, SO_ERROR, &val, &len) < 0) {
101 return -1;
102 } else if (val != 0) {
103 return -1;
104 }
105 return 0;
106 }
107 #endif
108
wait_connect(int s,int r,time_t timeout_seconds)109 static int wait_connect(int s, int r, time_t timeout_seconds)
110 {
111 #if defined(WIN32) || !USE_POLL
112 fd_set fds;
113 #else
114 struct pollfd pfd;
115 #endif // WIN32
116 struct timeval timeout;
117
118 if (r == 0) {
119 /* connected immediately */
120 return 0;
121 }
122 else if (r == -1) {
123 if (errno == EINPROGRESS) {
124 /* select */
125 }
126 else {
127 return -1;
128 }
129 }
130
131 if (timeout_seconds == 0) {
132 timeout = mailstream_network_delay;
133 }
134 else {
135 timeout.tv_sec = timeout_seconds;
136 timeout.tv_usec = 0;
137 }
138
139 #if defined(WIN32) || !USE_POLL
140 FD_ZERO(&fds);
141 FD_SET(s, &fds);
142 /* TODO: how to cancel this ? -> could be cancelled using a cancel fd */
143 r = select(s + 1, NULL, &fds, NULL, &timeout);
144 if (r <= 0) {
145 return -1;
146 }
147
148 if (!FD_ISSET(s, &fds)) {
149 /* though, it's strange */
150 return -1;
151 }
152 #else
153 pfd.fd = s;
154 pfd.events = POLLOUT;
155 pfd.revents = 0;
156
157 r = poll(&pfd, 1, timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
158 if (r <= 0) {
159 return -1;
160 }
161
162 if (pfd.revents & POLLOUT != POLLOUT) {
163 return -1;
164 }
165 #endif
166
167 return 0;
168 }
169
mail_tcp_connect(const char * server,uint16_t port)170 int mail_tcp_connect(const char * server, uint16_t port)
171 {
172 return mail_tcp_connect_with_local_address(server, port, NULL, 0);
173 }
174
mail_tcp_connect_timeout(const char * server,uint16_t port,time_t timeout)175 int mail_tcp_connect_timeout(const char * server, uint16_t port, time_t timeout)
176 {
177 return mail_tcp_connect_with_local_address_timeout(server, port, NULL, 0, timeout);
178 }
179
mail_tcp_connect_with_local_address(const char * server,uint16_t port,const char * local_address,uint16_t local_port)180 int mail_tcp_connect_with_local_address(const char * server, uint16_t port,
181 const char * local_address, uint16_t local_port)
182 {
183 return mail_tcp_connect_with_local_address_timeout(server, port, local_address, local_port, 0);
184 }
185
186 #ifndef WIN32
187 #include <sys/un.h>
mail_unix_connect_socket(const char * path)188 int mail_unix_connect_socket(const char *path)
189 {
190 struct sockaddr_un sa;
191 int s;
192
193 if (sizeof(sa.sun_path) <= strlen(path)) {
194 return -1;
195 }
196
197 if (!(memcpy(sa.sun_path, path, strlen(path)+1))) {
198 return -1;
199 }
200 sa.sun_family = AF_UNIX;
201
202
203 if (0 > (s = socket(AF_UNIX, SOCK_STREAM, 0)))
204 return -1;
205
206 if (prepare_fd(s))
207 goto close_socket;
208 if (connect(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_un)))
209 goto close_socket;
210 return s;
211
212 close_socket:
213 close(s);
214 return -1;
215 }
216 #endif
217
mail_tcp_connect_with_local_address_timeout(const char * server,uint16_t port,const char * local_address,uint16_t local_port,time_t timeout)218 int mail_tcp_connect_with_local_address_timeout(const char * server, uint16_t port,
219 const char * local_address, uint16_t local_port, time_t timeout)
220 {
221 #ifndef HAVE_IPV6
222 struct hostent * remotehost;
223 struct sockaddr_in sa;
224 #else /* HAVE_IPV6 */
225 struct addrinfo hints, *res, *ai;
226 struct addrinfo la_hints;
227 char port_str[6];
228 #endif
229 #ifdef WIN32
230 SOCKET s;
231 long r;
232 #else
233 int s;
234 int r;
235
236 if ('/' == server[0])
237 return mail_unix_connect_socket(server);
238 #endif
239
240 #ifndef HAVE_IPV6
241 s = socket(PF_INET, SOCK_STREAM, 0);
242 if (s == -1)
243 goto err;
244
245 if ((local_address != NULL) || (local_port != 0)) {
246 struct sockaddr_in la;
247
248 la.sin_family = AF_INET;
249 la.sin_port = htons(local_port);
250 if (local_address == NULL) {
251 la.sin_addr.s_addr = htonl(INADDR_ANY);
252 }
253 r = inet_aton(local_address, &la.sin_addr);
254 if (r == 0)
255 goto close_socket;
256 r = bind(s, (struct sockaddr *) &la, sizeof(struct sockaddr_in));
257 if (r == -1)
258 goto close_socket;
259 }
260
261 remotehost = gethostbyname(server);
262 if (remotehost == NULL)
263 goto close_socket;
264
265 sa.sin_family = AF_INET;
266 sa.sin_port = htons(port);
267 memcpy(&sa.sin_addr, remotehost->h_addr, remotehost->h_length);
268
269 r = prepare_fd(s);
270 if (r == -1) {
271 goto close_socket;
272 }
273
274 r = connect(s, (struct sockaddr *) &sa, sizeof(struct sockaddr_in));
275 r = wait_connect(s, r, timeout);
276 if (r == -1) {
277 goto close_socket;
278 }
279 #else /* HAVE_IPV6 */
280 memset(&hints, 0, sizeof(hints));
281 hints.ai_family = AF_UNSPEC;
282 hints.ai_socktype = SOCK_STREAM;
283 hints.ai_protocol = IPPROTO_TCP;
284
285 memset(&la_hints, 0, sizeof(la_hints));
286 la_hints.ai_family = AF_UNSPEC;
287 la_hints.ai_socktype = SOCK_STREAM;
288 la_hints.ai_flags = AI_PASSIVE;
289
290 /* convert port from integer to string. */
291 snprintf(port_str, sizeof(port_str), "%d", port);
292
293 res = NULL;
294 if (getaddrinfo(server, port_str, &hints, &res) != 0)
295 goto err;
296
297 s = -1;
298 for (ai = res; ai != NULL; ai = ai->ai_next) {
299 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
300
301 if (s == -1)
302 continue;
303
304 // Christopher Lyon Anderson - prevent SigPipe
305 #ifdef SO_NOSIGPIPE
306 int kOne = 1;
307 int err = setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &kOne, sizeof(kOne));
308 if (err != 0)
309 continue;
310 #endif
311
312 if ((local_address != NULL) || (local_port != 0)) {
313 char local_port_str[6];
314 char * p_local_port_str;
315 struct addrinfo * la_res;
316
317 if (local_port != 0) {
318 snprintf(local_port_str, sizeof(local_port_str), "%d", local_port);
319 p_local_port_str = local_port_str;
320 }
321 else {
322 p_local_port_str = NULL;
323 }
324 la_res = NULL;
325 r = getaddrinfo(local_address, p_local_port_str, &la_hints, &la_res);
326 if (r != 0)
327 goto close_socket;
328 r = bind(s, (struct sockaddr *) la_res->ai_addr, la_res->ai_addrlen);
329 if (la_res != NULL)
330 freeaddrinfo(la_res);
331 if (r == -1)
332 goto close_socket;
333 }
334
335 r = prepare_fd(s);
336 if (r == -1) {
337 goto close_socket;
338 }
339
340 r = connect(s, ai->ai_addr, ai->ai_addrlen);
341 r = wait_connect(s, r, timeout);
342
343 if (r != -1) {
344 r = verify_sock_errors(s);
345 }
346
347 if (r == -1) {
348 if (ai->ai_next) {
349 #ifdef WIN32
350 closesocket(s);
351 #else
352 close(s);
353 #endif
354 continue;
355 } else {
356 goto close_socket;
357 }
358 }
359 /* if we're here, we're good */
360 break;
361 }
362
363 if (res != NULL)
364 freeaddrinfo(res);
365
366 if (ai == NULL)
367 goto err;
368 #endif
369 return s;
370
371 close_socket:
372 #ifdef HAVE_IPV6
373 if (res != NULL)
374 freeaddrinfo(res);
375 #endif
376 #ifdef WIN32
377 closesocket(s);
378 #else
379 close(s);
380 #endif
381 err:
382 return -1;
383 }
384