2 * Copyright (C) 1998 Michael R. Elkins <me@mutt.org>
3 * Copyright (C) 1999-2005 Brendan Cully <brendan@kublai.com>
4 * Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
27 #include "mutt_socket.h"
28 #include "mutt_tunnel.h"
29 #if defined(USE_SSL) || defined(USE_GNUTLS) || defined(USE_NSS)
30 # include "mutt_ssl.h"
33 #include "mutt_idna.h"
36 #include <netinet/in.h>
40 #include <sys/types.h>
41 #include <sys/socket.h>
45 /* support for multiple socket connections */
46 static CONNECTION *Connections = NULL;
48 /* forward declarations */
49 static int socket_preconnect (void);
50 static int socket_connect (int fd, struct sockaddr *sa);
51 static CONNECTION *socket_new_conn ();
54 int mutt_socket_open (CONNECTION * conn)
56 if (socket_preconnect ())
59 return conn->conn_open (conn);
62 int mutt_socket_close (CONNECTION * conn)
69 "mutt_socket_close: Attempt to close closed connection.\n"));
71 rc = conn->conn_close (conn);
79 int mutt_socket_read (CONNECTION * conn, char *buf, size_t len)
86 "mutt_socket_read: attempt to read from closed connection\n"));
90 rc = conn->conn_read (conn, buf, len);
93 mutt_error (_("Connection to %s closed"), conn->account.host);
97 mutt_socket_close (conn);
102 int mutt_socket_write_d (CONNECTION * conn, const char *buf, int dbg)
107 dprint (dbg, (debugfile, "> %s", buf));
112 "mutt_socket_write: attempt to write to closed connection\n"));
116 len = mutt_strlen (buf);
117 if ((rc = conn->conn_write (conn, buf, len)) < 0) {
118 dprint (1, (debugfile,
119 "mutt_socket_write: error writing, closing socket\n"));
120 mutt_socket_close (conn);
126 dprint (1, (debugfile,
127 "mutt_socket_write: ERROR: wrote %d of %d bytes!\n", rc,
134 /* simple read buffering to speed things up. */
135 int mutt_socket_readchar (CONNECTION * conn, char *c)
137 if (conn->bufpos >= conn->available) {
140 conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
144 "mutt_socket_readchar: attempt to read from closed connection.\n"));
148 if (conn->available == 0) {
149 mutt_error (_("Connection to %s closed"), conn->account.host);
152 if (conn->available <= 0) {
153 mutt_socket_close (conn);
157 *c = conn->inbuf[conn->bufpos];
162 int mutt_socket_readln_d (char *buf, size_t buflen, CONNECTION * conn,
168 for (i = 0; i < buflen - 1; i++) {
169 if (mutt_socket_readchar (conn, &ch) != 1) {
179 /* strip \r from \r\n termination */
180 if (i && buf[i - 1] == '\r')
185 dprint (dbg, (debugfile, "< %s\n", buf));
187 /* number of bytes read, not strlen */
191 CONNECTION *mutt_socket_head (void)
196 /* mutt_socket_free: remove connection from connection list and free it */
197 void mutt_socket_free (CONNECTION * conn)
204 /* head is special case, doesn't need prev updated */
206 Connections = iter->next;
212 if (iter->next == conn) {
214 iter->next = tmp->next;
222 /* mutt_conn_find: find a connection off the list of connections whose
223 * account matches account. If start is not null, only search for
224 * connections after the given connection (allows higher level socket code
225 * to make more fine-grained searches than account info - eg in IMAP we may
226 * wish to find a connection which is not in IMAP_SELECTED state) */
227 CONNECTION *mutt_conn_find (const CONNECTION * start, const ACCOUNT * account)
231 char hook[LONG_STRING];
233 /* account isn't actually modified, since url isn't either */
234 mutt_account_tourl ((ACCOUNT *) account, &url);
236 url_ciss_tostring (&url, hook, sizeof (hook), 0);
237 mutt_account_hook (hook);
239 conn = start ? start->next : Connections;
241 if (mutt_account_match (account, &(conn->account)))
246 conn = socket_new_conn ();
247 memcpy (&conn->account, account, sizeof (ACCOUNT));
249 conn->next = Connections;
252 if (Tunnel && *Tunnel)
253 mutt_tunnel_socket_setup (conn);
254 else if (account->flags & M_ACCT_SSL) {
256 ssl_socket_setup (conn);
258 mutt_nss_socket_setup (conn);
260 if (mutt_gnutls_socket_setup (conn) < 0) {
261 mutt_socket_free (conn);
265 mutt_error _("SSL is unavailable.");
268 mutt_socket_free (conn);
274 conn->conn_read = raw_socket_read;
275 conn->conn_write = raw_socket_write;
276 conn->conn_open = raw_socket_open;
277 conn->conn_close = raw_socket_close;
283 static int socket_preconnect (void)
288 if (mutt_strlen (Preconnect)) {
289 dprint (2, (debugfile, "Executing preconnect: %s\n", Preconnect));
290 rc = mutt_system (Preconnect);
291 dprint (2, (debugfile, "Preconnect result: %d\n", rc));
294 mutt_perror (_("Preconnect command failed."));
304 /* socket_connect: set up to connect to a socket fd. */
305 static int socket_connect (int fd, struct sockaddr *sa)
310 if (sa->sa_family == AF_INET)
311 sa_size = sizeof (struct sockaddr_in);
312 #ifdef HAVE_GETADDRINFO
313 else if (sa->sa_family == AF_INET6)
314 sa_size = sizeof (struct sockaddr_in6);
317 dprint (1, (debugfile, "Unknown address family!\n"));
321 if (ConnectTimeout > 0)
322 alarm (ConnectTimeout);
324 mutt_allow_interrupt (1);
328 if (connect (fd, sa, sa_size) < 0) {
330 dprint (2, (debugfile, "Connection failed. errno: %d...\n", errno));
331 SigInt = 0; /* reset in case we caught SIGINTR while in connect() */
334 if (ConnectTimeout > 0)
336 mutt_allow_interrupt (0);
341 /* socket_new_conn: allocate and initialise a new connection. */
342 static CONNECTION *socket_new_conn ()
346 conn = (CONNECTION *) safe_calloc (1, sizeof (CONNECTION));
352 int raw_socket_close (CONNECTION * conn)
354 return close (conn->fd);
357 int raw_socket_read (CONNECTION * conn, char *buf, size_t len)
361 if ((rc = read (conn->fd, buf, len)) == -1) {
362 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
370 int raw_socket_write (CONNECTION * conn, const char *buf, size_t count)
374 if ((rc = write (conn->fd, buf, count)) == -1) {
375 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
383 int raw_socket_open (CONNECTION * conn)
388 char *host_idna = NULL;
390 #ifdef HAVE_GETADDRINFO
395 struct addrinfo hints;
396 struct addrinfo *res;
397 struct addrinfo *cur;
399 /* we accept v4 or v6 STREAM sockets */
400 memset (&hints, 0, sizeof (hints));
402 if (option (OPTUSEIPV6))
403 hints.ai_family = AF_UNSPEC;
405 hints.ai_family = AF_INET;
407 hints.ai_socktype = SOCK_STREAM;
409 snprintf (port, sizeof (port), "%d", conn->account.port);
412 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
413 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
417 host_idna = conn->account.host;
420 mutt_message (_("Looking up %s..."), conn->account.host);
423 rc = getaddrinfo (host_idna, port, &hints, &res);
430 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
434 mutt_message (_("Connecting to %s..."), conn->account.host);
437 for (cur = res; cur != NULL; cur = cur->ai_next) {
438 fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
440 if ((rc = socket_connect (fd, cur->ai_addr)) == 0) {
441 fcntl (fd, F_SETFD, FD_CLOEXEC);
453 /* --- IPv4 only --- */
455 struct sockaddr_in sin;
459 memset (&sin, 0, sizeof (sin));
460 sin.sin_port = htons (conn->account.port);
461 sin.sin_family = AF_INET;
464 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
465 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
469 host_idna = conn->account.host;
472 mutt_message (_("Looking up %s..."), conn->account.host);
474 if ((he = gethostbyname (host_idna)) == NULL) {
478 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
487 mutt_message (_("Connecting to %s..."), conn->account.host);
490 for (i = 0; he->h_addr_list[i] != NULL; i++) {
491 memcpy (&sin.sin_addr, he->h_addr_list[i], he->h_length);
492 fd = socket (PF_INET, SOCK_STREAM, IPPROTO_IP);
495 if ((rc = socket_connect (fd, (struct sockaddr *) &sin)) == 0) {
506 mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
507 (rc > 0) ? strerror (rc) : _("unknown error"));