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>
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.
18 #include "mutt_socket.h"
19 #include "mutt_tunnel.h"
20 #if defined(USE_SSL) || defined(USE_GNUTLS) || defined(USE_NSS)
21 # include "mutt_ssl.h"
24 #include "mutt_idna.h"
31 #include <netinet/in.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
40 /* support for multiple socket connections */
41 static CONNECTION *Connections = NULL;
43 /* forward declarations */
44 static int socket_preconnect (void);
45 static int socket_connect (int fd, struct sockaddr *sa);
46 static CONNECTION *socket_new_conn ();
49 int mutt_socket_open (CONNECTION * conn)
51 if (socket_preconnect ())
54 return conn->conn_open (conn);
57 int mutt_socket_close (CONNECTION * conn)
64 "mutt_socket_close: Attempt to close closed connection.\n"));
66 rc = conn->conn_close (conn);
74 int mutt_socket_read (CONNECTION * conn, char *buf, size_t len)
81 "mutt_socket_read: attempt to read from closed connection\n"));
85 rc = conn->conn_read (conn, buf, len);
88 mutt_error (_("Connection to %s closed"), conn->account.host);
92 mutt_socket_close (conn);
97 int mutt_socket_write_d (CONNECTION * conn, const char *buf, int dbg)
102 dprint (dbg, (debugfile, "> %s", buf));
107 "mutt_socket_write: attempt to write to closed connection\n"));
111 len = safe_strlen (buf);
112 if ((rc = conn->conn_write (conn, buf, len)) < 0) {
113 dprint (1, (debugfile,
114 "mutt_socket_write: error writing, closing socket\n"));
115 mutt_socket_close (conn);
121 dprint (1, (debugfile,
122 "mutt_socket_write: ERROR: wrote %d of %d bytes!\n", rc,
129 /* simple read buffering to speed things up. */
130 int mutt_socket_readchar (CONNECTION * conn, char *c)
132 if (conn->bufpos >= conn->available) {
135 conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
139 "mutt_socket_readchar: attempt to read from closed connection.\n"));
143 if (conn->available == 0) {
144 mutt_error (_("Connection to %s closed"), conn->account.host);
147 if (conn->available <= 0) {
148 mutt_socket_close (conn);
152 *c = conn->inbuf[conn->bufpos];
157 int mutt_socket_readln_d (char *buf, size_t buflen, CONNECTION * conn,
163 for (i = 0; i < buflen - 1; i++) {
164 if (mutt_socket_readchar (conn, &ch) != 1) {
174 /* strip \r from \r\n termination */
175 if (i && buf[i - 1] == '\r')
180 dprint (dbg, (debugfile, "< %s\n", buf));
182 /* number of bytes read, not safe_strlen */
186 CONNECTION *mutt_socket_head (void)
191 /* mutt_socket_free: remove connection from connection list and free it */
192 void mutt_socket_free (CONNECTION * conn)
199 /* head is special case, doesn't need prev updated */
201 Connections = iter->next;
207 if (iter->next == conn) {
209 iter->next = tmp->next;
217 /* mutt_conn_find: find a connection off the list of connections whose
218 * account matches account. If start is not null, only search for
219 * connections after the given connection (allows higher level socket code
220 * to make more fine-grained searches than account info - eg in IMAP we may
221 * wish to find a connection which is not in IMAP_SELECTED state) */
222 CONNECTION *mutt_conn_find (const CONNECTION * start, const ACCOUNT * account)
226 char hook[LONG_STRING];
228 /* account isn't actually modified, since url isn't either */
229 mutt_account_tourl ((ACCOUNT *) account, &url);
231 url_ciss_tostring (&url, hook, sizeof (hook), 0);
232 mutt_account_hook (hook);
234 conn = start ? start->next : Connections;
236 if (mutt_account_match (account, &(conn->account)))
241 conn = socket_new_conn ();
242 memcpy (&conn->account, account, sizeof (ACCOUNT));
244 conn->next = Connections;
247 if (Tunnel && *Tunnel)
248 mutt_tunnel_socket_setup (conn);
249 else if (account->flags & M_ACCT_SSL) {
251 ssl_socket_setup (conn);
253 mutt_nss_socket_setup (conn);
255 if (mutt_gnutls_socket_setup (conn) < 0) {
256 mutt_socket_free (conn);
260 mutt_error _("SSL is unavailable.");
263 mutt_socket_free (conn);
269 conn->conn_read = raw_socket_read;
270 conn->conn_write = raw_socket_write;
271 conn->conn_open = raw_socket_open;
272 conn->conn_close = raw_socket_close;
278 static int socket_preconnect (void)
283 if (safe_strlen (Preconnect)) {
284 dprint (2, (debugfile, "Executing preconnect: %s\n", Preconnect));
285 rc = mutt_system (Preconnect);
286 dprint (2, (debugfile, "Preconnect result: %d\n", rc));
289 mutt_perror (_("Preconnect command failed."));
299 /* socket_connect: set up to connect to a socket fd. */
300 static int socket_connect (int fd, struct sockaddr *sa)
305 if (sa->sa_family == AF_INET)
306 sa_size = sizeof (struct sockaddr_in);
307 #ifdef HAVE_GETADDRINFO
308 else if (sa->sa_family == AF_INET6)
309 sa_size = sizeof (struct sockaddr_in6);
312 dprint (1, (debugfile, "Unknown address family!\n"));
316 if (ConnectTimeout > 0)
317 alarm (ConnectTimeout);
319 mutt_allow_interrupt (1);
323 if (connect (fd, sa, sa_size) < 0) {
325 dprint (2, (debugfile, "Connection failed. errno: %d...\n", errno));
326 SigInt = 0; /* reset in case we caught SIGINTR while in connect() */
329 if (ConnectTimeout > 0)
331 mutt_allow_interrupt (0);
336 /* socket_new_conn: allocate and initialise a new connection. */
337 static CONNECTION *socket_new_conn ()
341 conn = (CONNECTION *) safe_calloc (1, sizeof (CONNECTION));
347 int raw_socket_close (CONNECTION * conn)
349 return close (conn->fd);
352 int raw_socket_read (CONNECTION * conn, char *buf, size_t len)
356 if ((rc = read (conn->fd, buf, len)) == -1) {
357 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
365 int raw_socket_write (CONNECTION * conn, const char *buf, size_t count)
369 if ((rc = write (conn->fd, buf, count)) == -1) {
370 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
378 int raw_socket_open (CONNECTION * conn)
383 char *host_idna = NULL;
385 #ifdef HAVE_GETADDRINFO
390 struct addrinfo hints;
391 struct addrinfo *res;
392 struct addrinfo *cur;
394 /* we accept v4 or v6 STREAM sockets */
395 memset (&hints, 0, sizeof (hints));
397 if (option (OPTUSEIPV6))
398 hints.ai_family = AF_UNSPEC;
400 hints.ai_family = AF_INET;
402 hints.ai_socktype = SOCK_STREAM;
404 snprintf (port, sizeof (port), "%d", conn->account.port);
407 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
408 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
412 host_idna = conn->account.host;
415 mutt_message (_("Looking up %s..."), conn->account.host);
418 rc = getaddrinfo (host_idna, port, &hints, &res);
425 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
429 mutt_message (_("Connecting to %s..."), conn->account.host);
432 for (cur = res; cur != NULL; cur = cur->ai_next) {
433 fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
435 if ((rc = socket_connect (fd, cur->ai_addr)) == 0) {
436 fcntl (fd, F_SETFD, FD_CLOEXEC);
448 /* --- IPv4 only --- */
450 struct sockaddr_in sin;
454 memset (&sin, 0, sizeof (sin));
455 sin.sin_port = htons (conn->account.port);
456 sin.sin_family = AF_INET;
459 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
460 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
464 host_idna = conn->account.host;
467 mutt_message (_("Looking up %s..."), conn->account.host);
469 if ((he = gethostbyname (host_idna)) == NULL) {
473 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
482 mutt_message (_("Connecting to %s..."), conn->account.host);
485 for (i = 0; he->h_addr_list[i] != NULL; i++) {
486 memcpy (&sin.sin_addr, he->h_addr_list[i], he->h_length);
487 fd = socket (PF_INET, SOCK_STREAM, IPPROTO_IP);
490 if ((rc = socket_connect (fd, (struct sockaddr *) &sin)) == 0) {
501 mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
502 (rc > 0) ? strerror (rc) : _("unknown error"));