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