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