xref: /openssh-portable/ssh-keyscan.c (revision 4ca6a1fa)
1*4ca6a1faSdjm@openbsd.org /* $OpenBSD: ssh-keyscan.c,v 1.139 2021/01/27 09:26:54 djm Exp $ */
2b6434ae0SBen Lindstrom /*
3b6434ae0SBen Lindstrom  * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
4b6434ae0SBen Lindstrom  *
5b6434ae0SBen Lindstrom  * Modification and redistribution in source and binary forms is
6b6434ae0SBen Lindstrom  * permitted provided that due credit is given to the author and the
7a238f6e8SBen Lindstrom  * OpenBSD project by leaving this copyright notice intact.
8b6434ae0SBen Lindstrom  */
9b6434ae0SBen Lindstrom 
10b6434ae0SBen Lindstrom #include "includes.h"
11b6434ae0SBen Lindstrom 
1248b68ce1Sdjm@openbsd.org #include <sys/types.h>
139b481510SDamien Miller #include "openbsd-compat/sys-queue.h"
14cd4223c2SDamien Miller #include <sys/resource.h>
159aec9194SDamien Miller #ifdef HAVE_SYS_TIME_H
169aec9194SDamien Miller # include <sys/time.h>
179aec9194SDamien Miller #endif
18e3476ed0SDamien Miller 
1946aa3e0cSDarren Tucker #include <netinet/in.h>
2046aa3e0cSDarren Tucker #include <arpa/inet.h>
2146aa3e0cSDarren Tucker 
22670104b9Sdjm@openbsd.org #ifdef WITH_OPENSSL
23e3476ed0SDamien Miller #include <openssl/bn.h>
24670104b9Sdjm@openbsd.org #endif
25e3476ed0SDamien Miller 
26be43ebf9SDamien Miller #include <netdb.h>
27deecec98SDarren Tucker #include <errno.h>
28e7a1e5cfSDamien Miller #include <stdarg.h>
29a7a73ee3SDamien Miller #include <stdio.h>
30e7a1e5cfSDamien Miller #include <stdlib.h>
31d7834353SDamien Miller #include <signal.h>
32e3476ed0SDamien Miller #include <string.h>
33e6b3b610SDamien Miller #include <unistd.h>
34b6434ae0SBen Lindstrom 
35b6434ae0SBen Lindstrom #include "xmalloc.h"
36b6434ae0SBen Lindstrom #include "ssh.h"
373f797653Smarkus@openbsd.org #include "sshbuf.h"
383f797653Smarkus@openbsd.org #include "sshkey.h"
39d7834353SDamien Miller #include "cipher.h"
40325e70c9SBen Lindstrom #include "kex.h"
41325e70c9SBen Lindstrom #include "compat.h"
42325e70c9SBen Lindstrom #include "myproposal.h"
43325e70c9SBen Lindstrom #include "packet.h"
44325e70c9SBen Lindstrom #include "dispatch.h"
45226cfa03SBen Lindstrom #include "log.h"
46d20b855bSBen Lindstrom #include "atomicio.h"
47325e70c9SBen Lindstrom #include "misc.h"
48db7b8171SDamien Miller #include "hostfile.h"
493f797653Smarkus@openbsd.org #include "ssherr.h"
503f797653Smarkus@openbsd.org #include "ssh_api.h"
511a348359Sdjm@openbsd.org #include "dns.h"
52b6434ae0SBen Lindstrom 
53325e70c9SBen Lindstrom /* Flag indicating whether IPv4 or IPv6.  This can be set on the command line.
54325e70c9SBen Lindstrom    Default value is AF_UNSPEC means both IPv4 and IPv6. */
55325e70c9SBen Lindstrom int IPv4or6 = AF_UNSPEC;
56b6434ae0SBen Lindstrom 
57325e70c9SBen Lindstrom int ssh_port = SSH_DEFAULT_PORT;
58325e70c9SBen Lindstrom 
59873d3e7dSdjm@openbsd.org #define KT_DSA		(1)
60873d3e7dSdjm@openbsd.org #define KT_RSA		(1<<1)
61873d3e7dSdjm@openbsd.org #define KT_ECDSA	(1<<2)
62873d3e7dSdjm@openbsd.org #define KT_ED25519	(1<<3)
631b11ea7cSmarkus@openbsd.org #define KT_XMSS		(1<<4)
649b6e30b9Sdjm@openbsd.org #define KT_ECDSA_SK	(1<<5)
659b6e30b9Sdjm@openbsd.org #define KT_ED25519_SK	(1<<6)
66873d3e7dSdjm@openbsd.org 
67873d3e7dSdjm@openbsd.org #define KT_MIN		KT_DSA
689b6e30b9Sdjm@openbsd.org #define KT_MAX		KT_ED25519_SK
69325e70c9SBen Lindstrom 
703a424cddSdjm@openbsd.org int get_cert = 0;
719b6e30b9Sdjm@openbsd.org int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK;
72b6434ae0SBen Lindstrom 
73db7b8171SDamien Miller int hash_hosts = 0;		/* Hash hostname on output */
74db7b8171SDamien Miller 
751a348359Sdjm@openbsd.org int print_sshfp = 0;		/* Print SSHFP records instead of known_hosts */
761a348359Sdjm@openbsd.org 
77c2c18a39Sdjm@openbsd.org int found_one = 0;		/* Successfully found a key */
78c2c18a39Sdjm@openbsd.org 
79b6434ae0SBen Lindstrom #define MAXMAXFD 256
80b6434ae0SBen Lindstrom 
81b6434ae0SBen Lindstrom /* The number of seconds after which to give up on a TCP connection */
82b6434ae0SBen Lindstrom int timeout = 5;
83b6434ae0SBen Lindstrom 
84b6434ae0SBen Lindstrom int maxfd;
85d20b855bSBen Lindstrom #define MAXCON (maxfd - 10)
86b6434ae0SBen Lindstrom 
87ec84dc12SKevin Steves extern char *__progname;
88c1e0421cSBen Lindstrom fd_set *read_wait;
8907d86becSDamien Miller size_t read_wait_nfdset;
90b6434ae0SBen Lindstrom int ncon;
91b6434ae0SBen Lindstrom 
92b6434ae0SBen Lindstrom /*
93b6434ae0SBen Lindstrom  * Keep a connection structure for each file descriptor.  The state
94b6434ae0SBen Lindstrom  * associated with file descriptor n is held in fdcon[n].
95b6434ae0SBen Lindstrom  */
96b6434ae0SBen Lindstrom typedef struct Connection {
9746c16220SBen Lindstrom 	u_char c_status;	/* State of connection on this file desc. */
98b6434ae0SBen Lindstrom #define CS_UNUSED 0		/* File descriptor unused */
99b6434ae0SBen Lindstrom #define CS_CON 1		/* Waiting to connect/read greeting */
100b6434ae0SBen Lindstrom #define CS_SIZE 2		/* Waiting to read initial packet size */
101b6434ae0SBen Lindstrom #define CS_KEYS 3		/* Waiting to read public key packet */
102b6434ae0SBen Lindstrom 	int c_fd;		/* Quick lookup: c->c_fd == c - fdcon */
103b6434ae0SBen Lindstrom 	int c_plen;		/* Packet length field for ssh packet */
104b6434ae0SBen Lindstrom 	int c_len;		/* Total bytes which must be read. */
105b6434ae0SBen Lindstrom 	int c_off;		/* Length of data read so far. */
106873d3e7dSdjm@openbsd.org 	int c_keytype;		/* Only one of KT_* */
107c265e2e6Smiod@openbsd.org 	sig_atomic_t c_done;	/* SSH2 done */
108b6434ae0SBen Lindstrom 	char *c_namebase;	/* Address to free for c_name and c_namelist */
109b6434ae0SBen Lindstrom 	char *c_name;		/* Hostname of connection for errors */
110b6434ae0SBen Lindstrom 	char *c_namelist;	/* Pointer to other possible addresses */
111b6434ae0SBen Lindstrom 	char *c_output_name;	/* Hostname of connection for output */
112b6434ae0SBen Lindstrom 	char *c_data;		/* Data read from this fd */
1133f797653Smarkus@openbsd.org 	struct ssh *c_ssh;	/* SSH-connection */
114b6434ae0SBen Lindstrom 	struct timeval c_tv;	/* Time at which connection gets aborted */
115b6434ae0SBen Lindstrom 	TAILQ_ENTRY(Connection) c_link;	/* List of connections in timeout order. */
116b6434ae0SBen Lindstrom } con;
117b6434ae0SBen Lindstrom 
118b6434ae0SBen Lindstrom TAILQ_HEAD(conlist, Connection) tq;	/* Timeout Queue */
119b6434ae0SBen Lindstrom con *fdcon;
120b6434ae0SBen Lindstrom 
1213f797653Smarkus@openbsd.org static void keyprint(con *c, struct sshkey *key);
1223f797653Smarkus@openbsd.org 
123bba81213SBen Lindstrom static int
fdlim_get(int hard)124b6434ae0SBen Lindstrom fdlim_get(int hard)
125b6434ae0SBen Lindstrom {
1265adbad22SBen Lindstrom #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
127b6434ae0SBen Lindstrom 	struct rlimit rlfd;
128b0a4cd8fSBen Lindstrom 
1294d28fa78Sderaadt@openbsd.org 	if (getrlimit(RLIMIT_NOFILE, &rlfd) == -1)
130b6434ae0SBen Lindstrom 		return (-1);
131b6434ae0SBen Lindstrom 	if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY)
132e00074a7SDamien Miller 		return SSH_SYSFDMAX;
133b6434ae0SBen Lindstrom 	else
134b6434ae0SBen Lindstrom 		return hard ? rlfd.rlim_max : rlfd.rlim_cur;
1352c467a20SBen Lindstrom #else
136e00074a7SDamien Miller 	return SSH_SYSFDMAX;
1372c467a20SBen Lindstrom #endif
138b6434ae0SBen Lindstrom }
139b6434ae0SBen Lindstrom 
140bba81213SBen Lindstrom static int
fdlim_set(int lim)141b6434ae0SBen Lindstrom fdlim_set(int lim)
142b6434ae0SBen Lindstrom {
1435adbad22SBen Lindstrom #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
144b6434ae0SBen Lindstrom 	struct rlimit rlfd;
1452c467a20SBen Lindstrom #endif
1465c98db50SBen Lindstrom 
147b6434ae0SBen Lindstrom 	if (lim <= 0)
148b6434ae0SBen Lindstrom 		return (-1);
1495adbad22SBen Lindstrom #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
1504d28fa78Sderaadt@openbsd.org 	if (getrlimit(RLIMIT_NOFILE, &rlfd) == -1)
151b6434ae0SBen Lindstrom 		return (-1);
152b6434ae0SBen Lindstrom 	rlfd.rlim_cur = lim;
1534d28fa78Sderaadt@openbsd.org 	if (setrlimit(RLIMIT_NOFILE, &rlfd) == -1)
154b6434ae0SBen Lindstrom 		return (-1);
1552c467a20SBen Lindstrom #elif defined (HAVE_SETDTABLESIZE)
1562c467a20SBen Lindstrom 	setdtablesize(lim);
1572c467a20SBen Lindstrom #endif
158b6434ae0SBen Lindstrom 	return (0);
159b6434ae0SBen Lindstrom }
160b6434ae0SBen Lindstrom 
161b6434ae0SBen Lindstrom /*
162b6434ae0SBen Lindstrom  * This is an strsep function that returns a null field for adjacent
163b6434ae0SBen Lindstrom  * separators.  This is the same as the 4.4BSD strsep, but different from the
164b6434ae0SBen Lindstrom  * one in the GNU libc.
165b6434ae0SBen Lindstrom  */
166bba81213SBen Lindstrom static char *
xstrsep(char ** str,const char * delim)167b6434ae0SBen Lindstrom xstrsep(char **str, const char *delim)
168b6434ae0SBen Lindstrom {
169b6434ae0SBen Lindstrom 	char *s, *e;
170b6434ae0SBen Lindstrom 
171b6434ae0SBen Lindstrom 	if (!**str)
172b6434ae0SBen Lindstrom 		return (NULL);
173b6434ae0SBen Lindstrom 
174b6434ae0SBen Lindstrom 	s = *str;
175b6434ae0SBen Lindstrom 	e = s + strcspn(s, delim);
176b6434ae0SBen Lindstrom 
177b6434ae0SBen Lindstrom 	if (*e != '\0')
178b6434ae0SBen Lindstrom 		*e++ = '\0';
179b6434ae0SBen Lindstrom 	*str = e;
180b6434ae0SBen Lindstrom 
181b6434ae0SBen Lindstrom 	return (s);
182b6434ae0SBen Lindstrom }
183b6434ae0SBen Lindstrom 
184b6434ae0SBen Lindstrom /*
185b6434ae0SBen Lindstrom  * Get the next non-null token (like GNU strsep).  Strsep() will return a
186b6434ae0SBen Lindstrom  * null token for two adjacent separators, so we may have to loop.
187b6434ae0SBen Lindstrom  */
188bba81213SBen Lindstrom static char *
strnnsep(char ** stringp,char * delim)189b6434ae0SBen Lindstrom strnnsep(char **stringp, char *delim)
190b6434ae0SBen Lindstrom {
191b6434ae0SBen Lindstrom 	char *tok;
192b6434ae0SBen Lindstrom 
193b6434ae0SBen Lindstrom 	do {
194b6434ae0SBen Lindstrom 		tok = xstrsep(stringp, delim);
195b6434ae0SBen Lindstrom 	} while (tok && *tok == '\0');
196b6434ae0SBen Lindstrom 	return (tok);
197b6434ae0SBen Lindstrom }
198b6434ae0SBen Lindstrom 
199325e70c9SBen Lindstrom 
200325e70c9SBen Lindstrom static int
key_print_wrapper(struct sshkey * hostkey,struct ssh * ssh)2013f797653Smarkus@openbsd.org key_print_wrapper(struct sshkey *hostkey, struct ssh *ssh)
202325e70c9SBen Lindstrom {
2033f797653Smarkus@openbsd.org 	con *c;
2043f797653Smarkus@openbsd.org 
2053f797653Smarkus@openbsd.org 	if ((c = ssh_get_app_data(ssh)) != NULL)
2063f797653Smarkus@openbsd.org 		keyprint(c, hostkey);
2073f797653Smarkus@openbsd.org 	/* always abort key exchange */
2083f797653Smarkus@openbsd.org 	return -1;
209325e70c9SBen Lindstrom }
210325e70c9SBen Lindstrom 
211325e70c9SBen Lindstrom static int
ssh2_capable(int remote_major,int remote_minor)212325e70c9SBen Lindstrom ssh2_capable(int remote_major, int remote_minor)
213325e70c9SBen Lindstrom {
214325e70c9SBen Lindstrom 	switch (remote_major) {
215325e70c9SBen Lindstrom 	case 1:
216325e70c9SBen Lindstrom 		if (remote_minor == 99)
217325e70c9SBen Lindstrom 			return 1;
218325e70c9SBen Lindstrom 		break;
219325e70c9SBen Lindstrom 	case 2:
220325e70c9SBen Lindstrom 		return 1;
221325e70c9SBen Lindstrom 	default:
222325e70c9SBen Lindstrom 		break;
223325e70c9SBen Lindstrom 	}
224325e70c9SBen Lindstrom 	return 0;
225325e70c9SBen Lindstrom }
226325e70c9SBen Lindstrom 
2273f797653Smarkus@openbsd.org static void
keygrab_ssh2(con * c)228325e70c9SBen Lindstrom keygrab_ssh2(con *c)
229325e70c9SBen Lindstrom {
2309235a030SDamien Miller 	char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT };
2313f797653Smarkus@openbsd.org 	int r;
232325e70c9SBen Lindstrom 
2333a424cddSdjm@openbsd.org 	switch (c->c_keytype) {
2343a424cddSdjm@openbsd.org 	case KT_DSA:
2353a424cddSdjm@openbsd.org 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
2363a424cddSdjm@openbsd.org 		    "ssh-dss-cert-v01@openssh.com" : "ssh-dss";
2373a424cddSdjm@openbsd.org 		break;
2383a424cddSdjm@openbsd.org 	case KT_RSA:
2393a424cddSdjm@openbsd.org 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
2407250879cSdjm@openbsd.org 		    "rsa-sha2-512-cert-v01@openssh.com,"
2417250879cSdjm@openbsd.org 		    "rsa-sha2-256-cert-v01@openssh.com,"
2427250879cSdjm@openbsd.org 		    "ssh-rsa-cert-v01@openssh.com" :
2437250879cSdjm@openbsd.org 		    "rsa-sha2-512,"
2447250879cSdjm@openbsd.org 		    "rsa-sha2-256,"
2457250879cSdjm@openbsd.org 		    "ssh-rsa";
2463a424cddSdjm@openbsd.org 		break;
2473a424cddSdjm@openbsd.org 	case KT_ED25519:
2483a424cddSdjm@openbsd.org 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
2493a424cddSdjm@openbsd.org 		    "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519";
2503a424cddSdjm@openbsd.org 		break;
2511b11ea7cSmarkus@openbsd.org 	case KT_XMSS:
2521b11ea7cSmarkus@openbsd.org 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
2531b11ea7cSmarkus@openbsd.org 		    "ssh-xmss-cert-v01@openssh.com" : "ssh-xmss@openssh.com";
2541b11ea7cSmarkus@openbsd.org 		break;
2553a424cddSdjm@openbsd.org 	case KT_ECDSA:
2563a424cddSdjm@openbsd.org 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
2573a424cddSdjm@openbsd.org 		    "ecdsa-sha2-nistp256-cert-v01@openssh.com,"
2583a424cddSdjm@openbsd.org 		    "ecdsa-sha2-nistp384-cert-v01@openssh.com,"
2593a424cddSdjm@openbsd.org 		    "ecdsa-sha2-nistp521-cert-v01@openssh.com" :
2603a424cddSdjm@openbsd.org 		    "ecdsa-sha2-nistp256,"
2613a424cddSdjm@openbsd.org 		    "ecdsa-sha2-nistp384,"
2623a424cddSdjm@openbsd.org 		    "ecdsa-sha2-nistp521";
2633a424cddSdjm@openbsd.org 		break;
2649b6e30b9Sdjm@openbsd.org 	case KT_ECDSA_SK:
2659b6e30b9Sdjm@openbsd.org 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
2669b6e30b9Sdjm@openbsd.org 		    "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" :
2679b6e30b9Sdjm@openbsd.org 		    "sk-ecdsa-sha2-nistp256@openssh.com";
2689b6e30b9Sdjm@openbsd.org 		break;
2699b6e30b9Sdjm@openbsd.org 	case KT_ED25519_SK:
2709b6e30b9Sdjm@openbsd.org 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
2719b6e30b9Sdjm@openbsd.org 		    "sk-ssh-ed25519-cert-v01@openssh.com" :
2729b6e30b9Sdjm@openbsd.org 		    "sk-ssh-ed25519@openssh.com";
2739b6e30b9Sdjm@openbsd.org 		break;
2743a424cddSdjm@openbsd.org 	default:
2753a424cddSdjm@openbsd.org 		fatal("unknown key type %d", c->c_keytype);
2763a424cddSdjm@openbsd.org 		break;
2773a424cddSdjm@openbsd.org 	}
2783f797653Smarkus@openbsd.org 	if ((r = kex_setup(c->c_ssh, myproposal)) != 0) {
2793f797653Smarkus@openbsd.org 		free(c->c_ssh);
2803f797653Smarkus@openbsd.org 		fprintf(stderr, "kex_setup: %s\n", ssh_err(r));
281325e70c9SBen Lindstrom 		exit(1);
282325e70c9SBen Lindstrom 	}
2833f797653Smarkus@openbsd.org #ifdef WITH_OPENSSL
284aaca72d6Sdjm@openbsd.org 	c->c_ssh->kex->kex[KEX_DH_GRP1_SHA1] = kex_gen_client;
285aaca72d6Sdjm@openbsd.org 	c->c_ssh->kex->kex[KEX_DH_GRP14_SHA1] = kex_gen_client;
286aaca72d6Sdjm@openbsd.org 	c->c_ssh->kex->kex[KEX_DH_GRP14_SHA256] = kex_gen_client;
287aaca72d6Sdjm@openbsd.org 	c->c_ssh->kex->kex[KEX_DH_GRP16_SHA512] = kex_gen_client;
288aaca72d6Sdjm@openbsd.org 	c->c_ssh->kex->kex[KEX_DH_GRP18_SHA512] = kex_gen_client;
2893f797653Smarkus@openbsd.org 	c->c_ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client;
2903f797653Smarkus@openbsd.org 	c->c_ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
291f2004cd1SDarren Tucker # ifdef OPENSSL_HAS_ECC
292aaca72d6Sdjm@openbsd.org 	c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
2933f797653Smarkus@openbsd.org # endif
294f2004cd1SDarren Tucker #endif
295aaca72d6Sdjm@openbsd.org 	c->c_ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
2962c71cec0Sdjm@openbsd.org 	c->c_ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
2973f797653Smarkus@openbsd.org 	ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper);
2983f797653Smarkus@openbsd.org 	/*
2993f797653Smarkus@openbsd.org 	 * do the key-exchange until an error occurs or until
3003f797653Smarkus@openbsd.org 	 * the key_print_wrapper() callback sets c_done.
3013f797653Smarkus@openbsd.org 	 */
30292e9fe63Smarkus@openbsd.org 	ssh_dispatch_run(c->c_ssh, DISPATCH_BLOCK, &c->c_done);
303325e70c9SBen Lindstrom }
304325e70c9SBen Lindstrom 
305325e70c9SBen Lindstrom static void
keyprint_one(const char * host,struct sshkey * key)3068a283445Sdjm@openbsd.org keyprint_one(const char *host, struct sshkey *key)
307325e70c9SBen Lindstrom {
3089ada37d3Sdjm@openbsd.org 	char *hostport;
3098a283445Sdjm@openbsd.org 	const char *known_host, *hashed;
310325e70c9SBen Lindstrom 
311c2c18a39Sdjm@openbsd.org 	found_one = 1;
312c2c18a39Sdjm@openbsd.org 
3131a348359Sdjm@openbsd.org 	if (print_sshfp) {
3141a348359Sdjm@openbsd.org 		export_dns_rr(host, key, stdout, 0);
3151a348359Sdjm@openbsd.org 		return;
3161a348359Sdjm@openbsd.org 	}
3171a348359Sdjm@openbsd.org 
3182c2cfe1aSdjm@openbsd.org 	hostport = put_host_port(host, ssh_port);
319db259720Sdjm@openbsd.org 	lowercase(hostport);
3208a283445Sdjm@openbsd.org 	if (hash_hosts && (hashed = host_hash(host, NULL, 0)) == NULL)
3218a283445Sdjm@openbsd.org 		fatal("host_hash failed");
3228a283445Sdjm@openbsd.org 	known_host = hash_hosts ? hashed : hostport;
3233a424cddSdjm@openbsd.org 	if (!get_cert)
3248a283445Sdjm@openbsd.org 		fprintf(stdout, "%s ", known_host);
3253f797653Smarkus@openbsd.org 	sshkey_write(key, stdout);
326b6434ae0SBen Lindstrom 	fputs("\n", stdout);
3272c2cfe1aSdjm@openbsd.org 	free(hostport);
328b6434ae0SBen Lindstrom }
329b6434ae0SBen Lindstrom 
3309ada37d3Sdjm@openbsd.org static void
keyprint(con * c,struct sshkey * key)3319ada37d3Sdjm@openbsd.org keyprint(con *c, struct sshkey *key)
3329ada37d3Sdjm@openbsd.org {
3339ada37d3Sdjm@openbsd.org 	char *hosts = c->c_output_name ? c->c_output_name : c->c_name;
3349ada37d3Sdjm@openbsd.org 	char *host, *ohosts;
3359ada37d3Sdjm@openbsd.org 
3369ada37d3Sdjm@openbsd.org 	if (key == NULL)
3379ada37d3Sdjm@openbsd.org 		return;
3383a424cddSdjm@openbsd.org 	if (get_cert || (!hash_hosts && ssh_port == SSH_DEFAULT_PORT)) {
3399ada37d3Sdjm@openbsd.org 		keyprint_one(hosts, key);
3409ada37d3Sdjm@openbsd.org 		return;
3419ada37d3Sdjm@openbsd.org 	}
3429ada37d3Sdjm@openbsd.org 	ohosts = hosts = xstrdup(hosts);
3439ada37d3Sdjm@openbsd.org 	while ((host = strsep(&hosts, ",")) != NULL)
3449ada37d3Sdjm@openbsd.org 		keyprint_one(host, key);
3459ada37d3Sdjm@openbsd.org 	free(ohosts);
3469ada37d3Sdjm@openbsd.org }
3479ada37d3Sdjm@openbsd.org 
348bba81213SBen Lindstrom static int
tcpconnect(char * host)349b6434ae0SBen Lindstrom tcpconnect(char *host)
350b6434ae0SBen Lindstrom {
351b6434ae0SBen Lindstrom 	struct addrinfo hints, *ai, *aitop;
352b6434ae0SBen Lindstrom 	char strport[NI_MAXSERV];
353b6434ae0SBen Lindstrom 	int gaierr, s = -1;
354b6434ae0SBen Lindstrom 
355325e70c9SBen Lindstrom 	snprintf(strport, sizeof strport, "%d", ssh_port);
356b6434ae0SBen Lindstrom 	memset(&hints, 0, sizeof(hints));
357325e70c9SBen Lindstrom 	hints.ai_family = IPv4or6;
358b6434ae0SBen Lindstrom 	hints.ai_socktype = SOCK_STREAM;
359fae7bbe5Sdjm@openbsd.org 	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
360fae7bbe5Sdjm@openbsd.org 		error("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr));
361fae7bbe5Sdjm@openbsd.org 		return -1;
362fae7bbe5Sdjm@openbsd.org 	}
363b6434ae0SBen Lindstrom 	for (ai = aitop; ai; ai = ai->ai_next) {
3647bd98e7fSDarren Tucker 		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
3654d28fa78Sderaadt@openbsd.org 		if (s == -1) {
366b6434ae0SBen Lindstrom 			error("socket: %s", strerror(errno));
367b6434ae0SBen Lindstrom 			continue;
368b6434ae0SBen Lindstrom 		}
369232711f6SDamien Miller 		if (set_nonblock(s) == -1)
370816036f1Sdjm@openbsd.org 			fatal_f("set_nonblock(%d)", s);
3714d28fa78Sderaadt@openbsd.org 		if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1 &&
372b6434ae0SBen Lindstrom 		    errno != EINPROGRESS)
373b6434ae0SBen Lindstrom 			error("connect (`%s'): %s", host, strerror(errno));
374b6434ae0SBen Lindstrom 		else
375b6434ae0SBen Lindstrom 			break;
376b6434ae0SBen Lindstrom 		close(s);
377b6434ae0SBen Lindstrom 		s = -1;
378b6434ae0SBen Lindstrom 	}
379b6434ae0SBen Lindstrom 	freeaddrinfo(aitop);
380b6434ae0SBen Lindstrom 	return s;
381b6434ae0SBen Lindstrom }
382b6434ae0SBen Lindstrom 
383bba81213SBen Lindstrom static int
conalloc(char * iname,char * oname,int keytype)384325e70c9SBen Lindstrom conalloc(char *iname, char *oname, int keytype)
385b6434ae0SBen Lindstrom {
386b6434ae0SBen Lindstrom 	char *namebase, *name, *namelist;
387965710f6SBen Lindstrom 	int s;
388b6434ae0SBen Lindstrom 
389b6434ae0SBen Lindstrom 	namebase = namelist = xstrdup(iname);
390b6434ae0SBen Lindstrom 
391b6434ae0SBen Lindstrom 	do {
392b6434ae0SBen Lindstrom 		name = xstrsep(&namelist, ",");
393b6434ae0SBen Lindstrom 		if (!name) {
394a627d42eSDarren Tucker 			free(namebase);
395b6434ae0SBen Lindstrom 			return (-1);
396b6434ae0SBen Lindstrom 		}
397b6434ae0SBen Lindstrom 	} while ((s = tcpconnect(name)) < 0);
398b6434ae0SBen Lindstrom 
399b6434ae0SBen Lindstrom 	if (s >= maxfd)
400fa72ddacSKevin Steves 		fatal("conalloc: fdno %d too high", s);
401b6434ae0SBen Lindstrom 	if (fdcon[s].c_status)
402fa72ddacSKevin Steves 		fatal("conalloc: attempt to reuse fdno %d", s);
403b6434ae0SBen Lindstrom 
404816036f1Sdjm@openbsd.org 	debug3_f("oname %s kt %d", oname, keytype);
405b6434ae0SBen Lindstrom 	fdcon[s].c_fd = s;
406b6434ae0SBen Lindstrom 	fdcon[s].c_status = CS_CON;
407b6434ae0SBen Lindstrom 	fdcon[s].c_namebase = namebase;
408b6434ae0SBen Lindstrom 	fdcon[s].c_name = name;
409b6434ae0SBen Lindstrom 	fdcon[s].c_namelist = namelist;
410b6434ae0SBen Lindstrom 	fdcon[s].c_output_name = xstrdup(oname);
411b6434ae0SBen Lindstrom 	fdcon[s].c_data = (char *) &fdcon[s].c_plen;
412b6434ae0SBen Lindstrom 	fdcon[s].c_len = 4;
413b6434ae0SBen Lindstrom 	fdcon[s].c_off = 0;
414325e70c9SBen Lindstrom 	fdcon[s].c_keytype = keytype;
4155db6fbf1Sdtucker@openbsd.org@openbsd.org 	monotime_tv(&fdcon[s].c_tv);
416b6434ae0SBen Lindstrom 	fdcon[s].c_tv.tv_sec += timeout;
417b6434ae0SBen Lindstrom 	TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
418c1e0421cSBen Lindstrom 	FD_SET(s, read_wait);
419b6434ae0SBen Lindstrom 	ncon++;
420b6434ae0SBen Lindstrom 	return (s);
421b6434ae0SBen Lindstrom }
422b6434ae0SBen Lindstrom 
423bba81213SBen Lindstrom static void
confree(int s)424b6434ae0SBen Lindstrom confree(int s)
425b6434ae0SBen Lindstrom {
426b6434ae0SBen Lindstrom 	if (s >= maxfd || fdcon[s].c_status == CS_UNUSED)
427fa72ddacSKevin Steves 		fatal("confree: attempt to free bad fdno %d", s);
428a627d42eSDarren Tucker 	free(fdcon[s].c_namebase);
429a627d42eSDarren Tucker 	free(fdcon[s].c_output_name);
430b6434ae0SBen Lindstrom 	if (fdcon[s].c_status == CS_KEYS)
431a627d42eSDarren Tucker 		free(fdcon[s].c_data);
432b6434ae0SBen Lindstrom 	fdcon[s].c_status = CS_UNUSED;
433325e70c9SBen Lindstrom 	fdcon[s].c_keytype = 0;
4343f797653Smarkus@openbsd.org 	if (fdcon[s].c_ssh) {
4353f797653Smarkus@openbsd.org 		ssh_packet_close(fdcon[s].c_ssh);
4363f797653Smarkus@openbsd.org 		free(fdcon[s].c_ssh);
4373f797653Smarkus@openbsd.org 		fdcon[s].c_ssh = NULL;
438d79bceb9Sdtucker@openbsd.org 	} else
439d79bceb9Sdtucker@openbsd.org 		close(s);
440b6434ae0SBen Lindstrom 	TAILQ_REMOVE(&tq, &fdcon[s], c_link);
441c1e0421cSBen Lindstrom 	FD_CLR(s, read_wait);
442b6434ae0SBen Lindstrom 	ncon--;
443b6434ae0SBen Lindstrom }
444b6434ae0SBen Lindstrom 
445bba81213SBen Lindstrom static void
contouch(int s)446b6434ae0SBen Lindstrom contouch(int s)
447b6434ae0SBen Lindstrom {
448b6434ae0SBen Lindstrom 	TAILQ_REMOVE(&tq, &fdcon[s], c_link);
4495db6fbf1Sdtucker@openbsd.org@openbsd.org 	monotime_tv(&fdcon[s].c_tv);
450b6434ae0SBen Lindstrom 	fdcon[s].c_tv.tv_sec += timeout;
451b6434ae0SBen Lindstrom 	TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
452b6434ae0SBen Lindstrom }
453b6434ae0SBen Lindstrom 
454bba81213SBen Lindstrom static int
conrecycle(int s)455b6434ae0SBen Lindstrom conrecycle(int s)
456b6434ae0SBen Lindstrom {
457b6434ae0SBen Lindstrom 	con *c = &fdcon[s];
458965710f6SBen Lindstrom 	int ret;
459b6434ae0SBen Lindstrom 
460325e70c9SBen Lindstrom 	ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype);
461b6434ae0SBen Lindstrom 	confree(s);
462b6434ae0SBen Lindstrom 	return (ret);
463b6434ae0SBen Lindstrom }
464b6434ae0SBen Lindstrom 
465bba81213SBen Lindstrom static void
congreet(int s)466b6434ae0SBen Lindstrom congreet(int s)
467b6434ae0SBen Lindstrom {
468eccb9de7SDamien Miller 	int n = 0, remote_major = 0, remote_minor = 0;
469325e70c9SBen Lindstrom 	char buf[256], *cp;
47083c02ef6SDamien Miller 	char remote_version[sizeof buf];
471eccb9de7SDamien Miller 	size_t bufsiz;
472b6434ae0SBen Lindstrom 	con *c = &fdcon[s];
473b6434ae0SBen Lindstrom 
474873d3e7dSdjm@openbsd.org 	/* send client banner */
475873d3e7dSdjm@openbsd.org 	n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n",
476873d3e7dSdjm@openbsd.org 	    PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2);
477873d3e7dSdjm@openbsd.org 	if (n < 0 || (size_t)n >= sizeof(buf)) {
478873d3e7dSdjm@openbsd.org 		error("snprintf: buffer too small");
479873d3e7dSdjm@openbsd.org 		confree(s);
480873d3e7dSdjm@openbsd.org 		return;
481873d3e7dSdjm@openbsd.org 	}
482873d3e7dSdjm@openbsd.org 	if (atomicio(vwrite, s, buf, n) != (size_t)n) {
483873d3e7dSdjm@openbsd.org 		error("write (%s): %s", c->c_name, strerror(errno));
484873d3e7dSdjm@openbsd.org 		confree(s);
485873d3e7dSdjm@openbsd.org 		return;
486873d3e7dSdjm@openbsd.org 	}
487873d3e7dSdjm@openbsd.org 
4884bbacb70SDamien Miller 	for (;;) {
4894bbacb70SDamien Miller 		memset(buf, '\0', sizeof(buf));
490884a4acaSBen Lindstrom 		bufsiz = sizeof(buf);
491884a4acaSBen Lindstrom 		cp = buf;
4924bbacb70SDamien Miller 		while (bufsiz-- &&
4934bbacb70SDamien Miller 		    (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') {
494de8fc6faSBen Lindstrom 			if (*cp == '\r')
495de8fc6faSBen Lindstrom 				*cp = '\n';
496884a4acaSBen Lindstrom 			cp++;
497de8fc6faSBen Lindstrom 		}
4984bbacb70SDamien Miller 		if (n != 1 || strncmp(buf, "SSH-", 4) == 0)
4994bbacb70SDamien Miller 			break;
5004bbacb70SDamien Miller 	}
5016b28c35aSBen Lindstrom 	if (n == 0) {
502b253cc42SDamien Miller 		switch (errno) {
503b253cc42SDamien Miller 		case EPIPE:
5046b28c35aSBen Lindstrom 			error("%s: Connection closed by remote host", c->c_name);
505b253cc42SDamien Miller 			break;
506b253cc42SDamien Miller 		case ECONNREFUSED:
507b253cc42SDamien Miller 			break;
508b253cc42SDamien Miller 		default:
509b253cc42SDamien Miller 			error("read (%s): %s", c->c_name, strerror(errno));
510b253cc42SDamien Miller 			break;
511b253cc42SDamien Miller 		}
5126b28c35aSBen Lindstrom 		conrecycle(s);
5136b28c35aSBen Lindstrom 		return;
5146b28c35aSBen Lindstrom 	}
515884a4acaSBen Lindstrom 	if (*cp != '\n' && *cp != '\r') {
516b6434ae0SBen Lindstrom 		error("%s: bad greeting", c->c_name);
517b6434ae0SBen Lindstrom 		confree(s);
518b6434ae0SBen Lindstrom 		return;
519b6434ae0SBen Lindstrom 	}
520884a4acaSBen Lindstrom 	*cp = '\0';
5214509b5d4Sdjm@openbsd.org 	if ((c->c_ssh = ssh_packet_set_connection(NULL, s, s)) == NULL)
5224509b5d4Sdjm@openbsd.org 		fatal("ssh_packet_set_connection failed");
523802660cbSdjm@openbsd.org 	ssh_packet_set_timeout(c->c_ssh, timeout, 1);
5243f797653Smarkus@openbsd.org 	ssh_set_app_data(c->c_ssh, c);	/* back link */
525*4ca6a1faSdjm@openbsd.org 	c->c_ssh->compat = 0;
526325e70c9SBen Lindstrom 	if (sscanf(buf, "SSH-%d.%d-%[^\n]\n",
527325e70c9SBen Lindstrom 	    &remote_major, &remote_minor, remote_version) == 3)
528*4ca6a1faSdjm@openbsd.org 		compat_banner(c->c_ssh, remote_version);
529325e70c9SBen Lindstrom 	if (!ssh2_capable(remote_major, remote_minor)) {
530325e70c9SBen Lindstrom 		debug("%s doesn't support ssh2", c->c_name);
531325e70c9SBen Lindstrom 		confree(s);
532325e70c9SBen Lindstrom 		return;
533325e70c9SBen Lindstrom 	}
5341a348359Sdjm@openbsd.org 	fprintf(stderr, "%c %s:%d %s\n", print_sshfp ? ';' : '#',
5351a348359Sdjm@openbsd.org 	    c->c_name, ssh_port, chop(buf));
5363f797653Smarkus@openbsd.org 	keygrab_ssh2(c);
537325e70c9SBen Lindstrom 	confree(s);
538b6434ae0SBen Lindstrom }
539b6434ae0SBen Lindstrom 
540bba81213SBen Lindstrom static void
conread(int s)541b6434ae0SBen Lindstrom conread(int s)
542b6434ae0SBen Lindstrom {
543b6434ae0SBen Lindstrom 	con *c = &fdcon[s];
544b253cc42SDamien Miller 	size_t n;
545b6434ae0SBen Lindstrom 
546b6434ae0SBen Lindstrom 	if (c->c_status == CS_CON) {
547b6434ae0SBen Lindstrom 		congreet(s);
548b6434ae0SBen Lindstrom 		return;
549b6434ae0SBen Lindstrom 	}
550fe6649daSDarren Tucker 	n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off);
551b253cc42SDamien Miller 	if (n == 0) {
552b6434ae0SBen Lindstrom 		error("read (%s): %s", c->c_name, strerror(errno));
553b6434ae0SBen Lindstrom 		confree(s);
554b6434ae0SBen Lindstrom 		return;
555b6434ae0SBen Lindstrom 	}
556b6434ae0SBen Lindstrom 	c->c_off += n;
557b6434ae0SBen Lindstrom 
558b6434ae0SBen Lindstrom 	if (c->c_off == c->c_len)
559b6434ae0SBen Lindstrom 		switch (c->c_status) {
560b6434ae0SBen Lindstrom 		case CS_SIZE:
561b6434ae0SBen Lindstrom 			c->c_plen = htonl(c->c_plen);
562b6434ae0SBen Lindstrom 			c->c_len = c->c_plen + 8 - (c->c_plen & 7);
563b6434ae0SBen Lindstrom 			c->c_off = 0;
564b6434ae0SBen Lindstrom 			c->c_data = xmalloc(c->c_len);
565b6434ae0SBen Lindstrom 			c->c_status = CS_KEYS;
566b6434ae0SBen Lindstrom 			break;
567b6434ae0SBen Lindstrom 		default:
568fa72ddacSKevin Steves 			fatal("conread: invalid status %d", c->c_status);
569b6434ae0SBen Lindstrom 			break;
570b6434ae0SBen Lindstrom 		}
571b6434ae0SBen Lindstrom 
572b6434ae0SBen Lindstrom 	contouch(s);
573b6434ae0SBen Lindstrom }
574b6434ae0SBen Lindstrom 
575bba81213SBen Lindstrom static void
conloop(void)576b6434ae0SBen Lindstrom conloop(void)
577b6434ae0SBen Lindstrom {
578b6434ae0SBen Lindstrom 	struct timeval seltime, now;
579965710f6SBen Lindstrom 	fd_set *r, *e;
580b6434ae0SBen Lindstrom 	con *c;
581965710f6SBen Lindstrom 	int i;
582b6434ae0SBen Lindstrom 
5835db6fbf1Sdtucker@openbsd.org@openbsd.org 	monotime_tv(&now);
58461c183beSBen Lindstrom 	c = TAILQ_FIRST(&tq);
585b6434ae0SBen Lindstrom 
5861196d7f4Scheloha@openbsd.org 	if (c && timercmp(&c->c_tv, &now, >))
5871196d7f4Scheloha@openbsd.org 		timersub(&c->c_tv, &now, &seltime);
5881196d7f4Scheloha@openbsd.org 	else
589c5219e70SDamien Miller 		timerclear(&seltime);
590b6434ae0SBen Lindstrom 
59107d86becSDamien Miller 	r = xcalloc(read_wait_nfdset, sizeof(fd_mask));
59207d86becSDamien Miller 	e = xcalloc(read_wait_nfdset, sizeof(fd_mask));
59307d86becSDamien Miller 	memcpy(r, read_wait, read_wait_nfdset * sizeof(fd_mask));
59407d86becSDamien Miller 	memcpy(e, read_wait, read_wait_nfdset * sizeof(fd_mask));
595c1e0421cSBen Lindstrom 
596c1e0421cSBen Lindstrom 	while (select(maxfd, r, NULL, e, &seltime) == -1 &&
597d8968adbSDamien Miller 	    (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK))
598f9452513SBen Lindstrom 		;
599f9452513SBen Lindstrom 
600d20b855bSBen Lindstrom 	for (i = 0; i < maxfd; i++) {
601c1e0421cSBen Lindstrom 		if (FD_ISSET(i, e)) {
602b6434ae0SBen Lindstrom 			error("%s: exception!", fdcon[i].c_name);
603b6434ae0SBen Lindstrom 			confree(i);
604c1e0421cSBen Lindstrom 		} else if (FD_ISSET(i, r))
605b6434ae0SBen Lindstrom 			conread(i);
606d20b855bSBen Lindstrom 	}
607a627d42eSDarren Tucker 	free(r);
608a627d42eSDarren Tucker 	free(e);
609b6434ae0SBen Lindstrom 
61061c183beSBen Lindstrom 	c = TAILQ_FIRST(&tq);
6111196d7f4Scheloha@openbsd.org 	while (c && timercmp(&c->c_tv, &now, <)) {
612b6434ae0SBen Lindstrom 		int s = c->c_fd;
613d20b855bSBen Lindstrom 
61461c183beSBen Lindstrom 		c = TAILQ_NEXT(c, c_link);
615b6434ae0SBen Lindstrom 		conrecycle(s);
616b6434ae0SBen Lindstrom 	}
617b6434ae0SBen Lindstrom }
618b6434ae0SBen Lindstrom 
619325e70c9SBen Lindstrom static void
do_host(char * host)620325e70c9SBen Lindstrom do_host(char *host)
621b6434ae0SBen Lindstrom {
622325e70c9SBen Lindstrom 	char *name = strnnsep(&host, " \t\n");
623325e70c9SBen Lindstrom 	int j;
624b6434ae0SBen Lindstrom 
625eaffb9d6SBen Lindstrom 	if (name == NULL)
626eaffb9d6SBen Lindstrom 		return;
627873d3e7dSdjm@openbsd.org 	for (j = KT_MIN; j <= KT_MAX; j *= 2) {
628325e70c9SBen Lindstrom 		if (get_keytypes & j) {
629325e70c9SBen Lindstrom 			while (ncon >= MAXCON)
630325e70c9SBen Lindstrom 				conloop();
631325e70c9SBen Lindstrom 			conalloc(name, *host ? host : name, j);
632b6434ae0SBen Lindstrom 		}
633b6434ae0SBen Lindstrom 	}
634b6434ae0SBen Lindstrom }
635b6434ae0SBen Lindstrom 
6369c8edc96SBen Lindstrom void
sshfatal(const char * file,const char * func,int line,int showfunc,LogLevel level,const char * suffix,const char * fmt,...)6373554b4afSdjm@openbsd.org sshfatal(const char *file, const char *func, int line, int showfunc,
6389e2c4f64Sdjm@openbsd.org     LogLevel level, const char *suffix, const char *fmt, ...)
639325e70c9SBen Lindstrom {
6409c8edc96SBen Lindstrom 	va_list args;
641965710f6SBen Lindstrom 
6429c8edc96SBen Lindstrom 	va_start(args, fmt);
643f7bd11e4Sdjm@openbsd.org 	sshlogv(file, func, line, showfunc, level, suffix, fmt, args);
6449c8edc96SBen Lindstrom 	va_end(args);
645752250caSdjm@openbsd.org 	cleanup_exit(255);
646325e70c9SBen Lindstrom }
647325e70c9SBen Lindstrom 
648325e70c9SBen Lindstrom static void
usage(void)649b6434ae0SBen Lindstrom usage(void)
650b6434ae0SBen Lindstrom {
651c1719f7fSDamien Miller 	fprintf(stderr,
6521a348359Sdjm@openbsd.org 	    "usage: %s [-46cDHv] [-f file] [-p port] [-T timeout] [-t type]\n"
653f493d2b0Sjmc@openbsd.org 	    "\t\t   [host | addrlist namelist]\n",
654ddfb1e3aSBen Lindstrom 	    __progname);
655ddfb1e3aSBen Lindstrom 	exit(1);
656b6434ae0SBen Lindstrom }
657b6434ae0SBen Lindstrom 
658b6434ae0SBen Lindstrom int
main(int argc,char ** argv)659b6434ae0SBen Lindstrom main(int argc, char **argv)
660b6434ae0SBen Lindstrom {
661325e70c9SBen Lindstrom 	int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO;
6620e76c5e5SDamien Miller 	int opt, fopt_count = 0, j;
6637f906352Smarkus@openbsd.org 	char *tname, *cp, *line = NULL;
6647f906352Smarkus@openbsd.org 	size_t linesize = 0;
6650e76c5e5SDamien Miller 	FILE *fp;
666325e70c9SBen Lindstrom 
667325e70c9SBen Lindstrom 	extern int optind;
668325e70c9SBen Lindstrom 	extern char *optarg;
669b6434ae0SBen Lindstrom 
67059d3d5b8SDamien Miller 	__progname = ssh_get_progname(argv[0]);
6714e088e4dSBen Lindstrom 	seed_rng();
672b6434ae0SBen Lindstrom 	TAILQ_INIT(&tq);
673b6434ae0SBen Lindstrom 
674ce321d8aSDarren Tucker 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
675ce321d8aSDarren Tucker 	sanitise_stdfd();
676ce321d8aSDarren Tucker 
677325e70c9SBen Lindstrom 	if (argc <= 1)
678b6434ae0SBen Lindstrom 		usage();
679b6434ae0SBen Lindstrom 
6801a348359Sdjm@openbsd.org 	while ((opt = getopt(argc, argv, "cDHv46p:T:t:f:")) != -1) {
681325e70c9SBen Lindstrom 		switch (opt) {
682db7b8171SDamien Miller 		case 'H':
683db7b8171SDamien Miller 			hash_hosts = 1;
684db7b8171SDamien Miller 			break;
6853a424cddSdjm@openbsd.org 		case 'c':
6863a424cddSdjm@openbsd.org 			get_cert = 1;
6873a424cddSdjm@openbsd.org 			break;
6881a348359Sdjm@openbsd.org 		case 'D':
6891a348359Sdjm@openbsd.org 			print_sshfp = 1;
6901a348359Sdjm@openbsd.org 			break;
691325e70c9SBen Lindstrom 		case 'p':
692325e70c9SBen Lindstrom 			ssh_port = a2port(optarg);
6933dc71ad8SDamien Miller 			if (ssh_port <= 0) {
694325e70c9SBen Lindstrom 				fprintf(stderr, "Bad port '%s'\n", optarg);
695325e70c9SBen Lindstrom 				exit(1);
696b6434ae0SBen Lindstrom 			}
697325e70c9SBen Lindstrom 			break;
698325e70c9SBen Lindstrom 		case 'T':
699edd098b1SBen Lindstrom 			timeout = convtime(optarg);
700edd098b1SBen Lindstrom 			if (timeout == -1 || timeout == 0) {
701edd098b1SBen Lindstrom 				fprintf(stderr, "Bad timeout '%s'\n", optarg);
702b6434ae0SBen Lindstrom 				usage();
703edd098b1SBen Lindstrom 			}
704325e70c9SBen Lindstrom 			break;
705325e70c9SBen Lindstrom 		case 'v':
706325e70c9SBen Lindstrom 			if (!debug_flag) {
707325e70c9SBen Lindstrom 				debug_flag = 1;
708325e70c9SBen Lindstrom 				log_level = SYSLOG_LEVEL_DEBUG1;
709b6434ae0SBen Lindstrom 			}
710325e70c9SBen Lindstrom 			else if (log_level < SYSLOG_LEVEL_DEBUG3)
711325e70c9SBen Lindstrom 				log_level++;
712325e70c9SBen Lindstrom 			else
713325e70c9SBen Lindstrom 				fatal("Too high debugging level.");
714325e70c9SBen Lindstrom 			break;
715325e70c9SBen Lindstrom 		case 'f':
716325e70c9SBen Lindstrom 			if (strcmp(optarg, "-") == 0)
717325e70c9SBen Lindstrom 				optarg = NULL;
718325e70c9SBen Lindstrom 			argv[fopt_count++] = optarg;
719325e70c9SBen Lindstrom 			break;
720325e70c9SBen Lindstrom 		case 't':
721325e70c9SBen Lindstrom 			get_keytypes = 0;
722325e70c9SBen Lindstrom 			tname = strtok(optarg, ",");
723325e70c9SBen Lindstrom 			while (tname) {
7243f797653Smarkus@openbsd.org 				int type = sshkey_type_from_name(tname);
725f89b9285Sdjm@openbsd.org 
726325e70c9SBen Lindstrom 				switch (type) {
727325e70c9SBen Lindstrom 				case KEY_DSA:
728325e70c9SBen Lindstrom 					get_keytypes |= KT_DSA;
729325e70c9SBen Lindstrom 					break;
730eb8b60e3SDamien Miller 				case KEY_ECDSA:
731eb8b60e3SDamien Miller 					get_keytypes |= KT_ECDSA;
732eb8b60e3SDamien Miller 					break;
733325e70c9SBen Lindstrom 				case KEY_RSA:
734325e70c9SBen Lindstrom 					get_keytypes |= KT_RSA;
735325e70c9SBen Lindstrom 					break;
7365be9d9e3SDamien Miller 				case KEY_ED25519:
7375be9d9e3SDamien Miller 					get_keytypes |= KT_ED25519;
7385be9d9e3SDamien Miller 					break;
7391b11ea7cSmarkus@openbsd.org 				case KEY_XMSS:
7401b11ea7cSmarkus@openbsd.org 					get_keytypes |= KT_XMSS;
7411b11ea7cSmarkus@openbsd.org 					break;
7429b6e30b9Sdjm@openbsd.org 				case KEY_ED25519_SK:
7439b6e30b9Sdjm@openbsd.org 					get_keytypes |= KT_ED25519_SK;
7449b6e30b9Sdjm@openbsd.org 					break;
7459b6e30b9Sdjm@openbsd.org 				case KEY_ECDSA_SK:
7469b6e30b9Sdjm@openbsd.org 					get_keytypes |= KT_ECDSA_SK;
7479b6e30b9Sdjm@openbsd.org 					break;
748325e70c9SBen Lindstrom 				case KEY_UNSPEC:
749f89b9285Sdjm@openbsd.org 				default:
750f89b9285Sdjm@openbsd.org 					fatal("Unknown key type \"%s\"", tname);
751325e70c9SBen Lindstrom 				}
752325e70c9SBen Lindstrom 				tname = strtok(NULL, ",");
753325e70c9SBen Lindstrom 			}
754