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.
16 #include <lib-lib/mem.h>
20 #include "mutt_socket.h"
21 #include "mutt_tunnel.h"
22 #if defined(USE_SSL) || defined(USE_GNUTLS)
23 # include "mutt_ssl.h"
26 #include "mutt_idna.h"
31 #include "lib/debug.h"
34 #include <netinet/in.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
43 /* support for multiple socket connections */
44 static CONNECTION *Connections = NULL;
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);
52 int mutt_socket_open (CONNECTION * conn)
54 if (socket_preconnect ())
57 return conn->conn_open (conn);
60 int mutt_socket_close (CONNECTION * conn)
65 debug_print (1, ("Attempt to close closed connection.\n"));
67 rc = conn->conn_close (conn);
75 int mutt_socket_read (CONNECTION * conn, char *buf, size_t len)
80 debug_print (1, ("attempt to read from closed connection\n"));
84 rc = conn->conn_read (conn, buf, len);
87 mutt_error (_("Connection to %s closed"), conn->account.host);
91 mutt_socket_close (conn);
96 int mutt_socket_write_d (CONNECTION * conn, const char *buf, int dbg)
101 debug_print (dbg, ("> %s", buf));
104 debug_print (1, ("attempt to write to closed connection\n"));
109 if ((rc = conn->conn_write (conn, buf, len)) < 0) {
110 debug_print (1, ("error writing, closing socket\n"));
111 mutt_socket_close (conn);
117 debug_print (1, ("ERROR: wrote %d of %d bytes!\n", rc, len));
123 /* simple read buffering to speed things up. */
124 int mutt_socket_readchar (CONNECTION * conn, char *c)
126 if (conn->bufpos >= conn->available) {
129 conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
131 debug_print (1, ("attempt to read from closed connection.\n"));
135 if (conn->available == 0) {
136 mutt_error (_("Connection to %s closed"), conn->account.host);
139 if (conn->available <= 0) {
140 mutt_socket_close (conn);
144 *c = conn->inbuf[conn->bufpos];
149 int mutt_socket_readln_d (char *buf, size_t buflen, CONNECTION * conn,
155 for (i = 0; i < buflen - 1; i++) {
156 if (mutt_socket_readchar (conn, &ch) != 1) {
166 /* strip \r from \r\n termination */
167 if (i && buf[i - 1] == '\r')
172 debug_print (dbg, ("< %s\n", buf));
174 /* number of bytes read, not str_len */
178 CONNECTION *mutt_socket_head (void)
183 /* mutt_socket_free: remove connection from connection list and free it */
184 void mutt_socket_free (CONNECTION * conn)
191 /* head is special case, doesn't need prev updated */
193 Connections = iter->next;
199 if (iter->next == conn) {
201 iter->next = tmp->next;
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)
218 char hook[LONG_STRING];
220 /* account isn't actually modified, since url isn't either */
221 mutt_account_tourl ((ACCOUNT *) account, &url);
223 url_ciss_tostring (&url, hook, sizeof (hook), 0);
224 mutt_account_hook (hook);
226 conn = start ? start->next : Connections;
228 if (mutt_account_match (account, &(conn->account)))
233 conn = socket_new_conn ();
234 memcpy (&conn->account, account, sizeof (ACCOUNT));
236 conn->next = Connections;
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);
248 mutt_error _("SSL is unavailable.");
251 mutt_socket_free (conn);
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;
266 static int socket_preconnect (void)
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));
277 mutt_perror (_("Preconnect command failed."));
287 /* socket_connect: set up to connect to a socket fd. */
288 static int socket_connect (int fd, struct sockaddr *sa)
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);
300 debug_print (1, ("Unknown address family!\n"));
304 if (ConnectTimeout > 0)
305 alarm (ConnectTimeout);
307 mutt_allow_interrupt (1);
311 if (connect (fd, sa, sa_size) < 0) {
313 debug_print (2, ("Connection failed. errno: %d...\n", errno));
314 SigInt = 0; /* reset in case we caught SIGINTR while in connect() */
317 if (ConnectTimeout > 0)
319 mutt_allow_interrupt (0);
324 /* socket_new_conn: allocate and initialise a new connection. */
325 static CONNECTION *socket_new_conn (void)
329 conn = (CONNECTION *) mem_calloc (1, sizeof (CONNECTION));
335 int raw_socket_close (CONNECTION * conn)
337 return close (conn->fd);
340 int raw_socket_read (CONNECTION * conn, char *buf, size_t len)
344 if ((rc = read (conn->fd, buf, len)) == -1) {
345 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
353 int raw_socket_write (CONNECTION * conn, const char *buf, size_t count)
357 if ((rc = write (conn->fd, buf, count)) == -1) {
358 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
366 int raw_socket_open (CONNECTION * conn)
371 char *host_idna = NULL;
373 #ifdef HAVE_GETADDRINFO
378 struct addrinfo hints;
379 struct addrinfo *res;
380 struct addrinfo *cur;
382 /* we accept v4 or v6 STREAM sockets */
383 memset (&hints, 0, sizeof (hints));
385 if (option (OPTUSEIPV6))
386 hints.ai_family = AF_UNSPEC;
388 hints.ai_family = AF_INET;
390 hints.ai_socktype = SOCK_STREAM;
392 snprintf (port, sizeof (port), "%d", conn->account.port);
395 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
396 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
400 host_idna = conn->account.host;
403 mutt_message (_("Looking up %s..."), conn->account.host);
406 rc = getaddrinfo (host_idna, port, &hints, &res);
409 p_delete(&host_idna);
413 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
418 mutt_message (_("Connecting to %s..."), conn->account.host);
421 for (cur = res; cur != NULL; cur = cur->ai_next) {
422 fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
424 if ((rc = socket_connect (fd, cur->ai_addr)) == 0) {
425 fcntl (fd, F_SETFD, FD_CLOEXEC);
437 /* --- IPv4 only --- */
439 struct sockaddr_in sin;
443 memset (&sin, 0, sizeof (sin));
444 sin.sin_port = htons (conn->account.port);
445 sin.sin_family = AF_INET;
448 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
449 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
453 host_idna = conn->account.host;
456 mutt_message (_("Looking up %s..."), conn->account.host);
458 if ((he = gethostbyname (host_idna)) == NULL) {
460 p_delete(&host_idna);
462 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
468 p_delete(&host_idna);
471 mutt_message (_("Connecting to %s..."), conn->account.host);
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);
479 if ((rc = socket_connect (fd, (struct sockaddr *) &sin)) == 0) {
480 fcntl (fd, F_SETFD, FD_CLOEXEC);
491 mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
492 (rc > 0) ? strerror (rc) : _("unknown error"));