1 /*
2 * Copyright (C) 1998,2000 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 1999-2006,2008 Brendan Cully <brendan@kublai.com>
4 * Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21 #if HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include "mutt.h"
26 #include "mutt_socket.h"
27 #include "mutt_tunnel.h"
28 #if defined(USE_SSL)
29 # include "mutt_ssl.h"
30 #endif
31
32 #include "mutt_idna.h"
33
34 #include <unistd.h>
35 #include <netinet/in.h>
36 #include <netdb.h>
37 #include <stdlib.h>
38 #include <fcntl.h>
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_TIME_H
41 #include <sys/time.h>
42 #endif
43 #include <sys/socket.h>
44 #ifdef HAVE_SYS_SELECT_H
45 #include <sys/select.h>
46 #endif
47 #include <string.h>
48 #include <errno.h>
49
50 /* support for multiple socket connections */
51 static CONNECTION *Connections = NULL;
52
53 /* forward declarations */
54 static int socket_preconnect (void);
55 static int socket_connect (int fd, struct sockaddr* sa);
56 static CONNECTION* socket_new_conn (void);
57
58 /* Wrappers */
mutt_socket_open(CONNECTION * conn)59 int mutt_socket_open (CONNECTION* conn)
60 {
61 int rc;
62
63 if (socket_preconnect ())
64 return -1;
65
66 rc = conn->conn_open (conn);
67
68 dprint (2, (debugfile, "Connected to %s:%d on fd=%d\n",
69 NONULL (conn->account.host), conn->account.port, conn->fd));
70
71 return rc;
72 }
73
mutt_socket_close(CONNECTION * conn)74 int mutt_socket_close (CONNECTION* conn)
75 {
76 int rc = -1;
77
78 if (conn->fd < 0)
79 dprint (1, (debugfile, "mutt_socket_close: Attempt to close closed connection.\n"));
80 else
81 rc = conn->conn_close (conn);
82
83 conn->fd = -1;
84 conn->ssf = 0;
85
86 return rc;
87 }
88
mutt_socket_read(CONNECTION * conn,char * buf,size_t len)89 int mutt_socket_read (CONNECTION* conn, char* buf, size_t len)
90 {
91 int rc;
92
93 if (conn->fd < 0)
94 {
95 dprint (1, (debugfile, "mutt_socket_read: attempt to read from closed connection\n"));
96 return -1;
97 }
98
99 rc = conn->conn_read (conn, buf, len);
100 /* EOF */
101 if (rc == 0)
102 {
103 mutt_error (_("Connection to %s closed"), conn->account.host);
104 mutt_sleep (2);
105 }
106 if (rc <= 0)
107 mutt_socket_close (conn);
108
109 return rc;
110 }
111
mutt_socket_write_d(CONNECTION * conn,const char * buf,int len,int dbg)112 int mutt_socket_write_d (CONNECTION *conn, const char *buf, int len, int dbg)
113 {
114 int rc;
115 int sent = 0;
116
117 dprint (dbg, (debugfile,"%d> %s", conn->fd, buf));
118
119 if (conn->fd < 0)
120 {
121 dprint (1, (debugfile, "mutt_socket_write: attempt to write to closed connection\n"));
122 return -1;
123 }
124
125 if (len < 0)
126 len = mutt_strlen (buf);
127
128 while (sent < len)
129 {
130 if ((rc = conn->conn_write (conn, buf + sent, len - sent)) < 0)
131 {
132 dprint (1, (debugfile,
133 "mutt_socket_write: error writing (%s), closing socket\n",
134 strerror(errno)));
135 mutt_socket_close (conn);
136
137 return -1;
138 }
139
140 if (rc < len - sent)
141 dprint (3, (debugfile,
142 "mutt_socket_write: short write (%d of %d bytes)\n", rc,
143 len - sent));
144
145 sent += rc;
146 }
147
148 return sent;
149 }
150
151 /* poll whether reads would block.
152 * Returns: >0 if there is data to read,
153 * 0 if a read would block,
154 * -1 if this connection doesn't support polling */
mutt_socket_poll(CONNECTION * conn)155 int mutt_socket_poll (CONNECTION* conn)
156 {
157 if (conn->bufpos < conn->available)
158 return conn->available - conn->bufpos;
159
160 if (conn->conn_poll)
161 return conn->conn_poll (conn);
162
163 return -1;
164 }
165
166 /* simple read buffering to speed things up. */
mutt_socket_readchar(CONNECTION * conn,char * c)167 int mutt_socket_readchar (CONNECTION *conn, char *c)
168 {
169 if (conn->bufpos >= conn->available)
170 {
171 if (conn->fd >= 0)
172 conn->available = conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
173 else
174 {
175 dprint (1, (debugfile, "mutt_socket_readchar: attempt to read from closed connection.\n"));
176 return -1;
177 }
178 conn->bufpos = 0;
179 if (conn->available == 0)
180 {
181 mutt_error (_("Connection to %s closed"), conn->account.host);
182 mutt_sleep (2);
183 }
184 if (conn->available <= 0)
185 {
186 mutt_socket_close (conn);
187 return -1;
188 }
189 }
190 *c = conn->inbuf[conn->bufpos];
191 conn->bufpos++;
192 return 1;
193 }
194
mutt_socket_readln_d(char * buf,size_t buflen,CONNECTION * conn,int dbg)195 int mutt_socket_readln_d (char* buf, size_t buflen, CONNECTION* conn, int dbg)
196 {
197 char ch;
198 int i;
199
200 for (i = 0; i < buflen-1; i++)
201 {
202 if (mutt_socket_readchar (conn, &ch) != 1)
203 {
204 buf[i] = '\0';
205 return -1;
206 }
207
208 if (ch == '\n')
209 break;
210 buf[i] = ch;
211 }
212
213 /* strip \r from \r\n termination */
214 if (i && buf[i-1] == '\r')
215 i--;
216 buf[i] = '\0';
217
218 dprint (dbg, (debugfile, "%d< %s\n", conn->fd, buf));
219
220 /* number of bytes read, not strlen */
221 return i + 1;
222 }
223
mutt_socket_head(void)224 CONNECTION* mutt_socket_head (void)
225 {
226 return Connections;
227 }
228
229 /* mutt_socket_free: remove connection from connection list and free it */
mutt_socket_free(CONNECTION * conn)230 void mutt_socket_free (CONNECTION* conn)
231 {
232 CONNECTION* iter;
233 CONNECTION* tmp;
234
235 iter = Connections;
236
237 /* head is special case, doesn't need prev updated */
238 if (iter == conn)
239 {
240 Connections = iter->next;
241 FREE (&iter);
242 return;
243 }
244
245 while (iter->next)
246 {
247 if (iter->next == conn)
248 {
249 tmp = iter->next;
250 iter->next = tmp->next;
251 FREE (&tmp);
252 return;
253 }
254 iter = iter->next;
255 }
256 }
257
258 /* mutt_conn_find: find a connection off the list of connections whose
259 * account matches account. If start is not null, only search for
260 * connections after the given connection (allows higher level socket code
261 * to make more fine-grained searches than account info - eg in IMAP we may
262 * wish to find a connection which is not in IMAP_SELECTED state) */
mutt_conn_find(const CONNECTION * start,const ACCOUNT * account)263 CONNECTION* mutt_conn_find (const CONNECTION* start, const ACCOUNT* account)
264 {
265 CONNECTION* conn;
266 ciss_url_t url;
267 char hook[LONG_STRING];
268
269 /* account isn't actually modified, since url isn't either */
270 mutt_account_tourl ((ACCOUNT*) account, &url);
271 url.path = NULL;
272 url_ciss_tostring (&url, hook, sizeof (hook), 0);
273 mutt_account_hook (hook);
274
275 conn = start ? start->next : Connections;
276 while (conn)
277 {
278 if (mutt_account_match (account, &(conn->account)))
279 return conn;
280 conn = conn->next;
281 }
282
283 conn = socket_new_conn ();
284 memcpy (&conn->account, account, sizeof (ACCOUNT));
285
286 conn->next = Connections;
287 Connections = conn;
288
289 if (Tunnel && *Tunnel)
290 mutt_tunnel_socket_setup (conn);
291 else if (account->flags & M_ACCT_SSL)
292 {
293 #if defined(USE_SSL)
294 if (mutt_ssl_socket_setup (conn) < 0)
295 {
296 mutt_socket_free (conn);
297 return NULL;
298 }
299 #else
300 mutt_error _("SSL is unavailable.");
301 mutt_sleep (2);
302 mutt_socket_free (conn);
303
304 return NULL;
305 #endif
306 }
307 else
308 {
309 conn->conn_read = raw_socket_read;
310 conn->conn_write = raw_socket_write;
311 conn->conn_open = raw_socket_open;
312 conn->conn_close = raw_socket_close;
313 conn->conn_poll = raw_socket_poll;
314 }
315
316 return conn;
317 }
318
socket_preconnect(void)319 static int socket_preconnect (void)
320 {
321 int rc;
322 int save_errno;
323
324 if (mutt_strlen (Preconnect))
325 {
326 dprint (2, (debugfile, "Executing preconnect: %s\n", Preconnect));
327 rc = mutt_system (Preconnect);
328 dprint (2, (debugfile, "Preconnect result: %d\n", rc));
329 if (rc)
330 {
331 save_errno = errno;
332 mutt_perror (_("Preconnect command failed."));
333 mutt_sleep (1);
334
335 return save_errno;
336 }
337 }
338
339 return 0;
340 }
341
342 /* socket_connect: set up to connect to a socket fd. */
socket_connect(int fd,struct sockaddr * sa)343 static int socket_connect (int fd, struct sockaddr* sa)
344 {
345 int sa_size;
346 int save_errno;
347
348 if (sa->sa_family == AF_INET)
349 sa_size = sizeof (struct sockaddr_in);
350 #ifdef HAVE_GETADDRINFO
351 else if (sa->sa_family == AF_INET6)
352 sa_size = sizeof (struct sockaddr_in6);
353 #endif
354 else
355 {
356 dprint (1, (debugfile, "Unknown address family!\n"));
357 return -1;
358 }
359
360 if (ConnectTimeout > 0)
361 alarm (ConnectTimeout);
362
363 mutt_allow_interrupt (1);
364
365 save_errno = 0;
366
367 if (connect (fd, sa, sa_size) < 0)
368 {
369 save_errno = errno;
370 dprint (2, (debugfile, "Connection failed. errno: %d...\n", errno));
371 SigInt = 0; /* reset in case we caught SIGINTR while in connect() */
372 }
373
374 if (ConnectTimeout > 0)
375 alarm (0);
376 mutt_allow_interrupt (0);
377
378 return save_errno;
379 }
380
381 /* socket_new_conn: allocate and initialise a new connection. */
socket_new_conn(void)382 static CONNECTION* socket_new_conn (void)
383 {
384 CONNECTION* conn;
385
386 conn = (CONNECTION *) safe_calloc (1, sizeof (CONNECTION));
387 conn->fd = -1;
388
389 return conn;
390 }
391
raw_socket_close(CONNECTION * conn)392 int raw_socket_close (CONNECTION *conn)
393 {
394 return close (conn->fd);
395 }
396
raw_socket_read(CONNECTION * conn,char * buf,size_t len)397 int raw_socket_read (CONNECTION* conn, char* buf, size_t len)
398 {
399 int rc;
400
401 if ((rc = read (conn->fd, buf, len)) == -1)
402 {
403 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
404 strerror (errno));
405 mutt_sleep (2);
406 }
407
408 return rc;
409 }
410
raw_socket_write(CONNECTION * conn,const char * buf,size_t count)411 int raw_socket_write (CONNECTION* conn, const char* buf, size_t count)
412 {
413 int rc;
414
415 if ((rc = write (conn->fd, buf, count)) == -1)
416 {
417 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
418 strerror (errno));
419 mutt_sleep (2);
420 }
421
422 return rc;
423 }
424
raw_socket_poll(CONNECTION * conn)425 int raw_socket_poll (CONNECTION* conn)
426 {
427 fd_set rfds;
428 struct timeval tv = { 0, 0 };
429
430 if (conn->fd < 0)
431 return -1;
432
433 FD_ZERO (&rfds);
434 FD_SET (conn->fd, &rfds);
435
436 return select (conn->fd + 1, &rfds, NULL, NULL, &tv);
437 }
438
raw_socket_open(CONNECTION * conn)439 int raw_socket_open (CONNECTION* conn)
440 {
441 int rc;
442 int fd;
443
444 char *host_idna = NULL;
445
446 #ifdef HAVE_GETADDRINFO
447 /* --- IPv4/6 --- */
448
449 /* "65536\0" */
450 char port[6];
451 struct addrinfo hints;
452 struct addrinfo* res;
453 struct addrinfo* cur;
454
455 /* we accept v4 or v6 STREAM sockets */
456 memset (&hints, 0, sizeof (hints));
457
458 if (option (OPTUSEIPV6))
459 hints.ai_family = AF_UNSPEC;
460 else
461 hints.ai_family = AF_INET;
462
463 hints.ai_socktype = SOCK_STREAM;
464
465 snprintf (port, sizeof (port), "%d", conn->account.port);
466
467 # ifdef HAVE_LIBIDN
468 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS)
469 {
470 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
471 return -1;
472 }
473 # else
474 host_idna = conn->account.host;
475 # endif
476
477 if (!option(OPTNOCURSES))
478 mutt_message (_("Looking up %s..."), conn->account.host);
479
480 rc = getaddrinfo (host_idna, port, &hints, &res);
481
482 # ifdef HAVE_LIBIDN
483 FREE (&host_idna);
484 # endif
485
486 if (rc)
487 {
488 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
489 mutt_sleep (2);
490 return -1;
491 }
492
493 if (!option(OPTNOCURSES))
494 mutt_message (_("Connecting to %s..."), conn->account.host);
495
496 rc = -1;
497 for (cur = res; cur != NULL; cur = cur->ai_next)
498 {
499 fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
500 if (fd >= 0)
501 {
502 if ((rc = socket_connect (fd, cur->ai_addr)) == 0)
503 {
504 fcntl (fd, F_SETFD, FD_CLOEXEC);
505 conn->fd = fd;
506 break;
507 }
508 else
509 close (fd);
510 }
511 }
512
513 freeaddrinfo (res);
514
515 #else
516 /* --- IPv4 only --- */
517
518 struct sockaddr_in sin;
519 struct hostent* he;
520 int i;
521
522 memset (&sin, 0, sizeof (sin));
523 sin.sin_port = htons (conn->account.port);
524 sin.sin_family = AF_INET;
525
526 # ifdef HAVE_LIBIDN
527 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS)
528 {
529 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
530 return -1;
531 }
532 # else
533 host_idna = conn->account.host;
534 # endif
535
536 if (!option(OPTNOCURSES))
537 mutt_message (_("Looking up %s..."), conn->account.host);
538
539 he = gethostbyname (host_idna);
540
541 # ifdef HAVE_LIBIDN
542 FREE (&host_idna);
543 # endif
544
545 if (! he) {
546 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
547
548 return -1;
549 }
550
551 if (!option(OPTNOCURSES))
552 mutt_message (_("Connecting to %s..."), conn->account.host);
553
554 rc = -1;
555 for (i = 0; he->h_addr_list[i] != NULL; i++)
556 {
557 memcpy (&sin.sin_addr, he->h_addr_list[i], he->h_length);
558 fd = socket (PF_INET, SOCK_STREAM, IPPROTO_IP);
559
560 if (fd >= 0)
561 {
562 if ((rc = socket_connect (fd, (struct sockaddr*) &sin)) == 0)
563 {
564 fcntl (fd, F_SETFD, FD_CLOEXEC);
565 conn->fd = fd;
566 break;
567 }
568 else
569 close (fd);
570 }
571 }
572
573 #endif
574 if (rc)
575 {
576 mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
577 (rc > 0) ? strerror (rc) : _("unknown error"));
578 mutt_sleep (2);
579 return -1;
580 }
581
582 return 0;
583 }
584