fix segfault
[apps/madmutt.git] / mutt_socket.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1998 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 1999-2005 Brendan Cully <brendan@kublai.com>
5  * Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
6  *
7  * This file is part of mutt-ng, see http://www.muttng.org/.
8  * It's licensed under the GNU General Public License,
9  * please see the file GPL in the top level source directory.
10  */
11
12 #if HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #include <lib-lib/mem.h>
17
18 #include "mutt.h"
19 #include "globals.h"
20 #include "mutt_socket.h"
21 #include "mutt_tunnel.h"
22 #if defined(USE_SSL) || defined(USE_GNUTLS)
23 # include "mutt_ssl.h"
24 #endif
25
26 #include "mutt_idna.h"
27
28 #include "lib/mem.h"
29 #include "lib/intl.h"
30 #include "lib/str.h"
31 #include "lib/debug.h"
32
33 #include <unistd.h>
34 #include <netinet/in.h>
35 #include <netdb.h>
36 #include <stdlib.h>
37 #include <fcntl.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <string.h>
41 #include <errno.h>
42
43 /* support for multiple socket connections */
44 static CONNECTION *Connections = NULL;
45
46 /* forward declarations */
47 static int socket_preconnect (void);
48 static int socket_connect (int fd, struct sockaddr *sa);
49 static CONNECTION *socket_new_conn (void);
50
51 /* Wrappers */
52 int mutt_socket_open (CONNECTION * conn)
53 {
54   if (socket_preconnect ())
55     return -1;
56
57   return conn->conn_open (conn);
58 }
59
60 int mutt_socket_close (CONNECTION * conn)
61 {
62   int rc = -1;
63
64   if (conn->fd < 0)
65     debug_print (1, ("Attempt to close closed connection.\n"));
66   else
67     rc = conn->conn_close (conn);
68
69   conn->fd = -1;
70   conn->ssf = 0;
71
72   return rc;
73 }
74
75 int mutt_socket_read (CONNECTION * conn, char *buf, size_t len)
76 {
77   int rc;
78
79   if (conn->fd < 0) {
80     debug_print (1, ("attempt to read from closed connection\n"));
81     return -1;
82   }
83
84   rc = conn->conn_read (conn, buf, len);
85   /* EOF */
86   if (rc == 0) {
87     mutt_error (_("Connection to %s closed"), conn->account.host);
88     mutt_sleep (2);
89   }
90   if (rc <= 0)
91     mutt_socket_close (conn);
92
93   return rc;
94 }
95
96 int mutt_socket_write_d (CONNECTION * conn, const char *buf, int dbg)
97 {
98   int rc;
99   int len;
100
101   debug_print (dbg, ("> %s", buf));
102
103   if (conn->fd < 0) {
104     debug_print (1, ("attempt to write to closed connection\n"));
105     return -1;
106   }
107
108   len = str_len (buf);
109   if ((rc = conn->conn_write (conn, buf, len)) < 0) {
110     debug_print (1, ("error writing, closing socket\n"));
111     mutt_socket_close (conn);
112
113     return -1;
114   }
115
116   if (rc < len) {
117     debug_print (1, ("ERROR: wrote %d of %d bytes!\n", rc, len));
118   }
119
120   return rc;
121 }
122
123 /* simple read buffering to speed things up. */
124 int mutt_socket_readchar (CONNECTION * conn, char *c)
125 {
126   if (conn->bufpos >= conn->available) {
127     if (conn->fd >= 0)
128       conn->available =
129         conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
130     else {
131       debug_print (1, ("attempt to read from closed connection.\n"));
132       return -1;
133     }
134     conn->bufpos = 0;
135     if (conn->available == 0) {
136       mutt_error (_("Connection to %s closed"), conn->account.host);
137       mutt_sleep (2);
138     }
139     if (conn->available <= 0) {
140       mutt_socket_close (conn);
141       return -1;
142     }
143   }
144   *c = conn->inbuf[conn->bufpos];
145   conn->bufpos++;
146   return 1;
147 }
148
149 int mutt_socket_readln_d (char *buf, size_t buflen, CONNECTION * conn,
150                           int dbg)
151 {
152   char ch;
153   int i;
154
155   for (i = 0; i < buflen - 1; i++) {
156     if (mutt_socket_readchar (conn, &ch) != 1) {
157       buf[i] = '\0';
158       return -1;
159     }
160
161     if (ch == '\n')
162       break;
163     buf[i] = ch;
164   }
165
166   /* strip \r from \r\n termination */
167   if (i && buf[i - 1] == '\r')
168     buf[--i] = '\0';
169   else
170     buf[i] = '\0';
171
172   debug_print (dbg, ("< %s\n", buf));
173
174   /* number of bytes read, not str_len */
175   return i + 1;
176 }
177
178 CONNECTION *mutt_socket_head (void)
179 {
180   return Connections;
181 }
182
183 /* mutt_socket_free: remove connection from connection list and free it */
184 void mutt_socket_free (CONNECTION * conn)
185 {
186   CONNECTION *iter;
187   CONNECTION *tmp;
188
189   iter = Connections;
190
191   /* head is special case, doesn't need prev updated */
192   if (iter == conn) {
193     Connections = iter->next;
194     p_delete(&iter);
195     return;
196   }
197
198   while (iter->next) {
199     if (iter->next == conn) {
200       tmp = iter->next;
201       iter->next = tmp->next;
202       p_delete(&tmp);
203       return;
204     }
205     iter = iter->next;
206   }
207 }
208
209 /* mutt_conn_find: find a connection off the list of connections whose
210  *   account matches account. If start is not null, only search for
211  *   connections after the given connection (allows higher level socket code
212  *   to make more fine-grained searches than account info - eg in IMAP we may
213  *   wish to find a connection which is not in IMAP_SELECTED state) */
214 CONNECTION *mutt_conn_find (const CONNECTION * start, const ACCOUNT * account)
215 {
216   CONNECTION *conn;
217   ciss_url_t url;
218   char hook[LONG_STRING];
219
220   /* account isn't actually modified, since url isn't either */
221   mutt_account_tourl ((ACCOUNT *) account, &url);
222   url.path = NULL;
223   url_ciss_tostring (&url, hook, sizeof (hook), 0);
224   mutt_account_hook (hook);
225
226   conn = start ? start->next : Connections;
227   while (conn) {
228     if (mutt_account_match (account, &(conn->account)))
229       return conn;
230     conn = conn->next;
231   }
232
233   conn = socket_new_conn ();
234   memcpy (&conn->account, account, sizeof (ACCOUNT));
235
236   conn->next = Connections;
237   Connections = conn;
238
239   if (Tunnel && *Tunnel)
240     mutt_tunnel_socket_setup (conn);
241   else if (account->flags & M_ACCT_SSL) {
242 #if defined (USE_SSL) || defined (USE_GNUTLS)
243     if (mutt_ssl_socket_setup (conn) < 0) {
244       mutt_socket_free (conn);
245       return NULL;
246     }
247 #else
248     mutt_error _("SSL is unavailable.");
249
250     mutt_sleep (2);
251     mutt_socket_free (conn);
252
253     return NULL;
254 #endif
255   }
256   else {
257     conn->conn_read = raw_socket_read;
258     conn->conn_write = raw_socket_write;
259     conn->conn_open = raw_socket_open;
260     conn->conn_close = raw_socket_close;
261   }
262
263   return conn;
264 }
265
266 static int socket_preconnect (void)
267 {
268   int rc;
269   int save_errno;
270
271   if (str_len (Preconnect)) {
272     debug_print (2, ("Executing preconnect: %s\n", Preconnect));
273     rc = mutt_system (Preconnect);
274     debug_print (2, ("Preconnect result: %d\n", rc));
275     if (rc) {
276       save_errno = errno;
277       mutt_perror (_("Preconnect command failed."));
278       mutt_sleep (1);
279
280       return save_errno;
281     }
282   }
283
284   return 0;
285 }
286
287 /* socket_connect: set up to connect to a socket fd. */
288 static int socket_connect (int fd, struct sockaddr *sa)
289 {
290   int sa_size;
291   int save_errno;
292
293   if (sa->sa_family == AF_INET)
294     sa_size = sizeof (struct sockaddr_in);
295 #ifdef HAVE_GETADDRINFO
296   else if (sa->sa_family == AF_INET6)
297     sa_size = sizeof (struct sockaddr_in6);
298 #endif
299   else {
300     debug_print (1, ("Unknown address family!\n"));
301     return -1;
302   }
303
304   if (ConnectTimeout > 0)
305     alarm (ConnectTimeout);
306
307   mutt_allow_interrupt (1);
308
309   save_errno = 0;
310
311   if (connect (fd, sa, sa_size) < 0) {
312     save_errno = errno;
313     debug_print (2, ("Connection failed. errno: %d...\n", errno));
314     SigInt = 0;                 /* reset in case we caught SIGINTR while in connect() */
315   }
316
317   if (ConnectTimeout > 0)
318     alarm (0);
319   mutt_allow_interrupt (0);
320
321   return save_errno;
322 }
323
324 /* socket_new_conn: allocate and initialise a new connection. */
325 static CONNECTION *socket_new_conn (void)
326 {
327   CONNECTION *conn;
328
329   conn = p_new(CONNECTION, 1);
330   conn->fd = -1;
331
332   return conn;
333 }
334
335 int raw_socket_close (CONNECTION * conn)
336 {
337   return close (conn->fd);
338 }
339
340 int raw_socket_read (CONNECTION * conn, char *buf, size_t len)
341 {
342   int rc;
343
344   if ((rc = read (conn->fd, buf, len)) == -1) {
345     mutt_error (_("Error talking to %s (%s)"), conn->account.host,
346                 strerror (errno));
347     mutt_sleep (2);
348   }
349
350   return rc;
351 }
352
353 int raw_socket_write (CONNECTION * conn, const char *buf, size_t count)
354 {
355   int rc;
356
357   if ((rc = write (conn->fd, buf, count)) == -1) {
358     mutt_error (_("Error talking to %s (%s)"), conn->account.host,
359                 strerror (errno));
360     mutt_sleep (2);
361   }
362
363   return rc;
364 }
365
366 int raw_socket_open (CONNECTION * conn)
367 {
368   int rc;
369   int fd;
370
371   char *host_idna = NULL;
372
373 #ifdef HAVE_GETADDRINFO
374 /* --- IPv4/6 --- */
375
376   /* "65536\0" */
377   char port[6];
378   struct addrinfo hints;
379   struct addrinfo *res;
380   struct addrinfo *cur;
381
382   /* we accept v4 or v6 STREAM sockets */
383   memset (&hints, 0, sizeof (hints));
384
385   if (option (OPTUSEIPV6))
386     hints.ai_family = AF_UNSPEC;
387   else
388     hints.ai_family = AF_INET;
389
390   hints.ai_socktype = SOCK_STREAM;
391
392   snprintf (port, sizeof (port), "%d", conn->account.port);
393
394 # ifdef HAVE_LIBIDN
395   if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
396     mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
397     return -1;
398   }
399 # else
400   host_idna = conn->account.host;
401 # endif
402
403   mutt_message (_("Looking up %s..."), conn->account.host);
404
405
406   rc = getaddrinfo (host_idna, port, &hints, &res);
407
408 # ifdef HAVE_LIBIDN
409   p_delete(&host_idna);
410 # endif
411
412   if (rc) {
413     mutt_error (_("Could not find the host \"%s\""), conn->account.host);
414     mutt_sleep (2);
415     return -1;
416   }
417
418   mutt_message (_("Connecting to %s..."), conn->account.host);
419
420   rc = -1;
421   for (cur = res; cur != NULL; cur = cur->ai_next) {
422     fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
423     if (fd >= 0) {
424       if ((rc = socket_connect (fd, cur->ai_addr)) == 0) {
425         fcntl (fd, F_SETFD, FD_CLOEXEC);
426         conn->fd = fd;
427         break;
428       }
429       else
430         close (fd);
431     }
432   }
433
434   freeaddrinfo (res);
435
436 #else
437   /* --- IPv4 only --- */
438
439   struct sockaddr_in sin;
440   struct hostent *he;
441   int i;
442
443   memset (&sin, 0, sizeof (sin));
444   sin.sin_port = htons (conn->account.port);
445   sin.sin_family = AF_INET;
446
447 # ifdef HAVE_LIBIDN
448   if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
449     mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
450     return -1;
451   }
452 # else
453   host_idna = conn->account.host;
454 # endif
455
456   mutt_message (_("Looking up %s..."), conn->account.host);
457
458   if ((he = gethostbyname (host_idna)) == NULL) {
459 # ifdef HAVE_LIBIDN
460     p_delete(&host_idna);
461 # endif
462     mutt_error (_("Could not find the host \"%s\""), conn->account.host);
463
464     return -1;
465   }
466
467 # ifdef HAVE_LIBIDN
468   p_delete(&host_idna);
469 # endif
470
471   mutt_message (_("Connecting to %s..."), conn->account.host);
472
473   rc = -1;
474   for (i = 0; he->h_addr_list[i] != NULL; i++) {
475     memcpy (&sin.sin_addr, he->h_addr_list[i], he->h_length);
476     fd = socket (PF_INET, SOCK_STREAM, IPPROTO_IP);
477
478     if (fd >= 0) {
479       if ((rc = socket_connect (fd, (struct sockaddr *) &sin)) == 0) {
480         fcntl (fd, F_SETFD, FD_CLOEXEC);
481         conn->fd = fd;
482         break;
483       }
484       else
485         close (fd);
486     }
487   }
488
489 #endif
490   if (rc) {
491     mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
492                 (rc > 0) ? strerror (rc) : _("unknown error"));
493     mutt_sleep (2);
494     return -1;
495   }
496
497   return 0;
498 }