- (bal) OpenSSH CVS updates:
   - markus@cvs.openbsd.org 2000/12/04 19:24:02
     [ssh-keyscan.c ssh-keyscan.1]
     David Maziere's ssh-keyscan, ok niels@
 - (bal) Updated Makefile.in to include ssh-keyscan that was just added
   to the recent OpenBSD source tree.
This commit is contained in:
Ben Lindstrom 2000-12-05 01:15:09 +00:00
parent d121f61370
commit b6434ae0e8
4 changed files with 717 additions and 4 deletions

View File

@ -1,6 +1,14 @@
20001205
- (bal) OpenSSH CVS updates:
- markus@cvs.openbsd.org 2000/12/04 19:24:02
[ssh-keyscan.c ssh-keyscan.1]
David Maziere's ssh-keyscan, ok niels@
- (bal) Updated Makefile.in to include ssh-keyscan that was just added
to the recent OpenBSD source tree.
20001204
- (bal) More C functions defined in NeXT that are unaccessable without
defining -POSIX.
defining -POSIX.
- (bal) OpenBSD CVS updates:
- markus@cvs.openbsd.org 2000/12/03 11:29:04
[compat.c]

View File

@ -34,7 +34,7 @@ SSH_MODE= @SSHMODE@
INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@
TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT)
TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT)
LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o cli.o compat.o compress.o crc32.o cygwin_util.o deattack.o dispatch.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o rijndael.o entropy.o readpass.o rsa.o ssh-dss.o ssh-rsa.o tildexpand.o ttymodes.o uidswap.o util.o uuencode.o xmalloc.o
@ -44,8 +44,8 @@ SSHOBJS= ssh.o sshconnect.o sshconnect1.o sshconnect2.o log-client.o readconf.o
SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-skey.o auth2-skey.o auth-rhosts.o auth-options.o auth-krb4.o auth-pam.o auth2-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o dh.o pty.o log-server.o login.o loginrec.o servconf.o serverloop.o md5crypt.o session.o
TROFFMAN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh.1 sshd.8 sftp-server.8
CATMAN = scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh.0 sshd.0 sftp-server.0
TROFFMAN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8
CATMAN = scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh-keyscan.0 ssh.0 sshd.0 sftp-server.0
MANPAGES = @MANTYPE@
CONFIGFILES=sshd_config ssh_config primes
@ -100,6 +100,9 @@ ssh-agent$(EXEEXT): libopenbsd-compat.a libssh.a ssh-agent.o log-client.o
ssh-keygen$(EXEEXT): libopenbsd-compat.a libssh.a ssh-keygen.o log-client.o
$(LD) -o $@ ssh-keygen.o log-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
ssh-keyscan$(EXEEXT): libopenbsd-compat.a libssh.a log-client.o ssh-keyscan.o
$(LD) -o $@ ssh-keyscan.o log-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
sftp-server$(EXEEXT): libopenbsd-compat.a libssh.a sftp-server.o log-server.o
$(LD) -o $@ sftp-server.o log-server.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
@ -146,6 +149,7 @@ install-files:
$(INSTALL) -m 0755 -s ssh-add $(DESTDIR)$(bindir)/ssh-add
$(INSTALL) -m 0755 -s ssh-agent $(DESTDIR)$(bindir)/ssh-agent
$(INSTALL) -m 0755 -s ssh-keygen $(DESTDIR)$(bindir)/ssh-keygen
$(INSTALL) -m 0775 -s ssh-keyscan $(DESTDIR)$(bindir)/ssh-keyscan
$(INSTALL) -m 0755 -s sshd $(DESTDIR)$(sbindir)/sshd
$(INSTALL) -m 0755 -s sftp-server $(DESTDIR)$(libexecdir)/sftp-server
$(INSTALL) -m 644 ssh.[01].out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
@ -229,12 +233,14 @@ uninstall:
-rm -f $(DESTDIR)$(bindir)/ssh-add$(EXEEXT)
-rm -f $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT)
-rm -f $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT)
-rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT)
-rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT)
-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1
-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1
-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-agent.1
-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1
-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1
-rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8
-rm -f $(DESTDIR)$(bindir)/slogin
-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/slogin.1

94
ssh-keyscan.1 Normal file
View File

