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