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