@ -0,0 +1,94 @@
.Dd January 1, 1996
.Dt ssh-keyscan 1
.Os
.Sh NAME
.Nm ssh-keyscan
.Nd gather ssh public keys
.Sh SYNOPSIS
.Nm ssh-keyscan
.Op Fl t Ar timeout
.Op Ar -- | host | addrlist namelist
.Op Fl f Ar files ...
.Sh DESCRIPTION
.Nm
is a utility for gathering the public ssh host keys of a number of
hosts. It was designed to aid in building and verifying
.Pa ssh_known_hosts
files.
.Nm
provides a minimal interface suitable for use by shell and perl
scripts.
.Pp
.Nm
uses non-blocking socket I/O to contact as many hosts as possible in
parallel, so it is very efficient. The keys from a domain of 1,000
hosts can be collected in tens of seconds, even when some of those
hosts are down or do not run ssh. You do not need login access to the
machines you are scanning, nor does does the scanning process involve
any encryption.
.Sh SECURITY
If you make an ssh_known_hosts file using
.Nm
without verifying the keys, you will be vulnerable to
.I man in the middle
attacks.
On the other hand, if your security model allows such a risk,
.Nm
can help you detect tampered keyfiles or man in the middle attacks which
have begun after you created your ssh_known_hosts file.
.Sh OPTIONS
.Bl -tag -width Ds
.It Fl t
Set the timeout for connection attempts. If
.Pa timeout
seconds have elapsed since a connection was initiated to a host or since the
last time anything was read from that host, then the connection is
closed and the host in question considered unavailable. Default is 5
seconds.
.It Fl f
Read hosts or
.Pa addrlist namelist
pairs from this file, one per line.
If
.Pa -
is supplied instead of a filename,
.Nm
will read hosts or
.Pa addrlist namelist
pairs from the standard input.
.Sh EXAMPLES
.Pp
Print the host key for machine
.Pa hostname :
.Bd -literal
ssh-keyscan hostname
.Ed
.Pp
Find all hosts from the file
.Pa ssh_hosts
which have new or different keys from those in the sorted file
.Pa ssh_known_hosts :
.Bd -literal
ssh-keyscan -f ssh_hosts | sort -u - ssh_known_hosts | \e\
diff ssh_known_hosts -
.Ed
.Pp
.Sh FILES
.Pp
.Pa Input format:
1.2.3.4,1.2.4.4 name.my.domain,name,n.my.domain,n,1.2.3.4,1.2.4.4
.Pp
.Pa Output format:
host-or-namelist bits exponent modulus
.Pp
.Pa /etc/ssh_known_hosts
.Sh BUGS
It generates "Connection closed by remote host" messages on the consoles
of all the machines it scans.
This is because it opens a connection to the ssh port, reads the public
key, and drops the connection as soon as it gets the key.
.Sh SEE ALSO
.Xr ssh 1
.Xr sshd 8
.Sh AUTHOR
David Mazieres <dm@lcs.mit.edu>

605
ssh-keyscan.c Normal file
View File

