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