mutt stable branch with some hacks
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 <signal.h>
39#include <fcntl.h>
40#include <sys/types.h>
41#ifdef HAVE_SYS_TIME_H
42#include <sys/time.h>
43#endif
44#include <time.h>
45#include <sys/socket.h>
46#ifdef HAVE_SYS_SELECT_H
47#include <sys/select.h>
48#endif
49#include <string.h>
50#include <errno.h>
51
52/* support for multiple socket connections */
53static CONNECTION *Connections = NULL;
54
55/* forward declarations */
56static int socket_preconnect (void);
57static int socket_connect (int fd, struct sockaddr* sa);
58static CONNECTION* socket_new_conn (void);
59
60/* Wrappers */
61int mutt_socket_open (CONNECTION* conn)
62{
63 int rc;
64
65 if (socket_preconnect ())
66 return -1;
67
68 mutt_allow_interrupt (1);
69 rc = conn->conn_open (conn);
70 mutt_allow_interrupt (0);
71
72 dprint (2, (debugfile, "Connected to %s:%d on fd=%d\n",
73 NONULL (conn->account.host), conn->account.port, conn->fd));
74
75 return rc;
76}
77
78int mutt_socket_close (CONNECTION* conn)
79{
80 int rc = -1;
81
82 if (conn->fd < 0)
83 dprint (1, (debugfile, "mutt_socket_close: Attempt to close closed connection.\n"));
84 else {
85 mutt_allow_interrupt (1);
86 rc = conn->conn_close (conn);
87 mutt_allow_interrupt (0);
88 }
89
90 conn->fd = -1;
91 conn->ssf = 0;
92 conn->bufpos = 0;
93 conn->available = 0;
94
95 return rc;
96}
97
98int mutt_socket_read (CONNECTION* conn, char* buf, size_t len)
99{
100 int rc;
101
102 if (conn->fd < 0)
103 {
104 dprint (1, (debugfile, "mutt_socket_read: attempt to read from closed connection\n"));
105 return -1;
106 }
107
108 mutt_allow_interrupt (1);
109 rc = conn->conn_read (conn, buf, len);
110 mutt_allow_interrupt (0);
111
112 /* EOF */
113 if (rc == 0)
114 {
115 mutt_error (_("Connection to %s closed"), conn->account.host);
116 mutt_sleep (2);
117 }
118 if (rc <= 0)
119 mutt_socket_close (conn);
120
121 return rc;
122}
123
124int mutt_socket_write_d (CONNECTION *conn, const char *buf, int len, int dbg)
125{
126 int rc;
127 int sent = 0;
128
129 dprint (dbg, (debugfile,"%d> %s", conn->fd, buf));
130
131 if (conn->fd < 0)
132 {
133 dprint (1, (debugfile, "mutt_socket_write: attempt to write to closed connection\n"));
134 return -1;
135 }
136
137 if (len < 0)
138 len = mutt_strlen (buf);
139
140 while (sent < len)
141 {
142 mutt_allow_interrupt (1);
143 rc = conn->conn_write (conn, buf + sent, len - sent);
144 mutt_allow_interrupt (0);
145
146 if (rc < 0)
147 {
148 dprint (1, (debugfile,
149 "mutt_socket_write: error writing (%s), closing socket\n",
150 strerror(errno)));
151 mutt_socket_close (conn);
152
153 return -1;
154 }
155
156 if (rc < len - sent)
157 dprint (3, (debugfile,
158 "mutt_socket_write: short write (%d of %d bytes)\n", rc,
159 len - sent));
160
161 sent += rc;
162 }
163
164 return sent;
165}
166
167/* poll whether reads would block.
168 * Returns: >0 if there is data to read,
169 * 0 if a read would block,
170 * -1 if this connection doesn't support polling */
171int mutt_socket_poll (CONNECTION* conn, time_t wait_secs)
172{
173 if (conn->bufpos < conn->available)
174 return conn->available - conn->bufpos;
175
176 if (conn->conn_poll)
177 return conn->conn_poll (conn, wait_secs);
178
179 return -1;
180}
181
182/* simple read buffering to speed things up. */
183int mutt_socket_readchar (CONNECTION *conn, char *c)
184{
185 if (conn->bufpos >= conn->available)
186 {
187 if (conn->fd >= 0)
188 {
189 mutt_allow_interrupt (1);
190 conn->available = conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
191 mutt_allow_interrupt (0);
192 }
193 else
194 {
195 dprint (1, (debugfile, "mutt_socket_readchar: attempt to read from closed connection.\n"));
196 return -1;
197 }
198 conn->bufpos = 0;
199 if (conn->available == 0)
200 {
201 mutt_error (_("Connection to %s closed"), conn->account.host);
202 mutt_sleep (2);
203 }
204 if (conn->available <= 0)
205 {
206 mutt_socket_close (conn);
207 return -1;
208 }
209 }
210 *c = conn->inbuf[conn->bufpos];
211 conn->bufpos++;
212 return 1;
213}
214
215int mutt_socket_readln_d (char* buf, size_t buflen, CONNECTION* conn, int dbg)
216{
217 char ch;
218 int i;
219
220 for (i = 0; i < buflen-1; i++)
221 {
222 if (mutt_socket_readchar (conn, &ch) != 1)
223 {
224 buf[i] = '\0';
225 return -1;
226 }
227
228 if (ch == '\n')
229 break;
230 buf[i] = ch;
231 }
232
233 /* strip \r from \r\n termination */
234 if (i && buf[i-1] == '\r')
235 i--;
236 buf[i] = '\0';
237
238 dprint (dbg, (debugfile, "%d< %s\n", conn->fd, buf));
239
240 /* number of bytes read, not strlen */
241 return i + 1;
242}
243
244CONNECTION* mutt_socket_head (void)
245{
246 return Connections;
247}
248
249/* mutt_socket_free: remove connection from connection list and free it */
250void mutt_socket_free (CONNECTION* conn)
251{
252 CONNECTION* iter;
253 CONNECTION* tmp;
254
255 iter = Connections;
256
257 /* head is special case, doesn't need prev updated */
258 if (iter == conn)
259 {
260 Connections = iter->next;
261 FREE (&iter);
262 return;
263 }
264
265 while (iter->next)
266 {
267 if (iter->next == conn)
268 {
269 tmp = iter->next;
270 iter->next = tmp->next;
271 FREE (&tmp);
272 return;
273 }
274 iter = iter->next;
275 }
276}
277
278/* mutt_conn_find: find a connection off the list of connections whose
279 * account matches account. If start is not null, only search for
280 * connections after the given connection (allows higher level socket code
281 * to make more fine-grained searches than account info - eg in IMAP we may
282 * wish to find a connection which is not in IMAP_SELECTED state) */
283CONNECTION* mutt_conn_find (const CONNECTION* start, const ACCOUNT* account)
284{
285 CONNECTION* conn;
286 ciss_url_t url;
287 char hook[LONG_STRING];
288
289 /* account isn't actually modified, since url isn't either */
290 mutt_account_tourl ((ACCOUNT*) account, &url);
291 url.path = NULL;
292 url_ciss_tostring (&url, hook, sizeof (hook), 0);
293 mutt_account_hook (hook);
294
295 conn = start ? start->next : Connections;
296 while (conn)
297 {
298 if (mutt_account_match (account, &(conn->account)))
299 return conn;
300 conn = conn->next;
301 }
302
303 conn = socket_new_conn ();
304 memcpy (&conn->account, account, sizeof (ACCOUNT));
305
306 conn->next = Connections;
307 Connections = conn;
308
309 if (Tunnel)
310 mutt_tunnel_socket_setup (conn);
311 else if (account->flags & MUTT_ACCT_SSL)
312 {
313#if defined(USE_SSL)
314 if (mutt_ssl_socket_setup (conn) < 0)
315 {
316 mutt_socket_free (conn);
317 return NULL;
318 }
319#else
320 mutt_error _("SSL is unavailable.");
321 mutt_sleep (2);
322 mutt_socket_free (conn);
323
324 return NULL;
325#endif
326 }
327 else
328 {
329 conn->conn_read = raw_socket_read;
330 conn->conn_write = raw_socket_write;
331 conn->conn_open = raw_socket_open;
332 conn->conn_close = raw_socket_close;
333 conn->conn_poll = raw_socket_poll;
334 }
335
336 return conn;
337}
338
339static int socket_preconnect (void)
340{
341 int rc;
342 int save_errno;
343
344 if (mutt_strlen (Preconnect))
345 {
346 dprint (2, (debugfile, "Executing preconnect: %s\n", Preconnect));
347 rc = mutt_system (Preconnect);
348 dprint (2, (debugfile, "Preconnect result: %d\n", rc));
349 if (rc)
350 {
351 save_errno = errno;
352 mutt_perror (_("Preconnect command failed."));
353 mutt_sleep (1);
354
355 return save_errno;
356 }
357 }
358
359 return 0;
360}
361
362/* socket_connect: set up to connect to a socket fd. */
363static int socket_connect (int fd, struct sockaddr* sa)
364{
365 int sa_size;
366 int save_errno;
367 sigset_t set;
368
369 if (sa->sa_family == AF_INET)
370 sa_size = sizeof (struct sockaddr_in);
371#ifdef HAVE_GETADDRINFO
372 else if (sa->sa_family == AF_INET6)
373 sa_size = sizeof (struct sockaddr_in6);
374#endif
375 else
376 {
377 dprint (1, (debugfile, "Unknown address family!\n"));
378 return -1;
379 }
380
381 if (ConnectTimeout > 0)
382 alarm (ConnectTimeout);
383
384 mutt_allow_interrupt (1);
385
386 /* FreeBSD's connect() does not respect SA_RESTART, meaning
387 * a SIGWINCH will cause the connect to fail. */
388 sigemptyset (&set);
389 sigaddset (&set, SIGWINCH);
390 sigprocmask (SIG_BLOCK, &set, NULL);
391
392 save_errno = 0;
393
394 if (connect (fd, sa, sa_size) < 0)
395 {
396 save_errno = errno;
397 dprint (2, (debugfile, "Connection failed. errno: %d...\n", errno));
398 SigInt = 0; /* reset in case we caught SIGINTR while in connect() */
399 }
400
401 if (ConnectTimeout > 0)
402 alarm (0);
403 mutt_allow_interrupt (0);
404 sigprocmask (SIG_UNBLOCK, &set, NULL);
405
406 return save_errno;
407}
408
409/* socket_new_conn: allocate and initialise a new connection. */
410static CONNECTION* socket_new_conn (void)
411{
412 CONNECTION* conn;
413
414 conn = (CONNECTION *) safe_calloc (1, sizeof (CONNECTION));
415 conn->fd = -1;
416
417 return conn;
418}
419
420int raw_socket_close (CONNECTION *conn)
421{
422 return close (conn->fd);
423}
424
425int raw_socket_read (CONNECTION* conn, char* buf, size_t len)
426{
427 int rc;
428
429 do
430 {
431 rc = read (conn->fd, buf, len);
432 } while (rc < 0 && errno == EINTR);
433
434 if (rc < 0)
435 {
436 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
437 strerror (errno));
438 mutt_sleep (2);
439 return -1;
440 }
441
442 return rc;
443}
444
445int raw_socket_write (CONNECTION* conn, const char* buf, size_t count)
446{
447 int rc;
448 size_t sent = 0;
449
450 do
451 {
452 do
453 {
454 rc = write (conn->fd, buf + sent, count - sent);
455 } while (rc < 0 && errno == EINTR);
456
457 if (rc < 0)
458 {
459 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
460 strerror (errno));
461 mutt_sleep (2);
462 return -1;
463 }
464
465 sent += rc;
466 } while (sent < count);
467
468 return sent;
469}
470
471int raw_socket_poll (CONNECTION* conn, time_t wait_secs)
472{
473 fd_set rfds;
474 unsigned long wait_millis, post_t_millis;
475 struct timeval tv, pre_t, post_t;
476 int rv;
477
478 if (conn->fd < 0)
479 return -1;
480
481 wait_millis = wait_secs * 1000UL;
482
483 FOREVER
484 {
485 tv.tv_sec = wait_millis / 1000;
486 tv.tv_usec = (wait_millis % 1000) * 1000;
487
488 FD_ZERO (&rfds);
489 FD_SET (conn->fd, &rfds);
490
491 gettimeofday (&pre_t, NULL);
492 rv = select (conn->fd + 1, &rfds, NULL, NULL, &tv);
493 gettimeofday (&post_t, NULL);
494
495 if (rv > 0 ||
496 (rv < 0 && errno != EINTR))
497 return rv;
498
499 if (SigInt)
500 mutt_query_exit ();
501
502 wait_millis += (pre_t.tv_sec * 1000UL) + (pre_t.tv_usec / 1000);
503 post_t_millis = (post_t.tv_sec * 1000UL) + (post_t.tv_usec / 1000);
504 if (wait_millis <= post_t_millis)
505 return 0;
506 wait_millis -= post_t_millis;
507 }
508}
509
510int raw_socket_open (CONNECTION* conn)
511{
512 int rc;
513 int fd;
514
515 char *host_idna = NULL;
516
517#ifdef HAVE_GETADDRINFO
518/* --- IPv4/6 --- */
519
520 /* "65536\0" */
521 char port[6];
522 struct addrinfo hints;
523 struct addrinfo* res;
524 struct addrinfo* cur;
525
526 /* we accept v4 or v6 STREAM sockets */
527 memset (&hints, 0, sizeof (hints));
528
529 if (option (OPTUSEIPV6))
530 hints.ai_family = AF_UNSPEC;
531 else
532 hints.ai_family = AF_INET;
533
534 hints.ai_socktype = SOCK_STREAM;
535
536 snprintf (port, sizeof (port), "%d", conn->account.port);
537
538# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2)
539 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS)
540 {
541 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
542 return -1;
543 }
544# else
545 host_idna = conn->account.host;
546# endif
547
548 if (!option(OPTNOCURSES))
549 mutt_message (_("Looking up %s..."), conn->account.host);
550
551 rc = getaddrinfo (host_idna, port, &hints, &res);
552
553# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2)
554 FREE (&host_idna);
555# endif
556
557 if (rc)
558 {
559 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
560 mutt_sleep (2);
561 return -1;
562 }
563
564 if (!option(OPTNOCURSES))
565 mutt_message (_("Connecting to %s..."), conn->account.host);
566
567 rc = -1;
568 for (cur = res; cur != NULL; cur = cur->ai_next)
569 {
570 fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
571 if (fd >= 0)
572 {
573 if ((rc = socket_connect (fd, cur->ai_addr)) == 0)
574 {
575 fcntl (fd, F_SETFD, FD_CLOEXEC);
576 conn->fd = fd;
577 break;
578 }
579 else
580 close (fd);
581 }
582 }
583
584 freeaddrinfo (res);
585
586#else
587 /* --- IPv4 only --- */
588
589 struct sockaddr_in sin;
590 struct hostent* he;
591 int i;
592
593 memset (&sin, 0, sizeof (sin));
594 sin.sin_port = htons (conn->account.port);
595 sin.sin_family = AF_INET;
596
597# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2)
598 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS)
599 {
600 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
601 return -1;
602 }
603# else
604 host_idna = conn->account.host;
605# endif
606
607 if (!option(OPTNOCURSES))
608 mutt_message (_("Looking up %s..."), conn->account.host);
609
610 he = gethostbyname (host_idna);
611
612# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2)
613 FREE (&host_idna);
614# endif
615
616 if (! he)
617 {
618 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
619
620 return -1;
621 }
622
623 if (!option(OPTNOCURSES))
624 mutt_message (_("Connecting to %s..."), conn->account.host);
625
626 rc = -1;
627 for (i = 0; he->h_addr_list[i] != NULL; i++)
628 {
629 memcpy (&sin.sin_addr, he->h_addr_list[i], he->h_length);
630 fd = socket (PF_INET, SOCK_STREAM, IPPROTO_IP);
631
632 if (fd >= 0)
633 {
634 if ((rc = socket_connect (fd, (struct sockaddr*) &sin)) == 0)
635 {
636 fcntl (fd, F_SETFD, FD_CLOEXEC);
637 conn->fd = fd;
638 break;
639 }
640 else
641 close (fd);
642 }
643 }
644
645#endif
646 if (rc)
647 {
648 mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
649 (rc > 0) ? strerror (rc) : _("unknown error"));
650 mutt_sleep (2);
651 return -1;
652 }
653
654 return 0;
655}