@ -0,0 +1,605 @@
/*
* Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
*
* Modification and redistribution in source and binary forms is
* permitted provided that due credit is given to the author and the
* OpenBSD project (for instance by leaving this copyright notice
* intact).
*/
#include "includes.h"
RCSID("$OpenBSD: ssh-keyscan.c,v 1.1 2000/12/04 19:24:02 markus Exp $");
#include <sys/queue.h>
#include <err.h>
#include <errno.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include "xmalloc.h"
#include "ssh.h"
#include "key.h"
#include "buffer.h"
#include "bufaux.h"
static int argno = 1; /* Number of argument currently being parsed */
int family = AF_UNSPEC; /* IPv4, IPv6 or both */
#define PORT 22
#define MAXMAXFD 256
/* The number of seconds after which to give up on a TCP connection */
int timeout = 5;
int maxfd;
#define maxcon (maxfd - 10)
char *prog;
fd_set read_wait;
int ncon;
/*
* Keep a connection structure for each file descriptor. The state
* associated with file descriptor n is held in fdcon[n].
*/
typedef struct Connection {
unsigned char c_status; /* State of connection on this file desc. */
#define CS_UNUSED 0 /* File descriptor unused */
#define CS_CON 1 /* Waiting to connect/read greeting */
#define CS_SIZE 2 /* Waiting to read initial packet size */
#define CS_KEYS 3 /* Waiting to read public key packet */
int c_fd; /* Quick lookup: c->c_fd == c - fdcon */
int c_plen; /* Packet length field for ssh packet */
int c_len; /* Total bytes which must be read. */
int c_off; /* Length of data read so far. */
char *c_namebase; /* Address to free for c_name and c_namelist */
char *c_name; /* Hostname of connection for errors */
char *c_namelist; /* Pointer to other possible addresses */
char *c_output_name; /* Hostname of connection for output */
char *c_data; /* Data read from this fd */
struct timeval c_tv; /* Time at which connection gets aborted */
TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */
} con;
TAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */
con *fdcon;
/*
* This is just a wrapper around fgets() to make it usable.
*/
/* Stress-test. Increase this later. */
#define LINEBUF_SIZE 16
typedef struct {
char *buf;
unsigned int size;
int lineno;
const char *filename;
FILE *stream;
void (*errfun) (const char *,...);
} Linebuf;
static inline Linebuf *
Linebuf_alloc(const char *filename, void (*errfun) (const char *,...))
{
Linebuf *lb;
if (!(lb = malloc(sizeof(*lb)))) {
if (errfun)
(*errfun) ("linebuf (%s): malloc failed\n", lb->filename);
return (NULL);
}
if (filename) {
lb->filename = filename;
if (!(lb->stream = fopen(filename, "r"))) {
free(lb);
if (errfun)
(*errfun) ("%s: %s\n", filename, strerror(errno));
return (NULL);
}
} else {
lb->filename = "(stdin)";
lb->stream = stdin;
}
if (!(lb->buf = malloc(lb->size = LINEBUF_SIZE))) {
if (errfun)
(*errfun) ("linebuf (%s): malloc failed\n", lb->filename);
free(lb);
return (NULL);
}
lb->errfun = errfun;
lb->lineno = 0;
return (lb);
}
static inline void
Linebuf_free(Linebuf * lb)
{
fclose(lb->stream);
free(lb->buf);
free(lb);
}
static inline void
Linebuf_restart(Linebuf * lb)
{
clearerr(lb->stream);
rewind(lb->stream);
lb->lineno = 0;
}
static inline int
Linebuf_lineno(Linebuf * lb)
{
return (lb->lineno);
}
static inline char *
getline(Linebuf * lb)
{
int n = 0;
lb->lineno++;
for (;;) {
/* Read a line */
if (!fgets(&lb->buf[n], lb->size - n, lb->stream)) {
if (ferror(lb->stream) && lb->errfun)
(*lb->errfun) ("%s: %s\n", lb->filename, strerror(errno));
return (NULL);
}
n = strlen(lb->buf);
/* Return it or an error if it fits */
if (n > 0 && lb->buf[n - 1] == '\n') {
lb->buf[n - 1] = '\0';
return (lb->buf);
}
if (n != lb->size - 1) {
if (lb->errfun)
(*lb->errfun) ("%s: skipping incomplete last line\n", lb->filename);
return (NULL);
}
/* Double the buffer if we need more space */
if (!(lb->buf = realloc(lb->buf, (lb->size *= 2)))) {
if (lb->errfun)
(*lb->errfun) ("linebuf (%s): realloc failed\n", lb->filename);
return (NULL);
}
}
}
static int
fdlim_get(int hard)
{
struct rlimit rlfd;
if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
return (-1);
if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY)
return 10000;
else
return hard ? rlfd.rlim_max : rlfd.rlim_cur;
}
static int
fdlim_set(int lim)
{
struct rlimit rlfd;
if (lim <= 0)
return (-1);
if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0)
return (-1);
rlfd.rlim_cur = lim;
if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0)
return (-1);
return (0);
}
/*
* This is an strsep function that returns a null field for adjacent
* separators. This is the same as the 4.4BSD strsep, but different from the
* one in the GNU libc.
*/
inline char *
xstrsep(char **str, const char *delim)
{
char *s, *e;
if (!**str)
return (NULL);
s = *str;
e = s + strcspn(s, delim);
if (*e != '\0')
*e++ = '\0';
*str = e;
return (s);
}
/*
* Get the next non-null token (like GNU strsep). Strsep() will return a
* null token for two adjacent separators, so we may have to loop.
*/
char *
strnnsep(char **stringp, char *delim)
{
char *tok;
do {
tok = xstrsep(stringp, delim);
} while (tok && *tok == '\0');
return (tok);
}
void
keyprint(char *host, char *output_name, char *kd, int len)
{
static Key *rsa;
static Buffer msg;
if (rsa == NULL) {
buffer_init(&msg);
rsa = key_new(KEY_RSA1);
}
buffer_append(&msg, kd, len);
buffer_consume(&msg, 8 - (len & 7)); /* padding */
if (buffer_get_char(&msg) != (int) SSH_SMSG_PUBLIC_KEY) {
error("%s: invalid packet type", host);
buffer_clear(&msg);
return;
}
buffer_consume(&msg, 8); /* cookie */
/* server key */
(void) buffer_get_int(&msg);
buffer_get_bignum(&msg, rsa->rsa->e);
buffer_get_bignum(&msg, rsa->rsa->n);
/* host key */
(void) buffer_get_int(&msg);
buffer_get_bignum(&msg, rsa->rsa->e);
buffer_get_bignum(&msg, rsa->rsa->n);
buffer_clear(&msg);
fprintf(stdout, "%s ", output_name ? output_name : host);
key_write(rsa, stdout);
fputs("\n", stdout);
}
int
tcpconnect(char *host)
{
struct addrinfo hints, *ai, *aitop;
char strport[NI_MAXSERV];
int gaierr, s = -1;
snprintf(strport, sizeof strport, "%d", PORT);
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
fatal("getaddrinfo %s: %s", host, gai_strerror(gaierr));
for (ai = aitop; ai; ai = ai->ai_next) {
s = socket(ai->ai_family, SOCK_STREAM, 0);
if (s < 0) {
error("socket: %s", strerror(errno));
continue;
}
if (fcntl(s, F_SETFL, O_NDELAY) < 0)
fatal("F_SETFL: %s", strerror(errno));
if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 &&
errno != EINPROGRESS)
error("connect (`%s'): %s", host, strerror(errno));
else
break;
close(s);
s = -1;
}
freeaddrinfo(aitop);
return s;
}
int
conalloc(char *iname, char *oname)
{
int s;
char *namebase, *name, *namelist;
namebase = namelist = xstrdup(iname);
do {
name = xstrsep(&namelist, ",");
if (!name) {
free(namebase);
return (-1);
}
} while ((s = tcpconnect(name)) < 0);
if (s >= maxfd)
fatal("conalloc: fdno %d too high\n", s);
if (fdcon[s].c_status)
fatal("conalloc: attempt to reuse fdno %d\n", s);
fdcon[s].c_fd = s;
fdcon[s].c_status = CS_CON;
fdcon[s].c_namebase = namebase;
fdcon[s].c_name = name;
fdcon[s].c_namelist = namelist;
fdcon[s].c_output_name = xstrdup(oname);
fdcon[s].c_data = (char *) &fdcon[s].c_plen;
fdcon[s].c_len = 4;
fdcon[s].c_off = 0;
gettimeofday(&fdcon[s].c_tv, NULL);
fdcon[s].c_tv.tv_sec += timeout;
TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
FD_SET(s, &read_wait);
ncon++;
return (s);
}
void
confree(int s)
{
close(s);
if (s >= maxfd || fdcon[s].c_status == CS_UNUSED)
fatal("confree: attempt to free bad fdno %d\n", s);
free(fdcon[s].c_namebase);
free(fdcon[s].c_output_name);
if (fdcon[s].c_status == CS_KEYS)
free(fdcon[s].c_data);
fdcon[s].c_status = CS_UNUSED;
TAILQ_REMOVE(&tq, &fdcon[s], c_link);
FD_CLR(s, &read_wait);
ncon--;
}
void
contouch(int s)
{
TAILQ_REMOVE(&tq, &fdcon[s], c_link);
gettimeofday(&fdcon[s].c_tv, NULL);
fdcon[s].c_tv.tv_sec += timeout;
TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link);
}
int
conrecycle(int s)
{
int ret;
con *c = &fdcon[s];
char *iname, *oname;
iname = xstrdup(c->c_namelist);
oname = c->c_output_name;
c->c_output_name = NULL;/* prevent it from being freed */
confree(s);
ret = conalloc(iname, oname);
free(iname);
return (ret);
}
void
congreet(int s)
{
char buf[80];
int n;
con *c = &fdcon[s];
n = read(s, buf, sizeof(buf));
if (n < 0) {
if (errno != ECONNREFUSED)
error("read (%s): %s", c->c_name, strerror(errno));
conrecycle(s);
return;
}
if (buf[n - 1] != '\n') {
error("%s: bad greeting", c->c_name);
confree(s);
return;
}
buf[n - 1] = '\0';
fprintf(stderr, "# %s %s\n", c->c_name, buf);
n = snprintf(buf, sizeof buf, "SSH-1.5-OpenSSH-keyscan\r\n");
if (write(s, buf, n) != n) {
error("write (%s): %s", c->c_name, strerror(errno));
confree(s);
return;
}
c->c_status = CS_SIZE;
contouch(s);
}
void
conread(int s)
{
int n;
con *c = &fdcon[s];
if (c->c_status == CS_CON) {
congreet(s);
return;
}
n = read(s, c->c_data + c->c_off, c->c_len - c->c_off);
if (n < 0) {
error("read (%s): %s", c->c_name, strerror(errno));
confree(s);
return;
}
c->c_off += n;
if (c->c_off == c->c_len)
switch (c->c_status) {
case CS_SIZE:
c->c_plen = htonl(c->c_plen);
c->c_len = c->c_plen + 8 - (c->c_plen & 7);
c->c_off = 0;
c->c_data = xmalloc(c->c_len);
c->c_status = CS_KEYS;
break;
case CS_KEYS:
keyprint(c->c_name, c->c_output_name, c->c_data, c->c_plen);
confree(s);
return;
break;
default:
fatal("conread: invalid status %d\n", c->c_status);
break;
}
contouch(s);
}
void
conloop(void)
{
fd_set r, e;
struct timeval seltime, now;
int i;
con *c;
gettimeofday(&now, NULL);
c = tq.tqh_first;
if (c &&
(c->c_tv.tv_sec > now.tv_sec ||
(c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) {
seltime = c->c_tv;
seltime.tv_sec -= now.tv_sec;
seltime.tv_usec -= now.tv_usec;
if ((int) seltime.tv_usec < 0) {
seltime.tv_usec += 1000000;
seltime.tv_sec--;
}
} else
seltime.tv_sec = seltime.tv_usec = 0;
r = e = read_wait;
select(maxfd, &r, NULL, &e, &seltime);
for (i = 0; i < maxfd; i++)
if (FD_ISSET(i, &e)) {
error("%s: exception!", fdcon[i].c_name);
confree(i);
} else if (FD_ISSET(i, &r))
conread(i);
c = tq.tqh_first;
while (c &&
(c->c_tv.tv_sec < now.tv_sec ||
(c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) {
int s = c->c_fd;
c = c->c_link.tqe_next;
conrecycle(s);
}
}
char *
nexthost(int argc, char **argv)
{
static Linebuf *lb;
for (;;) {
if (!lb) {
if (argno >= argc)
return (NULL);
if (argv[argno][0] != '-')
return (argv[argno++]);
if (!strcmp(argv[argno], "--")) {
if (++argno >= argc)
return (NULL);
return (argv[argno++]);
} else if (!strncmp(argv[argno], "-f", 2)) {
char *fname;
if (argv[argno][2])
fname = &argv[argno++][2];
else if (++argno >= argc) {
error("missing filename for `-f'");
return (NULL);
} else
fname = argv[argno++];
if (!strcmp(fname, "-"))
fname = NULL;
lb = Linebuf_alloc(fname, warn);
} else
error("ignoring invalid/misplaced option `%s'", argv[argno++]);
} else {
char *line;
line = getline(lb);
if (line)
return (line);
Linebuf_free(lb);
lb = NULL;
}
}
}
static void
usage(void)
{
fatal("usage: %s [-t timeout] { [--] host | -f file } ...\n", prog);
return;
}
int
main(int argc, char **argv)
{
char *host = NULL;
TAILQ_INIT(&tq);
if ((prog = strrchr(argv[0], '/')))
prog++;
else
prog = argv[0];
if (argc <= argno)
usage();
if (argv[1][0] == '-' && argv[1][1] == 't') {
argno++;
if (argv[1][2])
timeout = atoi(&argv[1][2]);
else {
if (argno >= argc)
usage();
timeout = atoi(argv[argno++]);
}
if (timeout <= 0)
usage();
}
if (argc <= argno)
usage();
maxfd = fdlim_get(1);
if (maxfd < 0)
fatal("%s: fdlim_get: bad value\n", prog);
if (maxfd > MAXMAXFD)
maxfd = MAXMAXFD;
if (maxcon <= 0)
fatal("%s: not enough file descriptors\n", prog);
if (maxfd > fdlim_get(0))
fdlim_set(maxfd);
fdcon = xmalloc(maxfd * sizeof(con));
do {
while (ncon < maxcon) {
char *name;
host = nexthost(argc, argv);
if (host == NULL)
break;
name = strnnsep(&host, " \t\n");
conalloc(name, *host ? host : name);
}
conloop();
} while (host);
while (ncon > 0)
conloop();
return (0);
}