jcs's openbsd hax
openbsd
at jcs 802 lines 19 kB view raw
1/* $OpenBSD: ssh-keyscan.c,v 1.167 2025/08/29 03:50:38 djm Exp $ */ 2/* 3 * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. 4 * 5 * Modification and redistribution in source and binary forms is 6 * permitted provided that due credit is given to the author and the 7 * OpenBSD project by leaving this copyright notice intact. 8 */ 9 10#include <sys/types.h> 11#include <sys/socket.h> 12#include <sys/queue.h> 13#include <sys/time.h> 14#include <sys/resource.h> 15 16#ifdef WITH_OPENSSL 17#include <openssl/bn.h> 18#endif 19 20#include <errno.h> 21#include <limits.h> 22#include <netdb.h> 23#include <stdarg.h> 24#include <stdio.h> 25#include <stdlib.h> 26#include <poll.h> 27#include <signal.h> 28#include <string.h> 29#include <unistd.h> 30 31#include "xmalloc.h" 32#include "ssh.h" 33#include "sshbuf.h" 34#include "sshkey.h" 35#include "cipher.h" 36#include "digest.h" 37#include "kex.h" 38#include "compat.h" 39#include "myproposal.h" 40#include "packet.h" 41#include "dispatch.h" 42#include "log.h" 43#include "atomicio.h" 44#include "misc.h" 45#include "hostfile.h" 46#include "ssherr.h" 47#include "ssh_api.h" 48#include "dns.h" 49#include "addr.h" 50 51/* Flag indicating whether IPv4 or IPv6. This can be set on the command line. 52 Default value is AF_UNSPEC means both IPv4 and IPv6. */ 53int IPv4or6 = AF_UNSPEC; 54 55int ssh_port = SSH_DEFAULT_PORT; 56 57#define KT_RSA (1) 58#define KT_ECDSA (1<<1) 59#define KT_ED25519 (1<<2) 60#define KT_ECDSA_SK (1<<4) 61#define KT_ED25519_SK (1<<5) 62 63#define KT_MIN KT_RSA 64#define KT_MAX KT_ED25519_SK 65 66int get_cert = 0; 67int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK; 68 69int hash_hosts = 0; /* Hash hostname on output */ 70 71int print_sshfp = 0; /* Print SSHFP records instead of known_hosts */ 72 73int found_one = 0; /* Successfully found a key */ 74 75int hashalg = -1; /* Hash for SSHFP records or -1 for all */ 76 77int quiet = 0; /* Don't print key comment lines */ 78 79#define MAXMAXFD 256 80 81/* The number of seconds after which to give up on a TCP connection */ 82int timeout = 5; 83 84int maxfd; 85#define MAXCON (maxfd - 10) 86 87extern char *__progname; 88struct pollfd *read_wait; 89int ncon; 90 91/* 92 * Keep a connection structure for each file descriptor. The state 93 * associated with file descriptor n is held in fdcon[n]. 94 */ 95typedef struct Connection { 96 u_char c_status; /* State of connection on this file desc. */ 97#define CS_UNUSED 0 /* File descriptor unused */ 98#define CS_CON 1 /* Waiting to connect/read greeting */ 99 int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ 100 int c_keytype; /* Only one of KT_* */ 101 sig_atomic_t c_done; /* SSH2 done */ 102 char *c_namebase; /* Address to free for c_name and c_namelist */ 103 char *c_name; /* Hostname of connection for errors */ 104 char *c_namelist; /* Pointer to other possible addresses */ 105 char *c_output_name; /* Hostname of connection for output */ 106 struct ssh *c_ssh; /* SSH-connection */ 107 struct timespec c_ts; /* Time at which connection gets aborted */ 108 TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ 109} con; 110 111TAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ 112con *fdcon; 113 114static void keyprint(con *c, struct sshkey *key); 115 116static int 117fdlim_get(int hard) 118{ 119 struct rlimit rlfd; 120 121 if (getrlimit(RLIMIT_NOFILE, &rlfd) == -1) 122 return (-1); 123 if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY || 124 (hard ? rlfd.rlim_max : rlfd.rlim_cur) > INT_MAX) 125 return sysconf(_SC_OPEN_MAX); 126 return hard ? rlfd.rlim_max : rlfd.rlim_cur; 127} 128 129static int 130fdlim_set(int lim) 131{ 132 struct rlimit rlfd; 133 134 if (lim <= 0) 135 return (-1); 136 if (getrlimit(RLIMIT_NOFILE, &rlfd) == -1) 137 return (-1); 138 rlfd.rlim_cur = lim; 139 if (setrlimit(RLIMIT_NOFILE, &rlfd) == -1) 140 return (-1); 141 return (0); 142} 143 144/* 145 * This is an strsep function that returns a null field for adjacent 146 * separators. This is the same as the 4.4BSD strsep, but different from the 147 * one in the GNU libc. 148 */ 149static char * 150xstrsep(char **str, const char *delim) 151{ 152 char *s, *e; 153 154 if (!**str) 155 return (NULL); 156 157 s = *str; 158 e = s + strcspn(s, delim); 159 160 if (*e != '\0') 161 *e++ = '\0'; 162 *str = e; 163 164 return (s); 165} 166 167/* 168 * Get the next non-null token (like GNU strsep). Strsep() will return a 169 * null token for two adjacent separators, so we may have to loop. 170 */ 171static char * 172strnnsep(char **stringp, char *delim) 173{ 174 char *tok; 175 176 do { 177 tok = xstrsep(stringp, delim); 178 } while (tok && *tok == '\0'); 179 return (tok); 180} 181 182 183static int 184key_print_wrapper(struct sshkey *hostkey, struct ssh *ssh) 185{ 186 con *c; 187 188 if ((c = ssh_get_app_data(ssh)) != NULL) 189 keyprint(c, hostkey); 190 /* always abort key exchange */ 191 return -1; 192} 193 194static int 195ssh2_capable(int remote_major, int remote_minor) 196{ 197 switch (remote_major) { 198 case 1: 199 if (remote_minor == 99) 200 return 1; 201 break; 202 case 2: 203 return 1; 204 default: 205 break; 206 } 207 return 0; 208} 209 210static void 211keygrab_ssh2(con *c) 212{ 213 char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; 214 int r; 215 216 switch (c->c_keytype) { 217 case KT_RSA: 218 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 219 "rsa-sha2-512-cert-v01@openssh.com," 220 "rsa-sha2-256-cert-v01@openssh.com," 221 "ssh-rsa-cert-v01@openssh.com" : 222 "rsa-sha2-512," 223 "rsa-sha2-256," 224 "ssh-rsa"; 225 break; 226 case KT_ED25519: 227 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 228 "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519"; 229 break; 230 case KT_ECDSA: 231 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 232 "ecdsa-sha2-nistp256-cert-v01@openssh.com," 233 "ecdsa-sha2-nistp384-cert-v01@openssh.com," 234 "ecdsa-sha2-nistp521-cert-v01@openssh.com" : 235 "ecdsa-sha2-nistp256," 236 "ecdsa-sha2-nistp384," 237 "ecdsa-sha2-nistp521"; 238 break; 239 case KT_ECDSA_SK: 240 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 241 "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" : 242 "sk-ecdsa-sha2-nistp256@openssh.com"; 243 break; 244 case KT_ED25519_SK: 245 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 246 "sk-ssh-ed25519-cert-v01@openssh.com" : 247 "sk-ssh-ed25519@openssh.com"; 248 break; 249 default: 250 fatal("unknown key type %d", c->c_keytype); 251 break; 252 } 253 if ((r = kex_setup(c->c_ssh, myproposal)) != 0) { 254 free(c->c_ssh); 255 fprintf(stderr, "kex_setup: %s\n", ssh_err(r)); 256 exit(1); 257 } 258#ifdef WITH_OPENSSL 259 c->c_ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client; 260 c->c_ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client; 261 c->c_ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client; 262 c->c_ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client; 263 c->c_ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client; 264 c->c_ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; 265 c->c_ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; 266 c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client; 267#endif 268 c->c_ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client; 269 c->c_ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client; 270 c->c_ssh->kex->kex[KEX_KEM_MLKEM768X25519_SHA256] = kex_gen_client; 271 ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper); 272 /* 273 * do the key-exchange until an error occurs or until 274 * the key_print_wrapper() callback sets c_done. 275 */ 276 ssh_dispatch_run(c->c_ssh, DISPATCH_BLOCK, &c->c_done); 277} 278 279static void 280keyprint_one(const char *host, struct sshkey *key) 281{ 282 char *hostport = NULL, *hashed = NULL; 283 const char *known_host; 284 int r = 0; 285 286 found_one = 1; 287 288 if (print_sshfp) { 289 export_dns_rr(host, key, stdout, 0, hashalg); 290 return; 291 } 292 293 hostport = put_host_port(host, ssh_port); 294 lowercase(hostport); 295 if (hash_hosts && (hashed = host_hash(hostport, NULL, 0)) == NULL) 296 fatal("host_hash failed"); 297 known_host = hash_hosts ? hashed : hostport; 298 if (!get_cert) 299 r = fprintf(stdout, "%s ", known_host); 300 if (r >= 0 && sshkey_write(key, stdout) == 0) 301 (void)fputs("\n", stdout); 302 free(hashed); 303 free(hostport); 304} 305 306static void 307keyprint(con *c, struct sshkey *key) 308{ 309 char *hosts = c->c_output_name ? c->c_output_name : c->c_name; 310 char *host, *ohosts; 311 312 if (key == NULL) 313 return; 314 if (get_cert || (!hash_hosts && ssh_port == SSH_DEFAULT_PORT)) { 315 keyprint_one(hosts, key); 316 return; 317 } 318 ohosts = hosts = xstrdup(hosts); 319 while ((host = strsep(&hosts, ",")) != NULL) 320 keyprint_one(host, key); 321 free(ohosts); 322} 323 324static int 325tcpconnect(char *host) 326{ 327 struct addrinfo hints, *ai, *aitop; 328 char strport[NI_MAXSERV]; 329 int gaierr, s = -1; 330 331 snprintf(strport, sizeof strport, "%d", ssh_port); 332 memset(&hints, 0, sizeof(hints)); 333 hints.ai_family = IPv4or6; 334 hints.ai_socktype = SOCK_STREAM; 335 if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { 336 error("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr)); 337 return -1; 338 } 339 for (ai = aitop; ai; ai = ai->ai_next) { 340 s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 341 if (s == -1) { 342 error("socket: %s", strerror(errno)); 343 continue; 344 } 345 if (set_nonblock(s) == -1) 346 fatal_f("set_nonblock(%d)", s); 347 if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1 && 348 errno != EINPROGRESS) 349 error("connect (`%s'): %s", host, strerror(errno)); 350 else 351 break; 352 close(s); 353 s = -1; 354 } 355 freeaddrinfo(aitop); 356 return s; 357} 358 359static int 360conalloc(const char *iname, const char *oname, int keytype) 361{ 362 char *namebase, *name, *namelist; 363 int s; 364 365 namebase = namelist = xstrdup(iname); 366 367 do { 368 name = xstrsep(&namelist, ","); 369 if (!name) { 370 free(namebase); 371 return (-1); 372 } 373 } while ((s = tcpconnect(name)) < 0); 374 375 if (s >= maxfd) 376 fatal("conalloc: fdno %d too high", s); 377 if (fdcon[s].c_status) 378 fatal("conalloc: attempt to reuse fdno %d", s); 379 380 debug3_f("oname %s kt %d", oname, keytype); 381 fdcon[s].c_fd = s; 382 fdcon[s].c_status = CS_CON; 383 fdcon[s].c_namebase = namebase; 384 fdcon[s].c_name = name; 385 fdcon[s].c_namelist = namelist; 386 fdcon[s].c_output_name = xstrdup(oname); 387 fdcon[s].c_keytype = keytype; 388 monotime_ts(&fdcon[s].c_ts); 389 fdcon[s].c_ts.tv_sec += timeout; 390 TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); 391 read_wait[s].fd = s; 392 read_wait[s].events = POLLIN; 393 ncon++; 394 return (s); 395} 396 397static void 398confree(int s) 399{ 400 if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) 401 fatal("confree: attempt to free bad fdno %d", s); 402 free(fdcon[s].c_namebase); 403 free(fdcon[s].c_output_name); 404 fdcon[s].c_status = CS_UNUSED; 405 fdcon[s].c_keytype = 0; 406 if (fdcon[s].c_ssh) { 407 ssh_packet_close(fdcon[s].c_ssh); 408 free(fdcon[s].c_ssh); 409 fdcon[s].c_ssh = NULL; 410 } else 411 close(s); 412 TAILQ_REMOVE(&tq, &fdcon[s], c_link); 413 read_wait[s].fd = -1; 414 read_wait[s].events = 0; 415 ncon--; 416} 417 418static int 419conrecycle(int s) 420{ 421 con *c = &fdcon[s]; 422 int ret; 423 424 ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); 425 confree(s); 426 return (ret); 427} 428 429static void 430congreet(int s) 431{ 432 int n = 0, remote_major = 0, remote_minor = 0; 433 char buf[256], *cp; 434 char remote_version[sizeof buf]; 435 size_t bufsiz; 436 con *c = &fdcon[s]; 437 438 /* send client banner */ 439 n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", 440 PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2); 441 if (n < 0 || (size_t)n >= sizeof(buf)) { 442 error("snprintf: buffer too small"); 443 confree(s); 444 return; 445 } 446 if (atomicio(vwrite, s, buf, n) != (size_t)n) { 447 error("write (%s): %s", c->c_name, strerror(errno)); 448 confree(s); 449 return; 450 } 451 452 /* 453 * Read the server banner as per RFC4253 section 4.2. The "SSH-" 454 * protocol identification string may be preceded by an arbitrarily 455 * large banner which we must read and ignore. Loop while reading 456 * newline-terminated lines until we have one starting with "SSH-". 457 * The ID string cannot be longer than 255 characters although the 458 * preceding banner lines may (in which case they'll be discarded 459 * in multiple iterations of the outer loop). 460 */ 461 for (;;) { 462 memset(buf, '\0', sizeof(buf)); 463 bufsiz = sizeof(buf); 464 cp = buf; 465 while (bufsiz-- && 466 (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') { 467 if (*cp == '\r') 468 *cp = '\n'; 469 cp++; 470 } 471 if (n != 1 || strncmp(buf, "SSH-", 4) == 0) 472 break; 473 } 474 if (n == 0) { 475 switch (errno) { 476 case EPIPE: 477 error("%s: Connection closed by remote host", c->c_name); 478 break; 479 case ECONNREFUSED: 480 break; 481 default: 482 error("read (%s): %s", c->c_name, strerror(errno)); 483 break; 484 } 485 conrecycle(s); 486 return; 487 } 488 if (cp >= buf + sizeof(buf)) { 489 error("%s: greeting exceeds allowable length", c->c_name); 490 confree(s); 491 return; 492 } 493 if (*cp != '\n' && *cp != '\r') { 494 error("%s: bad greeting", c->c_name); 495 confree(s); 496 return; 497 } 498 *cp = '\0'; 499 if ((c->c_ssh = ssh_packet_set_connection(NULL, s, s)) == NULL) 500 fatal("ssh_packet_set_connection failed"); 501 ssh_packet_set_timeout(c->c_ssh, timeout, 1); 502 ssh_set_app_data(c->c_ssh, c); /* back link */ 503 c->c_ssh->compat = 0; 504 if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", 505 &remote_major, &remote_minor, remote_version) == 3) 506 compat_banner(c->c_ssh, remote_version); 507 if (!ssh2_capable(remote_major, remote_minor)) { 508 debug("%s doesn't support ssh2", c->c_name); 509 confree(s); 510 return; 511 } 512 if (!quiet) { 513 fprintf(stdout, "%c %s:%d %s\n", print_sshfp ? ';' : '#', 514 c->c_name, ssh_port, chop(buf)); 515 } 516 keygrab_ssh2(c); 517 confree(s); 518} 519 520static void 521conread(int s) 522{ 523 con *c = &fdcon[s]; 524 525 if (c->c_status != CS_CON) 526 fatal("conread: invalid status %d", c->c_status); 527 528 congreet(s); 529} 530 531static void 532conloop(void) 533{ 534 struct timespec seltime, now; 535 con *c; 536 int i; 537 538 monotime_ts(&now); 539 c = TAILQ_FIRST(&tq); 540 541 if (c && timespeccmp(&c->c_ts, &now, >)) 542 timespecsub(&c->c_ts, &now, &seltime); 543 else 544 timespecclear(&seltime); 545 546 while (ppoll(read_wait, maxfd, &seltime, NULL) == -1) { 547 if (errno == EAGAIN || errno == EINTR) 548 continue; 549 error("poll error"); 550 } 551 552 for (i = 0; i < maxfd; i++) { 553 if (read_wait[i].revents & (POLLHUP|POLLERR|POLLNVAL)) 554 confree(i); 555 else if (read_wait[i].revents & (POLLIN)) 556 conread(i); 557 } 558 559 c = TAILQ_FIRST(&tq); 560 while (c && timespeccmp(&c->c_ts, &now, <)) { 561 int s = c->c_fd; 562 563 c = TAILQ_NEXT(c, c_link); 564 conrecycle(s); 565 } 566} 567 568static void 569do_one_host(char *host) 570{ 571 char *name = strnnsep(&host, " \t\n"); 572 int j; 573 574 if (name == NULL) 575 return; 576 for (j = KT_MIN; j <= KT_MAX; j *= 2) { 577 if (get_keytypes & j) { 578 while (ncon >= MAXCON) 579 conloop(); 580 conalloc(name, *host ? host : name, j); 581 } 582 } 583} 584 585static void 586do_host(char *host) 587{ 588 char daddr[128]; 589 struct xaddr addr, end_addr; 590 u_int masklen; 591 592 if (host == NULL) 593 return; 594 if (addr_pton_cidr(host, &addr, &masklen) != 0) { 595 /* Assume argument is a hostname */ 596 do_one_host(host); 597 } else { 598 /* Argument is a CIDR range */ 599 debug("CIDR range %s", host); 600 end_addr = addr; 601 if (addr_host_to_all1s(&end_addr, masklen) != 0) 602 goto badaddr; 603 /* 604 * Note: we deliberately include the all-zero/ones addresses. 605 */ 606 for (;;) { 607 if (addr_ntop(&addr, daddr, sizeof(daddr)) != 0) { 608 badaddr: 609 error("Invalid address %s", host); 610 return; 611 } 612 debug("CIDR expand: address %s", daddr); 613 do_one_host(daddr); 614 if (addr_cmp(&addr, &end_addr) == 0) 615 break; 616 addr_increment(&addr); 617 } 618 } 619} 620 621static void 622usage(void) 623{ 624 fprintf(stderr, 625 "usage: ssh-keyscan [-46cDHqv] [-f file] [-O option] [-p port] [-T timeout]\n" 626 " [-t type] [host | addrlist namelist]\n"); 627 exit(1); 628} 629 630int 631main(int argc, char **argv) 632{ 633 int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; 634 int opt, fopt_count = 0, j; 635 char *tname, *cp, *line = NULL; 636 size_t linesize = 0; 637 FILE *fp; 638 639 extern int optind; 640 extern char *optarg; 641 642 TAILQ_INIT(&tq); 643 644 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 645 sanitise_stdfd(); 646 647 if (argc <= 1) 648 usage(); 649 650 while ((opt = getopt(argc, argv, "cDHqv46O:p:T:t:f:")) != -1) { 651 switch (opt) { 652 case 'H': 653 hash_hosts = 1; 654 break; 655 case 'c': 656 get_cert = 1; 657 break; 658 case 'D': 659 print_sshfp = 1; 660 break; 661 case 'p': 662 ssh_port = a2port(optarg); 663 if (ssh_port <= 0) { 664 fprintf(stderr, "Bad port '%s'\n", optarg); 665 exit(1); 666 } 667 break; 668 case 'T': 669 timeout = convtime(optarg); 670 if (timeout == -1 || timeout == 0) { 671 fprintf(stderr, "Bad timeout '%s'\n", optarg); 672 usage(); 673 } 674 break; 675 case 'v': 676 if (!debug_flag) { 677 debug_flag = 1; 678 log_level = SYSLOG_LEVEL_DEBUG1; 679 } 680 else if (log_level < SYSLOG_LEVEL_DEBUG3) 681 log_level++; 682 else 683 fatal("Too high debugging level."); 684 break; 685 case 'q': 686 quiet = 1; 687 break; 688 case 'f': 689 if (strcmp(optarg, "-") == 0) 690 optarg = NULL; 691 argv[fopt_count++] = optarg; 692 break; 693 case 'O': 694 /* Maybe other misc options in the future too */ 695 if (strncmp(optarg, "hashalg=", 8) != 0) 696 fatal("Unsupported -O option"); 697 if ((hashalg = ssh_digest_alg_by_name( 698 optarg + 8)) == -1) 699 fatal("Unsupported hash algorithm"); 700 break; 701 case 't': 702 get_keytypes = 0; 703 tname = strtok(optarg, ","); 704 while (tname) { 705 int type = sshkey_type_from_shortname(tname); 706 707 switch (type) { 708 case KEY_ECDSA: 709 get_keytypes |= KT_ECDSA; 710 break; 711 case KEY_RSA: 712 get_keytypes |= KT_RSA; 713 break; 714 case KEY_ED25519: 715 get_keytypes |= KT_ED25519; 716 break; 717 case KEY_ED25519_SK: 718 get_keytypes |= KT_ED25519_SK; 719 break; 720 case KEY_ECDSA_SK: 721 get_keytypes |= KT_ECDSA_SK; 722 break; 723 case KEY_UNSPEC: 724 default: 725 fatal("Unknown key type \"%s\"", tname); 726 } 727 tname = strtok(NULL, ","); 728 } 729 break; 730 case '4': 731 IPv4or6 = AF_INET; 732 break; 733 case '6': 734 IPv4or6 = AF_INET6; 735 break; 736 default: 737 usage(); 738 } 739 } 740 if (optind == argc && !fopt_count) 741 usage(); 742 743 log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); 744 745 maxfd = fdlim_get(1); 746 if (maxfd < 0) 747 fatal("%s: fdlim_get: bad value", __progname); 748 if (maxfd > MAXMAXFD) 749 maxfd = MAXMAXFD; 750 if (MAXCON <= 0) 751 fatal("%s: not enough file descriptors", __progname); 752 if (maxfd > fdlim_get(0)) 753 fdlim_set(maxfd); 754 fdcon = xcalloc(maxfd, sizeof(con)); 755 read_wait = xcalloc(maxfd, sizeof(struct pollfd)); 756 for (j = 0; j < maxfd; j++) 757 read_wait[j].fd = -1; 758 759 ssh_signal(SIGPIPE, SIG_IGN); 760 for (j = 0; j < fopt_count; j++) { 761 if (argv[j] == NULL) 762 fp = stdin; 763 else if ((fp = fopen(argv[j], "r")) == NULL) 764 fatal("%s: %s: %s", __progname, 765 fp == stdin ? "<stdin>" : argv[j], strerror(errno)); 766 767 while (getline(&line, &linesize, fp) != -1) { 768 /* Chomp off trailing whitespace and comments */ 769 if ((cp = strchr(line, '#')) == NULL) 770 cp = line + strlen(line) - 1; 771 while (cp >= line) { 772 if (*cp == ' ' || *cp == '\t' || 773 *cp == '\n' || *cp == '#') 774 *cp-- = '\0'; 775 else 776 break; 777 } 778 779 /* Skip empty lines */ 780 if (*line == '\0') 781 continue; 782 783 do_host(line); 784 } 785 786 if (ferror(fp)) 787 fatal("%s: %s: %s", __progname, 788 fp == stdin ? "<stdin>" : argv[j], strerror(errno)); 789 790 if (fp != stdin) 791 fclose(fp); 792 } 793 free(line); 794 795 while (optind < argc) 796 do_host(argv[optind++]); 797 798 while (ncon > 0) 799 conloop(); 800 801 return found_one ? 0 : 1; 802}