ssh-keyscan.c revision 113911
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.41 2003/02/16 17:09:57 markus Exp $"); 11RCSID("$FreeBSD: head/crypto/openssh/ssh-keyscan.c 113911 2003-04-23 17:13:13Z 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->kex[KEX_DH_GRP1_SHA1] = kexdh_client; 359 c->c_kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; 360 c->c_kex->verify_host_key = hostjump; 361 362 if (!(j = setjmp(kexjmp))) { 363 nonfatal_fatal = 1; 364 dispatch_run(DISPATCH_BLOCK, &c->c_kex->done, c->c_kex); 365 fprintf(stderr, "Impossible! dispatch_run() returned!\n"); 366 exit(1); 367 } 368 nonfatal_fatal = 0; 369 xfree(c->c_kex); 370 c->c_kex = NULL; 371 packet_close(); 372 373 return j < 0? NULL : kexjmp_key; 374} 375 376static void 377keyprint(con *c, Key *key) 378{ 379 if (!key) 380 return; 381 382 fprintf(stdout, "%s ", c->c_output_name ? c->c_output_name : c->c_name); 383 key_write(key, stdout); 384 fputs("\n", stdout); 385} 386 387static int 388tcpconnect(char *host) 389{ 390 struct addrinfo hints, *ai, *aitop; 391 char strport[NI_MAXSERV]; 392 int gaierr, s = -1; 393 394 snprintf(strport, sizeof strport, "%d", ssh_port); 395 memset(&hints, 0, sizeof(hints)); 396 hints.ai_family = IPv4or6; 397 hints.ai_socktype = SOCK_STREAM; 398 if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) 399 fatal("getaddrinfo %s: %s", host, gai_strerror(gaierr)); 400 for (ai = aitop; ai; ai = ai->ai_next) { 401 s = socket(ai->ai_family, SOCK_STREAM, 0); 402 if (s < 0) { 403 error("socket: %s", strerror(errno)); 404 continue; 405 } 406 if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) 407 fatal("F_SETFL: %s", strerror(errno)); 408 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && 409 errno != EINPROGRESS) 410 error("connect (`%s'): %s", host, strerror(errno)); 411 else 412 break; 413 close(s); 414 s = -1; 415 } 416 freeaddrinfo(aitop); 417 return s; 418} 419 420static int 421conalloc(char *iname, char *oname, int keytype) 422{ 423 char *namebase, *name, *namelist; 424 int s; 425 426 namebase = namelist = xstrdup(iname); 427 428 do { 429 name = xstrsep(&namelist, ","); 430 if (!name) { 431 xfree(namebase); 432 return (-1); 433 } 434 } while ((s = tcpconnect(name)) < 0); 435 436 if (s >= maxfd) 437 fatal("conalloc: fdno %d too high", s); 438 if (fdcon[s].c_status) 439 fatal("conalloc: attempt to reuse fdno %d", s); 440 441 fdcon[s].c_fd = s; 442 fdcon[s].c_status = CS_CON; 443 fdcon[s].c_namebase = namebase; 444 fdcon[s].c_name = name; 445 fdcon[s].c_namelist = namelist; 446 fdcon[s].c_output_name = xstrdup(oname); 447 fdcon[s].c_data = (char *) &fdcon[s].c_plen; 448 fdcon[s].c_len = 4; 449 fdcon[s].c_off = 0; 450 fdcon[s].c_keytype = keytype; 451 gettimeofday(&fdcon[s].c_tv, NULL); 452 fdcon[s].c_tv.tv_sec += timeout; 453 TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 454 FD_SET(s, read_wait); 455 ncon++; 456 return (s); 457} 458 459static void 460confree(int s) 461{ 462 if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) 463 fatal("confree: attempt to free bad fdno %d", s); 464 close(s); 465 xfree(fdcon[s].c_namebase); 466 xfree(fdcon[s].c_output_name); 467 if (fdcon[s].c_status == CS_KEYS) 468 xfree(fdcon[s].c_data); 469 fdcon[s].c_status = CS_UNUSED; 470 fdcon[s].c_keytype = 0; 471 TAILQ_REMOVE(&tq, &fdcon[s], c_link); 472 FD_CLR(s, read_wait); 473 ncon--; 474} 475 476static void 477contouch(int s) 478{ 479 TAILQ_REMOVE(&tq, &fdcon[s], c_link); 480 gettimeofday(&fdcon[s].c_tv, NULL); 481 fdcon[s].c_tv.tv_sec += timeout; 482 TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 483} 484 485static int 486conrecycle(int s) 487{ 488 con *c = &fdcon[s]; 489 int ret; 490 491 ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); 492 confree(s); 493 return (ret); 494} 495 496static void 497congreet(int s) 498{ 499 int remote_major, remote_minor, n = 0; 500 char buf[256], *cp; 501 char remote_version[sizeof buf]; 502 size_t bufsiz; 503 con *c = &fdcon[s]; 504 505 bufsiz = sizeof(buf); 506 cp = buf; 507 while (bufsiz-- && (n = read(s, cp, 1)) == 1 && *cp != '\n') { 508 if (*cp == '\r') 509 *cp = '\n'; 510 cp++; 511 } 512 if (n < 0) { 513 if (errno != ECONNREFUSED) 514 error("read (%s): %s", c->c_name, strerror(errno)); 515 conrecycle(s); 516 return; 517 } 518 if (n == 0) { 519 error("%s: Connection closed by remote host", c->c_name); 520 conrecycle(s); 521 return; 522 } 523 if (*cp != '\n' && *cp != '\r') { 524 error("%s: bad greeting", c->c_name); 525 confree(s); 526 return; 527 } 528 *cp = '\0'; 529 if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 530 &remote_major, &remote_minor, remote_version) == 3) 531 compat_datafellows(remote_version); 532 else 533 datafellows = 0; 534 if (c->c_keytype != KT_RSA1) { 535 if (!ssh2_capable(remote_major, remote_minor)) { 536 debug("%s doesn't support ssh2", c->c_name); 537 confree(s); 538 return; 539 } 540 } else if (remote_major != 1) { 541 debug("%s doesn't support ssh1", c->c_name); 542 confree(s); 543 return; 544 } 545 fprintf(stderr, "# %s %s\n", c->c_name, chop(buf)); 546 n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", 547 c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2, 548 c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2); 549 if (atomicio(write, s, buf, n) != n) { 550 error("write (%s): %s", c->c_name, strerror(errno)); 551 confree(s); 552 return; 553 } 554 if (c->c_keytype != KT_RSA1) { 555 keyprint(c, keygrab_ssh2(c)); 556 confree(s); 557 return; 558 } 559 c->c_status = CS_SIZE; 560 contouch(s); 561} 562 563static void 564conread(int s) 565{ 566 con *c = &fdcon[s]; 567 int n; 568 569 if (c->c_status == CS_CON) { 570 congreet(s); 571 return; 572 } 573 n = read(s, c->c_data + c->c_off, c->c_len - c->c_off); 574 if (n < 0) { 575 error("read (%s): %s", c->c_name, strerror(errno)); 576 confree(s); 577 return; 578 } 579 c->c_off += n; 580 581 if (c->c_off == c->c_len) 582 switch (c->c_status) { 583 case CS_SIZE: 584 c->c_plen = htonl(c->c_plen); 585 c->c_len = c->c_plen + 8 - (c->c_plen & 7); 586 c->c_off = 0; 587 c->c_data = xmalloc(c->c_len); 588 c->c_status = CS_KEYS; 589 break; 590 case CS_KEYS: 591 keyprint(c, keygrab_ssh1(c)); 592 confree(s); 593 return; 594 break; 595 default: 596 fatal("conread: invalid status %d", c->c_status); 597 break; 598 } 599 600 contouch(s); 601} 602 603static void 604conloop(void) 605{ 606 struct timeval seltime, now; 607 fd_set *r, *e; 608 con *c; 609 int i; 610 611 gettimeofday(&now, NULL); 612 c = TAILQ_FIRST(&tq); 613 614 if (c && (c->c_tv.tv_sec > now.tv_sec || 615 (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { 616 seltime = c->c_tv; 617 seltime.tv_sec -= now.tv_sec; 618 seltime.tv_usec -= now.tv_usec; 619 if (seltime.tv_usec < 0) { 620 seltime.tv_usec += 1000000; 621 seltime.tv_sec--; 622 } 623 } else 624 seltime.tv_sec = seltime.tv_usec = 0; 625 626 r = xmalloc(read_wait_size); 627 memcpy(r, read_wait, read_wait_size); 628 e = xmalloc(read_wait_size); 629 memcpy(e, read_wait, read_wait_size); 630 631 while (select(maxfd, r, NULL, e, &seltime) == -1 && 632 (errno == EAGAIN || errno == EINTR)) 633 ; 634 635 for (i = 0; i < maxfd; i++) { 636 if (FD_ISSET(i, e)) { 637 error("%s: exception!", fdcon[i].c_name); 638 confree(i); 639 } else if (FD_ISSET(i, r)) 640 conread(i); 641 } 642 xfree(r); 643 xfree(e); 644 645 c = TAILQ_FIRST(&tq); 646 while (c && (c->c_tv.tv_sec < now.tv_sec || 647 (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { 648 int s = c->c_fd; 649 650 c = TAILQ_NEXT(c, c_link); 651 conrecycle(s); 652 } 653} 654 655static void 656do_host(char *host) 657{ 658 char *name = strnnsep(&host, " \t\n"); 659 int j; 660 661 if (name == NULL) 662 return; 663 for (j = KT_RSA1; j <= KT_RSA; j *= 2) { 664 if (get_keytypes & j) { 665 while (ncon >= MAXCON) 666 conloop(); 667 conalloc(name, *host ? host : name, j); 668 } 669 } 670} 671 672void 673fatal(const char *fmt,...) 674{ 675 va_list args; 676 677 va_start(args, fmt); 678 do_log(SYSLOG_LEVEL_FATAL, fmt, args); 679 va_end(args); 680 if (nonfatal_fatal) 681 longjmp(kexjmp, -1); 682 else 683 fatal_cleanup(); 684} 685 686static void 687usage(void) 688{ 689 fprintf(stderr, "usage: %s [-v46] [-p port] [-T timeout] [-f file]\n" 690 "\t\t [host | addrlist namelist] [...]\n", 691 __progname); 692 exit(1); 693} 694 695int 696main(int argc, char **argv) 697{ 698 int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; 699 int opt, fopt_count = 0; 700 char *tname; 701 702 extern int optind; 703 extern char *optarg; 704 705 __progname = get_progname(argv[0]); 706 init_rng(); 707 seed_rng(); 708 TAILQ_INIT(&tq); 709 710 if (argc <= 1) 711 usage(); 712 713 while ((opt = getopt(argc, argv, "v46p:T:t:f:")) != -1) { 714 switch (opt) { 715 case 'p': 716 ssh_port = a2port(optarg); 717 if (ssh_port == 0) { 718 fprintf(stderr, "Bad port '%s'\n", optarg); 719 exit(1); 720 } 721 break; 722 case 'T': 723 timeout = convtime(optarg); 724 if (timeout == -1 || timeout == 0) { 725 fprintf(stderr, "Bad timeout '%s'\n", optarg); 726 usage(); 727 } 728 break; 729 case 'v': 730 if (!debug_flag) { 731 debug_flag = 1; 732 log_level = SYSLOG_LEVEL_DEBUG1; 733 } 734 else if (log_level < SYSLOG_LEVEL_DEBUG3) 735 log_level++; 736 else 737 fatal("Too high debugging level."); 738 break; 739 case 'f': 740 if (strcmp(optarg, "-") == 0) 741 optarg = NULL; 742 argv[fopt_count++] = optarg; 743 break; 744 case 't': 745 get_keytypes = 0; 746 tname = strtok(optarg, ","); 747 while (tname) { 748 int type = key_type_from_name(tname); 749 switch (type) { 750 case KEY_RSA1: 751 get_keytypes |= KT_RSA1; 752 break; 753 case KEY_DSA: 754 get_keytypes |= KT_DSA; 755 break; 756 case KEY_RSA: 757 get_keytypes |= KT_RSA; 758 break; 759 case KEY_UNSPEC: 760 fatal("unknown key type %s", tname); 761 } 762 tname = strtok(NULL, ","); 763 } 764 break; 765 case '4': 766 IPv4or6 = AF_INET; 767 break; 768 case '6': 769 IPv4or6 = AF_INET6; 770 break; 771 case '?': 772 default: 773 usage(); 774 } 775 } 776 if (optind == argc && !fopt_count) 777 usage(); 778 779 log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); 780 781 maxfd = fdlim_get(1); 782 if (maxfd < 0) 783 fatal("%s: fdlim_get: bad value", __progname); 784 if (maxfd > MAXMAXFD) 785 maxfd = MAXMAXFD; 786 if (MAXCON <= 0) 787 fatal("%s: not enough file descriptors", __progname); 788 if (maxfd > fdlim_get(0)) 789 fdlim_set(maxfd); 790 fdcon = xmalloc(maxfd * sizeof(con)); 791 memset(fdcon, 0, maxfd * sizeof(con)); 792 793 read_wait_size = howmany(maxfd, NFDBITS) * sizeof(fd_mask); 794 read_wait = xmalloc(read_wait_size); 795 memset(read_wait, 0, read_wait_size); 796 797 if (fopt_count) { 798 Linebuf *lb; 799 char *line; 800 int j; 801 802 for (j = 0; j < fopt_count; j++) { 803 lb = Linebuf_alloc(argv[j], error); 804 if (!lb) 805 continue; 806 while ((line = Linebuf_getline(lb)) != NULL) 807 do_host(line); 808 Linebuf_free(lb); 809 } 810 } 811 812 while (optind < argc) 813 do_host(argv[optind++]); 814 815 while (ncon > 0) 816 conloop(); 817 818 return (0); 819} 820