ssh-keyscan.c revision 124211
1/* 2 * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. 3 * 4 * Modification and redistribution in source and binary forms is 5 * permitted provided that due credit is given to the author and the 6 * OpenBSD project by leaving this copyright notice intact. 7 */ 8 9#include "includes.h" 10RCSID("$OpenBSD: ssh-keyscan.c,v 1.44 2003/06/28 16:23:06 deraadt Exp $"); 11 12#include "openbsd-compat/sys-queue.h" 13 14#include <openssl/bn.h> 15 16#include <setjmp.h> 17#include "xmalloc.h" 18#include "ssh.h" 19#include "ssh1.h" 20#include "key.h" 21#include "kex.h" 22#include "compat.h" 23#include "myproposal.h" 24#include "packet.h" 25#include "dispatch.h" 26#include "buffer.h" 27#include "bufaux.h" 28#include "log.h" 29#include "atomicio.h" 30#include "misc.h" 31 32/* Flag indicating whether IPv4 or IPv6. This can be set on the command line. 33 Default value is AF_UNSPEC means both IPv4 and IPv6. */ 34int IPv4or6 = AF_UNSPEC; 35 36int ssh_port = SSH_DEFAULT_PORT; 37 38#define KT_RSA1 1 39#define KT_DSA 2 40#define KT_RSA 4 41 42int get_keytypes = KT_RSA1; /* Get only RSA1 keys by default */ 43 44#define MAXMAXFD 256 45 46/* The number of seconds after which to give up on a TCP connection */ 47int timeout = 5; 48 49int maxfd; 50#define MAXCON (maxfd - 10) 51 52#ifdef HAVE___PROGNAME 53extern char *__progname; 54#else 55char *__progname; 56#endif 57fd_set *read_wait; 58size_t read_wait_size; 59int ncon; 60int nonfatal_fatal = 0; 61jmp_buf kexjmp; 62Key *kexjmp_key; 63 64/* 65 * Keep a connection structure for each file descriptor. The state 66 * associated with file descriptor n is held in fdcon[n]. 67 */ 68typedef struct Connection { 69 u_char c_status; /* State of connection on this file desc. */ 70#define CS_UNUSED 0 /* File descriptor unused */ 71#define CS_CON 1 /* Waiting to connect/read greeting */ 72#define CS_SIZE 2 /* Waiting to read initial packet size */ 73#define CS_KEYS 3 /* Waiting to read public key packet */ 74 int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ 75 int c_plen; /* Packet length field for ssh packet */ 76 int c_len; /* Total bytes which must be read. */ 77 int c_off; /* Length of data read so far. */ 78 int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */ 79 char *c_namebase; /* Address to free for c_name and c_namelist */ 80 char *c_name; /* Hostname of connection for errors */ 81 char *c_namelist; /* Pointer to other possible addresses */ 82 char *c_output_name; /* Hostname of connection for output */ 83 char *c_data; /* Data read from this fd */ 84 Kex *c_kex; /* The key-exchange struct for ssh2 */ 85 struct timeval c_tv; /* Time at which connection gets aborted */ 86 TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ 87} con; 88 89TAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ 90con *fdcon; 91 92/* 93 * This is just a wrapper around fgets() to make it usable. 94 */ 95 96/* Stress-test. Increase this later. */ 97#define LINEBUF_SIZE 16 98 99typedef struct { 100 char *buf; 101 u_int size; 102 int lineno; 103 const char *filename; 104 FILE *stream; 105 void (*errfun) (const char *,...); 106} Linebuf; 107 108static Linebuf * 109Linebuf_alloc(const char *filename, void (*errfun) (const char *,...)) 110{ 111 Linebuf *lb; 112 113 if (!(lb = malloc(sizeof(*lb)))) { 114 if (errfun) 115 (*errfun) ("linebuf (%s): malloc failed\n", 116 filename ? filename : "(stdin)"); 117 return (NULL); 118 } 119 if (filename) { 120 lb->filename = filename; 121 if (!(lb->stream = fopen(filename, "r"))) { 122 xfree(lb); 123 if (errfun) 124 (*errfun) ("%s: %s\n", filename, strerror(errno)); 125 return (NULL); 126 } 127 } else { 128 lb->filename = "(stdin)"; 129 lb->stream = stdin; 130 } 131 132 if (!(lb->buf = malloc(lb->size = LINEBUF_SIZE))) { 133 if (errfun) 134 (*errfun) ("linebuf (%s): malloc failed\n", lb->filename); 135 xfree(lb); 136 return (NULL); 137 } 138 lb->errfun = errfun; 139 lb->lineno = 0; 140 return (lb); 141} 142 143static void 144Linebuf_free(Linebuf * lb) 145{ 146 fclose(lb->stream); 147 xfree(lb->buf); 148 xfree(lb); 149} 150 151#if 0 152static void 153Linebuf_restart(Linebuf * lb) 154{ 155 clearerr(lb->stream); 156 rewind(lb->stream); 157 lb->lineno = 0; 158} 159 160static int 161Linebuf_lineno(Linebuf * lb) 162{ 163 return (lb->lineno); 164} 165#endif 166 167static char * 168Linebuf_getline(Linebuf * lb) 169{ 170 int n = 0; 171 void *p; 172 173 lb->lineno++; 174 for (;;) { 175 /* Read a line */ 176 if (!fgets(&lb->buf[n], lb->size - n, lb->stream)) { 177 if (ferror(lb->stream) && lb->errfun) 178 (*lb->errfun)("%s: %s\n", lb->filename, 179 strerror(errno)); 180 return (NULL); 181 } 182 n = strlen(lb->buf); 183 184 /* Return it or an error if it fits */ 185 if (n > 0 && lb->buf[n - 1] == '\n') { 186 lb->buf[n - 1] = '\0'; 187 return (lb->buf); 188 } 189 if (n != lb->size - 1) { 190 if (lb->errfun) 191 (*lb->errfun)("%s: skipping incomplete last line\n", 192 lb->filename); 193 return (NULL); 194 } 195 /* Double the buffer if we need more space */ 196 lb->size *= 2; 197 if ((p = realloc(lb->buf, lb->size)) == NULL) { 198 lb->size /= 2; 199 if (lb->errfun) 200 (*lb->errfun)("linebuf (%s): realloc failed\n", 201 lb->filename); 202 return (NULL); 203 } 204 lb->buf = p; 205 } 206} 207 208static int 209fdlim_get(int hard) 210{ 211#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) 212 struct rlimit rlfd; 213 214 if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 215 return (-1); 216 if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY) 217 return 10000; 218 else 219 return hard ? rlfd.rlim_max : rlfd.rlim_cur; 220#elif defined (HAVE_SYSCONF) 221 return sysconf (_SC_OPEN_MAX); 222#else 223 return 10000; 224#endif 225} 226 227static int 228fdlim_set(int lim) 229{ 230#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 231 struct rlimit rlfd; 232#endif 233 234 if (lim <= 0) 235 return (-1); 236#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 237 if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) 238 return (-1); 239 rlfd.rlim_cur = lim; 240 if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0) 241 return (-1); 242#elif defined (HAVE_SETDTABLESIZE) 243 setdtablesize(lim); 244#endif 245 return (0); 246} 247 248/* 249 * This is an strsep function that returns a null field for adjacent 250 * separators. This is the same as the 4.4BSD strsep, but different from the 251 * one in the GNU libc. 252 */ 253static char * 254xstrsep(char **str, const char *delim) 255{ 256 char *s, *e; 257 258 if (!**str) 259 return (NULL); 260 261 s = *str; 262 e = s + strcspn(s, delim); 263 264 if (*e != '\0') 265 *e++ = '\0'; 266 *str = e; 267 268 return (s); 269} 270 271/* 272 * Get the next non-null token (like GNU strsep). Strsep() will return a 273 * null token for two adjacent separators, so we may have to loop. 274 */ 275static char * 276strnnsep(char **stringp, char *delim) 277{ 278 char *tok; 279 280 do { 281 tok = xstrsep(stringp, delim); 282 } while (tok && *tok == '\0'); 283 return (tok); 284} 285 286static Key * 287keygrab_ssh1(con *c) 288{ 289 static Key *rsa; 290 static Buffer msg; 291 292 if (rsa == NULL) { 293 buffer_init(&msg); 294 rsa = key_new(KEY_RSA1); 295 } 296 buffer_append(&msg, c->c_data, c->c_plen); 297 buffer_consume(&msg, 8 - (c->c_plen & 7)); /* padding */ 298 if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) { 299 error("%s: invalid packet type", c->c_name); 300 buffer_clear(&msg); 301 return NULL; 302 } 303 buffer_consume(&msg, 8); /* cookie */ 304 305 /* server key */ 306 (void) buffer_get_int(&msg); 307 buffer_get_bignum(&msg, rsa->rsa->e); 308 buffer_get_bignum(&msg, rsa->rsa->n); 309 310 /* host key */ 311 (void) buffer_get_int(&msg); 312 buffer_get_bignum(&msg, rsa->rsa->e); 313 buffer_get_bignum(&msg, rsa->rsa->n); 314 315 buffer_clear(&msg); 316 317 return (rsa); 318} 319 320static int 321hostjump(Key *hostkey) 322{ 323 kexjmp_key = hostkey; 324 longjmp(kexjmp, 1); 325} 326 327static int 328ssh2_capable(int remote_major, int remote_minor) 329{ 330 switch (remote_major) { 331 case 1: 332 if (remote_minor == 99) 333 return 1; 334 break; 335 case 2: 336 return 1; 337 default: 338 break; 339 } 340 return 0; 341} 342 343static Key * 344keygrab_ssh2(con *c) 345{ 346 int j; 347 348 packet_set_connection(c->c_fd, c->c_fd); 349 enable_compat20(); 350 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = c->c_keytype == KT_DSA? 351 "ssh-dss": "ssh-rsa"; 352 c->c_kex = kex_setup(myproposal); 353 c->c_kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; 354 c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; 355 c->c_kex->verify_host_key = hostjump; 356 357 if (!(j = setjmp(kexjmp))) { 358 nonfatal_fatal = 1; 359 dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex); 360 fprintf(stderr, "Impossible! dispatch_run() returned!\n"); 361 exit(1); 362 } 363 nonfatal_fatal = 0; 364 xfree(c->c_kex); 365 c->c_kex = NULL; 366 packet_close(); 367 368 return j < 0? NULL : kexjmp_key; 369} 370 371static void 372keyprint(con *c, Key *key) 373{ 374 if (!key) 375 return; 376 377 fprintf(stdout, "%s ", c->c_output_name ? c->c_output_name : c->c_name); 378 key_write(key, stdout); 379 fputs("\n", stdout); 380} 381 382static int 383tcpconnect(char *host) 384{ 385 struct addrinfo hints, *ai, *aitop; 386 char strport[NI_MAXSERV]; 387 int gaierr, s = -1; 388 389 snprintf(strport, sizeof strport, "%d", ssh_port); 390 memset(&hints, 0, sizeof(hints)); 391 hints.ai_family = IPv4or6; 392 hints.ai_socktype = SOCK_STREAM; 393 if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 394 fatal("getaddrinfo %s: %s", host, gai_strerror(gaierr)); 395 for (ai = aitop; ai; ai = ai->ai_next) { 396 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 397 if (s < 0) { 398 error("socket: %s", strerror(errno)); 399 continue; 400 } 401 if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) 402 fatal("F_SETFL: %s", strerror(errno)); 403 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && 404 errno != EINPROGRESS) 405 error("connect (`%s'): %s", host, strerror(errno)); 406 else 407 break; 408 close(s); 409 s = -1; 410 } 411 freeaddrinfo(aitop); 412 return s; 413} 414 415static int 416conalloc(char *iname, char *oname, int keytype) 417{ 418 char *namebase, *name, *namelist; 419 int s; 420 421 namebase = namelist = xstrdup(iname); 422 423 do { 424 name = xstrsep(&namelist, ","); 425 if (!name) { 426 xfree(namebase); 427 return (-1); 428 } 429 } while ((s = tcpconnect(name)) < 0); 430 431 if (s >= maxfd) 432 fatal("conalloc: fdno %d too high", s); 433 if (fdcon[s].c_status) 434 fatal("conalloc: attempt to reuse fdno %d", s); 435 436 fdcon[s].c_fd = s; 437 fdcon[s].c_status = CS_CON; 438 fdcon[s].c_namebase = namebase; 439 fdcon[s].c_name = name; 440 fdcon[s].c_namelist = namelist; 441 fdcon[s].c_output_name = xstrdup(oname); 442 fdcon[s].c_data = (char *) &fdcon[s].c_plen; 443 fdcon[s].c_len = 4; 444 fdcon[s].c_off = 0; 445 fdcon[s].c_keytype = keytype; 446 gettimeofday(&fdcon[s].c_tv, NULL); 447 fdcon[s].c_tv.tv_sec += timeout; 448 TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 449 FD_SET(s, read_wait); 450 ncon++; 451 return (s); 452} 453 454static void 455confree(int s) 456{ 457 if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) 458 fatal("confree: attempt to free bad fdno %d", s); 459 close(s); 460 xfree(fdcon[s].c_namebase); 461 xfree(fdcon[s].c_output_name); 462 if (fdcon[s].c_status == CS_KEYS) 463 xfree(fdcon[s].c_data); 464 fdcon[s].c_status = CS_UNUSED; 465 fdcon[s].c_keytype = 0; 466 TAILQ_REMOVE(&tq, &fdcon[s], c_link); 467 FD_CLR(s, read_wait); 468 ncon--; 469} 470 471static void 472contouch(int s) 473{ 474 TAILQ_REMOVE(&tq, &fdcon[s], c_link); 475 gettimeofday(&fdcon[s].c_tv, NULL); 476 fdcon[s].c_tv.tv_sec += timeout; 477 TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 478} 479 480static int 481conrecycle(int s) 482{ 483 con *c = &fdcon[s]; 484 int ret; 485 486 ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); 487 confree(s); 488 return (ret); 489} 490 491static void 492congreet(int s) 493{ 494 int remote_major, remote_minor, n = 0; 495 char buf[256], *cp; 496 char remote_version[sizeof buf]; 497 size_t bufsiz; 498 con *c = &fdcon[s]; 499 500 bufsiz = sizeof(buf); 501 cp = buf; 502 while (bufsiz-- && (n = read(s, cp, 1)) == 1 && *cp != '\n') { 503 if (*cp == '\r') 504 *cp = '\n'; 505 cp++; 506 } 507 if (n < 0) { 508 if (errno != ECONNREFUSED) 509 error("read (%s): %s", c->c_name, strerror(errno)); 510 conrecycle(s); 511 return; 512 } 513 if (n == 0) { 514 error("%s: Connection closed by remote host", c->c_name); 515 conrecycle(s); 516 return; 517 } 518 if (*cp != '\n' && *cp != '\r') { 519 error("%s: bad greeting", c->c_name); 520 confree(s); 521 return; 522 } 523 *cp = '\0'; 524 if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 525 &remote_major, &remote_minor, remote_version) == 3) 526 compat_datafellows(remote_version); 527 else 528 datafellows = 0; 529 if (c->c_keytype != KT_RSA1) { 530 if (!ssh2_capable(remote_major, remote_minor)) { 531 debug("%s doesn't support ssh2", c->c_name); 532 confree(s); 533 return; 534 } 535 } else if (remote_major != 1) { 536 debug("%s doesn't support ssh1", c->c_name); 537 confree(s); 538 return; 539 } 540 fprintf(stderr, "# %s %s\n", c->c_name, chop(buf)); 541 n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", 542 c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2, 543 c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2); 544 if (atomicio(vwrite, s, buf, n) != n) { 545 error("write (%s): %s", c->c_name, strerror(errno)); 546 confree(s); 547 return; 548 } 549 if (c->c_keytype != KT_RSA1) { 550 keyprint(c, keygrab_ssh2(c)); 551 confree(s); 552 return; 553 } 554 c->c_status = CS_SIZE; 555 contouch(s); 556} 557 558static void 559conread(int s) 560{ 561 con *c = &fdcon[s]; 562 int n; 563 564 if (c->c_status == CS_CON) { 565 congreet(s); 566 return; 567 } 568 n = read(s, c->c_data + c->c_off, c->c_len - c->c_off); 569 if (n < 0) { 570 error("read (%s): %s", c->c_name, strerror(errno)); 571 confree(s); 572 return; 573 } 574 c->c_off += n; 575 576 if (c->c_off == c->c_len) 577 switch (c->c_status) { 578 case CS_SIZE: 579 c->c_plen = htonl(c->c_plen); 580 c->c_len = c->c_plen + 8 - (c->c_plen & 7); 581 c->c_off = 0; 582 c->c_data = xmalloc(c->c_len); 583 c->c_status = CS_KEYS; 584 break; 585 case CS_KEYS: 586 keyprint(c, keygrab_ssh1(c)); 587 confree(s); 588 return; 589 break; 590 default: 591 fatal("conread: invalid status %d", c->c_status); 592 break; 593 } 594 595 contouch(s); 596} 597 598static void 599conloop(void) 600{ 601 struct timeval seltime, now; 602 fd_set *r, *e; 603 con *c; 604 int i; 605 606 gettimeofday(&now, NULL); 607 c = TAILQ_FIRST(&tq); 608 609 if (c && (c->c_tv.tv_sec > now.tv_sec || 610 (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { 611 seltime = c->c_tv; 612 seltime.tv_sec -= now.tv_sec; 613 seltime.tv_usec -= now.tv_usec; 614 if (seltime.tv_usec < 0) { 615 seltime.tv_usec += 1000000; 616 seltime.tv_sec--; 617 } 618 } else 619 seltime.tv_sec = seltime.tv_usec = 0; 620 621 r = xmalloc(read_wait_size); 622 memcpy(r, read_wait, read_wait_size); 623 e = xmalloc(read_wait_size); 624 memcpy(e, read_wait, read_wait_size); 625 626 while (select(maxfd, r, NULL, e, &seltime) == -1 && 627 (errno == EAGAIN || errno == EINTR)) 628 ; 629 630 for (i = 0; i < maxfd; i++) { 631 if (FD_ISSET(i, e)) { 632 error("%s: exception!", fdcon[i].c_name); 633 confree(i); 634 } else if (FD_ISSET(i, r)) 635 conread(i); 636 } 637 xfree(r); 638 xfree(e); 639 640 c = TAILQ_FIRST(&tq); 641 while (c && (c->c_tv.tv_sec < now.tv_sec || 642 (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { 643 int s = c->c_fd; 644 645 c = TAILQ_NEXT(c, c_link); 646 conrecycle(s); 647 } 648} 649 650static void 651do_host(char *host) 652{ 653 char *name = strnnsep(&host, " \t\n"); 654 int j; 655 656 if (name == NULL) 657 return; 658 for (j = KT_RSA1; j <= KT_RSA; j *= 2) { 659 if (get_keytypes & j) { 660 while (ncon >= MAXCON) 661 conloop(); 662 conalloc(name, *host ? host : name, j); 663 } 664 } 665} 666 667void 668fatal(const char *fmt,...) 669{ 670 va_list args; 671 672 va_start(args, fmt); 673 do_log(SYSLOG_LEVEL_FATAL, fmt, args); 674 va_end(args); 675 if (nonfatal_fatal) 676 longjmp(kexjmp, -1); 677 else 678 fatal_cleanup(); 679} 680 681static void 682usage(void) 683{ 684 fprintf(stderr, "usage: %s [-v46] [-p port] [-T timeout] [-t type] [-f file]\n" 685 "\t\t [host | addrlist namelist] [...]\n", 686 __progname); 687 exit(1); 688} 689 690int 691main(int argc, char **argv) 692{ 693 int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; 694 int opt, fopt_count = 0; 695 char *tname; 696 697 extern int optind; 698 extern char *optarg; 699 700 __progname = ssh_get_progname(argv[0]); 701 init_rng(); 702 seed_rng(); 703 TAILQ_INIT(&tq); 704 705 if (argc <= 1) 706 usage(); 707 708 while ((opt = getopt(argc, argv, "v46p:T:t:f:")) != -1) { 709 switch (opt) { 710 case 'p': 711 ssh_port = a2port(optarg); 712 if (ssh_port == 0) { 713 fprintf(stderr, "Bad port '%s'\n", optarg); 714 exit(1); 715 } 716 break; 717 case 'T': 718 timeout = convtime(optarg); 719 if (timeout == -1 || timeout == 0) { 720 fprintf(stderr, "Bad timeout '%s'\n", optarg); 721 usage(); 722 } 723 break; 724 case 'v': 725 if (!debug_flag) { 726 debug_flag = 1; 727 log_level = SYSLOG_LEVEL_DEBUG1; 728 } 729 else if (log_level < SYSLOG_LEVEL_DEBUG3) 730 log_level++; 731 else 732 fatal("Too high debugging level."); 733 break; 734 case 'f': 735 if (strcmp(optarg, "-") == 0) 736 optarg = NULL; 737 argv[fopt_count++] = optarg; 738 break; 739 case 't': 740 get_keytypes = 0; 741 tname = strtok(optarg, ","); 742 while (tname) { 743 int type = key_type_from_name(tname); 744 switch (type) { 745 case KEY_RSA1: 746 get_keytypes |= KT_RSA1; 747 break; 748 case KEY_DSA: 749 get_keytypes |= KT_DSA; 750 break; 751 case KEY_RSA: 752 get_keytypes |= KT_RSA; 753 break; 754 case KEY_UNSPEC: 755 fatal("unknown key type %s", tname); 756 } 757 tname = strtok(NULL, ","); 758 } 759 break; 760 case '4': 761 IPv4or6 = AF_INET; 762 break; 763 case '6': 764 IPv4or6 = AF_INET6; 765 break; 766 case '?': 767 default: 768 usage(); 769 } 770 } 771 if (optind == argc && !fopt_count) 772 usage(); 773 774 log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); 775 776 maxfd = fdlim_get(1); 777 if (maxfd < 0) 778 fatal("%s: fdlim_get: bad value", __progname); 779 if (maxfd > MAXMAXFD) 780 maxfd = MAXMAXFD; 781 if (MAXCON <= 0) 782 fatal("%s: not enough file descriptors", __progname); 783 if (maxfd > fdlim_get(0)) 784 fdlim_set(maxfd); 785 fdcon = xmalloc(maxfd * sizeof(con)); 786 memset(fdcon, 0, maxfd * sizeof(con)); 787 788 read_wait_size = howmany(maxfd, NFDBITS) * sizeof(fd_mask); 789 read_wait = xmalloc(read_wait_size); 790 memset(read_wait, 0, read_wait_size); 791 792 if (fopt_count) { 793 Linebuf *lb; 794 char *line; 795 int j; 796 797 for (j = 0; j < fopt_count; j++) { 798 lb = Linebuf_alloc(argv[j], error); 799 if (!lb) 800 continue; 801 while ((line = Linebuf_getline(lb)) != NULL) 802 do_host(line); 803 Linebuf_free(lb); 804 } 805 } 806 807 while (optind < argc) 808 do_host(argv[optind++]); 809 810 while (ncon > 0) 811 conloop(); 812 813 return (0); 814} 815