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