jcs's openbsd hax
openbsd
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}