mutt stable branch with some hacks
at jcs 655 lines 14 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 <signal.h> 39#include <fcntl.h> 40#include <sys/types.h> 41#ifdef HAVE_SYS_TIME_H 42#include <sys/time.h> 43#endif 44#include <time.h> 45#include <sys/socket.h> 46#ifdef HAVE_SYS_SELECT_H 47#include <sys/select.h> 48#endif 49#include <string.h> 50#include <errno.h> 51 52/* support for multiple socket connections */ 53static CONNECTION *Connections = NULL; 54 55/* forward declarations */ 56static int socket_preconnect (void); 57static int socket_connect (int fd, struct sockaddr* sa); 58static CONNECTION* socket_new_conn (void); 59 60/* Wrappers */ 61int mutt_socket_open (CONNECTION* conn) 62{ 63 int rc; 64 65 if (socket_preconnect ()) 66 return -1; 67 68 mutt_allow_interrupt (1); 69 rc = conn->conn_open (conn); 70 mutt_allow_interrupt (0); 71 72 dprint (2, (debugfile, "Connected to %s:%d on fd=%d\n", 73 NONULL (conn->account.host), conn->account.port, conn->fd)); 74 75 return rc; 76} 77 78int mutt_socket_close (CONNECTION* conn) 79{ 80 int rc = -1; 81 82 if (conn->fd < 0) 83 dprint (1, (debugfile, "mutt_socket_close: Attempt to close closed connection.\n")); 84 else { 85 mutt_allow_interrupt (1); 86 rc = conn->conn_close (conn); 87 mutt_allow_interrupt (0); 88 } 89 90 conn->fd = -1; 91 conn->ssf = 0; 92 conn->bufpos = 0; 93 conn->available = 0; 94 95 return rc; 96} 97 98int mutt_socket_read (CONNECTION* conn, char* buf, size_t len) 99{ 100 int rc; 101 102 if (conn->fd < 0) 103 { 104 dprint (1, (debugfile, "mutt_socket_read: attempt to read from closed connection\n")); 105 return -1; 106 } 107 108 mutt_allow_interrupt (1); 109 rc = conn->conn_read (conn, buf, len); 110 mutt_allow_interrupt (0); 111 112 /* EOF */ 113 if (rc == 0) 114 { 115 mutt_error (_("Connection to %s closed"), conn->account.host); 116 mutt_sleep (2); 117 } 118 if (rc <= 0) 119 mutt_socket_close (conn); 120 121 return rc; 122} 123 124int mutt_socket_write_d (CONNECTION *conn, const char *buf, int len, int dbg) 125{ 126 int rc; 127 int sent = 0; 128 129 dprint (dbg, (debugfile,"%d> %s", conn->fd, buf)); 130 131 if (conn->fd < 0) 132 { 133 dprint (1, (debugfile, "mutt_socket_write: attempt to write to closed connection\n")); 134 return -1; 135 } 136 137 if (len < 0) 138 len = mutt_strlen (buf); 139 140 while (sent < len) 141 { 142 mutt_allow_interrupt (1); 143 rc = conn->conn_write (conn, buf + sent, len - sent); 144 mutt_allow_interrupt (0); 145 146 if (rc < 0) 147 { 148 dprint (1, (debugfile, 149 "mutt_socket_write: error writing (%s), closing socket\n", 150 strerror(errno))); 151 mutt_socket_close (conn); 152 153 return -1; 154 } 155 156 if (rc < len - sent) 157 dprint (3, (debugfile, 158 "mutt_socket_write: short write (%d of %d bytes)\n", rc, 159 len - sent)); 160 161 sent += rc; 162 } 163 164 return sent; 165} 166 167/* poll whether reads would block. 168 * Returns: >0 if there is data to read, 169 * 0 if a read would block, 170 * -1 if this connection doesn't support polling */ 171int mutt_socket_poll (CONNECTION* conn, time_t wait_secs) 172{ 173 if (conn->bufpos < conn->available) 174 return conn->available - conn->bufpos; 175 176 if (conn->conn_poll) 177 return conn->conn_poll (conn, wait_secs); 178 179 return -1; 180} 181 182/* simple read buffering to speed things up. */ 183int mutt_socket_readchar (CONNECTION *conn, char *c) 184{ 185 if (conn->bufpos >= conn->available) 186 { 187 if (conn->fd >= 0) 188 { 189 mutt_allow_interrupt (1); 190 conn->available = conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf)); 191 mutt_allow_interrupt (0); 192 } 193 else 194 { 195 dprint (1, (debugfile, "mutt_socket_readchar: attempt to read from closed connection.\n")); 196 return -1; 197 } 198 conn->bufpos = 0; 199 if (conn->available == 0) 200 { 201 mutt_error (_("Connection to %s closed"), conn->account.host); 202 mutt_sleep (2); 203 } 204 if (conn->available <= 0) 205 { 206 mutt_socket_close (conn); 207 return -1; 208 } 209 } 210 *c = conn->inbuf[conn->bufpos]; 211 conn->bufpos++; 212 return 1; 213} 214 215int mutt_socket_readln_d (char* buf, size_t buflen, CONNECTION* conn, int dbg) 216{ 217 char ch; 218 int i; 219 220 for (i = 0; i < buflen-1; i++) 221 { 222 if (mutt_socket_readchar (conn, &ch) != 1) 223 { 224 buf[i] = '\0'; 225 return -1; 226 } 227 228 if (ch == '\n') 229 break; 230 buf[i] = ch; 231 } 232 233 /* strip \r from \r\n termination */ 234 if (i && buf[i-1] == '\r') 235 i--; 236 buf[i] = '\0'; 237 238 dprint (dbg, (debugfile, "%d< %s\n", conn->fd, buf)); 239 240 /* number of bytes read, not strlen */ 241 return i + 1; 242} 243 244CONNECTION* mutt_socket_head (void) 245{ 246 return Connections; 247} 248 249/* mutt_socket_free: remove connection from connection list and free it */ 250void mutt_socket_free (CONNECTION* conn) 251{ 252 CONNECTION* iter; 253 CONNECTION* tmp; 254 255 iter = Connections; 256 257 /* head is special case, doesn't need prev updated */ 258 if (iter == conn) 259 { 260 Connections = iter->next; 261 FREE (&iter); 262 return; 263 } 264 265 while (iter->next) 266 { 267 if (iter->next == conn) 268 { 269 tmp = iter->next; 270 iter->next = tmp->next; 271 FREE (&tmp); 272 return; 273 } 274 iter = iter->next; 275 } 276} 277 278/* mutt_conn_find: find a connection off the list of connections whose 279 * account matches account. If start is not null, only search for 280 * connections after the given connection (allows higher level socket code 281 * to make more fine-grained searches than account info - eg in IMAP we may 282 * wish to find a connection which is not in IMAP_SELECTED state) */ 283CONNECTION* mutt_conn_find (const CONNECTION* start, const ACCOUNT* account) 284{ 285 CONNECTION* conn; 286 ciss_url_t url; 287 char hook[LONG_STRING]; 288 289 /* account isn't actually modified, since url isn't either */ 290 mutt_account_tourl ((ACCOUNT*) account, &url); 291 url.path = NULL; 292 url_ciss_tostring (&url, hook, sizeof (hook), 0); 293 mutt_account_hook (hook); 294 295 conn = start ? start->next : Connections; 296 while (conn) 297 { 298 if (mutt_account_match (account, &(conn->account))) 299 return conn; 300 conn = conn->next; 301 } 302 303 conn = socket_new_conn (); 304 memcpy (&conn->account, account, sizeof (ACCOUNT)); 305 306 conn->next = Connections; 307 Connections = conn; 308 309 if (Tunnel) 310 mutt_tunnel_socket_setup (conn); 311 else if (account->flags & MUTT_ACCT_SSL) 312 { 313#if defined(USE_SSL) 314 if (mutt_ssl_socket_setup (conn) < 0) 315 { 316 mutt_socket_free (conn); 317 return NULL; 318 } 319#else 320 mutt_error _("SSL is unavailable."); 321 mutt_sleep (2); 322 mutt_socket_free (conn); 323 324 return NULL; 325#endif 326 } 327 else 328 { 329 conn->conn_read = raw_socket_read; 330 conn->conn_write = raw_socket_write; 331 conn->conn_open = raw_socket_open; 332 conn->conn_close = raw_socket_close; 333 conn->conn_poll = raw_socket_poll; 334 } 335 336 return conn; 337} 338 339static int socket_preconnect (void) 340{ 341 int rc; 342 int save_errno; 343 344 if (mutt_strlen (Preconnect)) 345 { 346 dprint (2, (debugfile, "Executing preconnect: %s\n", Preconnect)); 347 rc = mutt_system (Preconnect); 348 dprint (2, (debugfile, "Preconnect result: %d\n", rc)); 349 if (rc) 350 { 351 save_errno = errno; 352 mutt_perror (_("Preconnect command failed.")); 353 mutt_sleep (1); 354 355 return save_errno; 356 } 357 } 358 359 return 0; 360} 361 362/* socket_connect: set up to connect to a socket fd. */ 363static int socket_connect (int fd, struct sockaddr* sa) 364{ 365 int sa_size; 366 int save_errno; 367 sigset_t set; 368 369 if (sa->sa_family == AF_INET) 370 sa_size = sizeof (struct sockaddr_in); 371#ifdef HAVE_GETADDRINFO 372 else if (sa->sa_family == AF_INET6) 373 sa_size = sizeof (struct sockaddr_in6); 374#endif 375 else 376 { 377 dprint (1, (debugfile, "Unknown address family!\n")); 378 return -1; 379 } 380 381 if (ConnectTimeout > 0) 382 alarm (ConnectTimeout); 383 384 mutt_allow_interrupt (1); 385 386 /* FreeBSD's connect() does not respect SA_RESTART, meaning 387 * a SIGWINCH will cause the connect to fail. */ 388 sigemptyset (&set); 389 sigaddset (&set, SIGWINCH); 390 sigprocmask (SIG_BLOCK, &set, NULL); 391 392 save_errno = 0; 393 394 if (connect (fd, sa, sa_size) < 0) 395 { 396 save_errno = errno; 397 dprint (2, (debugfile, "Connection failed. errno: %d...\n", errno)); 398 SigInt = 0; /* reset in case we caught SIGINTR while in connect() */ 399 } 400 401 if (ConnectTimeout > 0) 402 alarm (0); 403 mutt_allow_interrupt (0); 404 sigprocmask (SIG_UNBLOCK, &set, NULL); 405 406 return save_errno; 407} 408 409/* socket_new_conn: allocate and initialise a new connection. */ 410static CONNECTION* socket_new_conn (void) 411{ 412 CONNECTION* conn; 413 414 conn = (CONNECTION *) safe_calloc (1, sizeof (CONNECTION)); 415 conn->fd = -1; 416 417 return conn; 418} 419 420int raw_socket_close (CONNECTION *conn) 421{ 422 return close (conn->fd); 423} 424 425int raw_socket_read (CONNECTION* conn, char* buf, size_t len) 426{ 427 int rc; 428 429 do 430 { 431 rc = read (conn->fd, buf, len); 432 } while (rc < 0 && errno == EINTR); 433 434 if (rc < 0) 435 { 436 mutt_error (_("Error talking to %s (%s)"), conn->account.host, 437 strerror (errno)); 438 mutt_sleep (2); 439 return -1; 440 } 441 442 return rc; 443} 444 445int raw_socket_write (CONNECTION* conn, const char* buf, size_t count) 446{ 447 int rc; 448 size_t sent = 0; 449 450 do 451 { 452 do 453 { 454 rc = write (conn->fd, buf + sent, count - sent); 455 } while (rc < 0 && errno == EINTR); 456 457 if (rc < 0) 458 { 459 mutt_error (_("Error talking to %s (%s)"), conn->account.host, 460 strerror (errno)); 461 mutt_sleep (2); 462 return -1; 463 } 464 465 sent += rc; 466 } while (sent < count); 467 468 return sent; 469} 470 471int raw_socket_poll (CONNECTION* conn, time_t wait_secs) 472{ 473 fd_set rfds; 474 unsigned long wait_millis, post_t_millis; 475 struct timeval tv, pre_t, post_t; 476 int rv; 477 478 if (conn->fd < 0) 479 return -1; 480 481 wait_millis = wait_secs * 1000UL; 482 483 FOREVER 484 { 485 tv.tv_sec = wait_millis / 1000; 486 tv.tv_usec = (wait_millis % 1000) * 1000; 487 488 FD_ZERO (&rfds); 489 FD_SET (conn->fd, &rfds); 490 491 gettimeofday (&pre_t, NULL); 492 rv = select (conn->fd + 1, &rfds, NULL, NULL, &tv); 493 gettimeofday (&post_t, NULL); 494 495 if (rv > 0 || 496 (rv < 0 && errno != EINTR)) 497 return rv; 498 499 if (SigInt) 500 mutt_query_exit (); 501 502 wait_millis += (pre_t.tv_sec * 1000UL) + (pre_t.tv_usec / 1000); 503 post_t_millis = (post_t.tv_sec * 1000UL) + (post_t.tv_usec / 1000); 504 if (wait_millis <= post_t_millis) 505 return 0; 506 wait_millis -= post_t_millis; 507 } 508} 509 510int raw_socket_open (CONNECTION* conn) 511{ 512 int rc; 513 int fd; 514 515 char *host_idna = NULL; 516 517#ifdef HAVE_GETADDRINFO 518/* --- IPv4/6 --- */ 519 520 /* "65536\0" */ 521 char port[6]; 522 struct addrinfo hints; 523 struct addrinfo* res; 524 struct addrinfo* cur; 525 526 /* we accept v4 or v6 STREAM sockets */ 527 memset (&hints, 0, sizeof (hints)); 528 529 if (option (OPTUSEIPV6)) 530 hints.ai_family = AF_UNSPEC; 531 else 532 hints.ai_family = AF_INET; 533 534 hints.ai_socktype = SOCK_STREAM; 535 536 snprintf (port, sizeof (port), "%d", conn->account.port); 537 538# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2) 539 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) 540 { 541 mutt_error (_("Bad IDN \"%s\"."), conn->account.host); 542 return -1; 543 } 544# else 545 host_idna = conn->account.host; 546# endif 547 548 if (!option(OPTNOCURSES)) 549 mutt_message (_("Looking up %s..."), conn->account.host); 550 551 rc = getaddrinfo (host_idna, port, &hints, &res); 552 553# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2) 554 FREE (&host_idna); 555# endif 556 557 if (rc) 558 { 559 mutt_error (_("Could not find the host \"%s\""), conn->account.host); 560 mutt_sleep (2); 561 return -1; 562 } 563 564 if (!option(OPTNOCURSES)) 565 mutt_message (_("Connecting to %s..."), conn->account.host); 566 567 rc = -1; 568 for (cur = res; cur != NULL; cur = cur->ai_next) 569 { 570 fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol); 571 if (fd >= 0) 572 { 573 if ((rc = socket_connect (fd, cur->ai_addr)) == 0) 574 { 575 fcntl (fd, F_SETFD, FD_CLOEXEC); 576 conn->fd = fd; 577 break; 578 } 579 else 580 close (fd); 581 } 582 } 583 584 freeaddrinfo (res); 585 586#else 587 /* --- IPv4 only --- */ 588 589 struct sockaddr_in sin; 590 struct hostent* he; 591 int i; 592 593 memset (&sin, 0, sizeof (sin)); 594 sin.sin_port = htons (conn->account.port); 595 sin.sin_family = AF_INET; 596 597# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2) 598 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) 599 { 600 mutt_error (_("Bad IDN \"%s\"."), conn->account.host); 601 return -1; 602 } 603# else 604 host_idna = conn->account.host; 605# endif 606 607 if (!option(OPTNOCURSES)) 608 mutt_message (_("Looking up %s..."), conn->account.host); 609 610 he = gethostbyname (host_idna); 611 612# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2) 613 FREE (&host_idna); 614# endif 615 616 if (! he) 617 { 618 mutt_error (_("Could not find the host \"%s\""), conn->account.host); 619 620 return -1; 621 } 622 623 if (!option(OPTNOCURSES)) 624 mutt_message (_("Connecting to %s..."), conn->account.host); 625 626 rc = -1; 627 for (i = 0; he->h_addr_list[i] != NULL; i++) 628 { 629 memcpy (&sin.sin_addr, he->h_addr_list[i], he->h_length); 630 fd = socket (PF_INET, SOCK_STREAM, IPPROTO_IP); 631 632 if (fd >= 0) 633 { 634 if ((rc = socket_connect (fd, (struct sockaddr*) &sin)) == 0) 635 { 636 fcntl (fd, F_SETFD, FD_CLOEXEC); 637 conn->fd = fd; 638 break; 639 } 640 else 641 close (fd); 642 } 643 } 644 645#endif 646 if (rc) 647 { 648 mutt_error (_("Could not connect to %s (%s)."), conn->account.host, 649 (rc > 0) ? strerror (rc) : _("unknown error")); 650 mutt_sleep (2); 651 return -1; 652 } 653 654 return 0; 655}