mutt stable branch with some hacks
at master 583 lines 13 kB view raw
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 */ 51static CONNECTION *Connections = NULL; 52 53/* forward declarations */ 54static int socket_preconnect (void); 55static int socket_connect (int fd, struct sockaddr* sa); 56static CONNECTION* socket_new_conn (void); 57 58/* Wrappers */ 59int 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 74int 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 89int 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 112int 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 */ 155int 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. */ 167int 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 195int 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 224CONNECTION* mutt_socket_head (void) 225{ 226 return Connections; 227} 228 229/* mutt_socket_free: remove connection from connection list and free it */ 230void 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) */ 263CONNECTION* 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 & MUTT_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 319static 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. */ 343static 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. */ 382static 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 392int raw_socket_close (CONNECTION *conn) 393{ 394 return close (conn->fd); 395} 396 397int 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 411int 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 425int 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 439int 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}