c[plain text]
/* Netcat 1.10 RELEASE 960320
A damn useful little "backend" utility begun 950915 or thereabouts,
as *Hobbit*'s first real stab at some sockets programming. Something that
should have and indeed may have existed ten years ago, but never became a
standard Unix utility. IMHO, "nc" could take its place right next to cat,
cp, rm, mv, dd, ls, and all those other cryptic and Unix-like things.
Read the README for the whole story, doc, applications, etc.
Layout:
conditional includes:
includes:
handy defines:
globals:
malloced globals:
cmd-flag globals:
support routines:
readwrite select loop:
main:
bluesky:
parse ranges of IP address as well as ports, perhaps
RAW mode!
backend progs to grab a pty and look like a real telnetd?!
backend progs to do various encryption modes??!?!
*/
#include "generic.h"
/* conditional includes -- a very messy section which you may have to dink
for your own architecture [and please send diffs...]: */
/* #undef _POSIX_SOURCE
/* might need this for something? */
#define HAVE_BIND
/* ASSUMPTION -- seems to work everywhere! */
#define HAVE_HELP
/* undefine if you dont want the help text */
/* #define ANAL
/* if you want case-sensitive DNS matching */
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#else
#include <malloc.h>
#endif
#ifdef HAVE_SELECT_H
#include <sys/types.h>
#include <sys/select.h>
#endif
/* have to do this *before* including types.h. xxx: Linux still has it wrong */
#ifdef FD_SETSIZE
/* should be in types.h, butcha never know. */
#undef FD_SETSIZE
/* if we ever need more than 16 active */
#endif
/* fd's, something is horribly wrong! */
#define FD_SETSIZE 16
/* <-- this'll give us a long anyways, wtf */
#include <sys/types.h>
/* *now* do it. Sigh, this is broken */
#ifdef HAVE_RANDOM
#define SRAND srandom
/* timeval, time_t */
/* jmp_buf et al */
/* basics, SO_ and AF_ defs, sockaddr, ... */
/* sockaddr_in, htons, in_addr */
/* misc crud that netinet/ip.h references */
/* IPOPT_LSRR, header stuff */
/* hostent, gethostby*, getservby* */
/* inet_ntoa */
/* strcpy, strchr, yadda yadda */
/* O_WRONLY et al */
/* handy stuff: */
#define SA struct sockaddr
/* socket overgeneralization braindeath */
#define SAI struct sockaddr_in /* ... whoever came up with this model */
#define IA struct in_addr
/* ... should be taken out and shot, */
/* ... not that TLI is any better. sigh.. */
#define SLEAZE_PORT 31337
/* for UDP-scan RTT trick, change if ya want */
#define USHORT unsigned short /* use these for options an' stuff */
#define BIGSIZ 8192
/* big buffers */
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif
#ifdef MAXHOSTNAMELEN
#undef MAXHOSTNAMELEN
/* might be too small on aix, so fix it */
#endif
#define MAXHOSTNAMELEN 256
struct host_poop {
char name[MAXHOSTNAMELEN];
char addrs[8][24];
struct in_addr iaddrs[8];
};
#define HINF struct host_poop
struct port_poop {
char name [64];
char anum [8];
USHORT num;
};
#define PINF struct port_poop
/* dns name */
/* ascii-format IP addresses */
/* real addresses: in_addr.s_addr: ulong */
/* name in /etc/services */
/* ascii-format number */
/* real host-order number */
/* globals: */
jmp_buf jbuf;
/* timer crud */
int jval = 0;
/* timer crud */
int netfd = -1;
int ofd = 0;
/* hexdump output fd */
static char unknown[] = "(UNKNOWN)";
static char p_tcp[] = "tcp";
/* for getservby* */
static char p_udp[] = "udp";
#ifdef HAVE_BIND
/* holler :
fake varargs -- need to do this way because we wind up calling through
more levels of indirection than vanilla varargs can handle, and not all
machines have vfprintf/vsyslog/whatever! 6 params oughta be enough. */
void holler (str, p1, p2, p3, p4, p5, p6)
char * str;
char * p1, * p2, * p3, * p4, * p5, * p6;
{
if (o_verbose) {
fprintf (stderr, str, p1, p2, p3, p4, p5, p6);
#ifdef HAVE_BIND
if (h_errno) {
/* if host-lookup variety of error ... */
if (h_errno > 4)
/* oh no you don't, either */
fprintf (stderr, "preposterous h_errno: %d", h_errno);
else
fprintf (stderr, h_errs[h_errno]);
/* handle it here */
h_errno = 0;
/* and reset for next call */
}
#endif
if (errno) {
/* this gives funny-looking messages, but */
perror (" ");
/* it's more portable than sys_errlist[]... */
} else
/* xxx: do something better? */
fprintf (stderr, "\n");
fflush (stderr);
}
} /* holler */
/* bail :
error-exit handler, callable from anywhere */
void bail (str, p1, p2, p3, p4, p5, p6)
char * str;
char * p1, * p2, * p3, * p4, * p5, * p6;
{
o_verbose = 1;
holler (str, p1, p2, p3, p4, p5, p6);
close (netfd);
sleep (1);
exit (1);
} /* bail */
/* catch :
no-brainer interrupt handler */
void catch ()
{
errno = 0;
if (o_verbose > 1)
/* normally we don't care */
bail (wrote_txt, wrote_net, wrote_out);
bail (" punt!");
}
/* timeout and other signal handling cruft */
void tmtravel ()
{
signal (SIGALRM, SIG_IGN);
alarm (0);
if (jval == 0)
bail ("spurious timer interrupt!");
longjmp (jbuf, jval);
}
/* arm :
y = 65535;
while (y > 0) {
if (block[y] == 1) {
block[y] = 2;
break;
}
y--;
} /* while y */
if (y)
return (y);
return (0);
} /* nextport */
/* bummer. */
/* loadports :
set "to be tested" indications in BLOCK, from LO to HI. Almost too small
to be a separate routine, but makes main() a little cleaner... */
/* doexec :
fiddle all the file descriptors around, and hand off to another prog. Sort
of like a one-off "poor man's inetd". This is the only section of code
that would be security-critical, which is why it's ifdefed out by default.
Use at your own hairy risk; if you leave shells lying around behind open
listening ports you deserve to lose!! */
doexec (fd)
int fd;
{
register char * p;
dup2 (fd, 0);
/* the precise order of fiddlage */
close (fd);
/* is apparently crucial; this is */
dup2 (0, 1);
/* swiped directly out of "inetd". */
dup2 (0, 2);
p = strrchr (pr00gie, '/');
/* shorter argv[0] */
if (p)
p++;
else
p = pr00gie;
Debug (("gonna exec %s as %s...", pr00gie, p))
execl (pr00gie, p, NULL);
bail ("exec %s failed", pr00gie);
/* this gets sent out. Hmm... */
} /* doexec */
#endif /* GAPING_SECURITY_HOLE */
/* doconnect :
do all the socket stuff, and return an fd for one of
an open outbound TCP connection
a UDP stub-socket thingie
with appropriate socket options set up if we wanted source-routing, or
an unconnected TCP or UDP socket to listen on.
Examines various global o_blah flags to figure out what-all to do. */
int doconnect (rad, rp, lad, lp)
IA * rad;
USHORT rp;
IA * lad;
USHORT lp;
{
register int nnetfd;
if (rr)
bail ("Can't grab %s:%d with bind",
inet_ntoa(lclend->sin_addr), lp);
if (o_listen)
return (nnetfd);
if (rr < 0)
bail ("local listen fuxored");
}
/* Various things that follow temporarily trash bigbuf_net, which might contain
a copy of any recvfrom()ed packet, but we'll read() another copy later. */
/* I can't believe I have to do all this to get my own goddamn bound address
and port number. It should just get filled in during bind() or something.
All this is only useful if we didn't say -p for listening, since if we
said -p we *know* what port we're listening on. At any rate we won't bother
with it all unless we wanted to see it, although listening quietly on a
random unknown port is probably not very useful without "netstat". */
if (o_verbose) {
x = sizeof (SA);
/* how 'bout getsockNUM instead, pinheads?! */
rr = getsockname (nnetfd, (SA *) lclend, &x);
if (rr < 0)
holler ("local getsockname failed");
strcpy (bigbuf_net, "listening on [");
/* buffer reuse... */
if (lclend->sin_addr.s_addr)
strcat (bigbuf_net, inet_ntoa (lclend->sin_addr));
else
strcat (bigbuf_net, "any");
strcat (bigbuf_net, "] %d ...");
z = ntohs (lclend->sin_port);
holler (bigbuf_net, z);
} /* verbose -- whew!! */
/* UDP is a speeeeecial case -- we have to do I/O *and* get the calling
party's particulars all at once, listen() and accept() don't apply.
At least in the BSD universe, however, recvfrom/PEEK is enough to tell
us something came in, and we can set things up so straight read/write
actually does work after all. Yow. YMMV on strange platforms! */
if (o_udpmode) {
x = sizeof (SA);
/* retval for recvfrom */
arm (2, o_wait);
/* might as well timeout this, too */
if (setjmp (jbuf) == 0) { /* do timeout for initial connect */
rr = recvfrom
/* and here we block... */
(nnetfd, bigbuf_net, BIGSIZ, MSG_PEEK, (SA *) remend, &x);
Debug (("dolisten/recvfrom ding, rr = %d, netbuf %s ", rr, bigbuf_net))
} else
goto dol_tmo;
/* timeout */
arm (0, 0);
/* I'm not completely clear on how this works -- BSD seems to make UDP
just magically work in a connect()ed context, but we'll undoubtedly run
into systems this deal doesn't work on. For now, we apparently have to
issue a connect() on our just-tickled socket so we can write() back.
Again, why the fuck doesn't it just get filled in and taken care of?!
This hack is anything but optimal. Basically, if you want your listener
to also be able to send data back, you need this connect() line, which
also has the side effect that now anything from a different source or even a
different port on the other end won't show up and will cause ICMP errors.
I guess that's what they meant by "connect".
Let's try to remember what the "U" is *really* for, eh? */
rr = connect (nnetfd, (SA *)remend, sizeof (SA));
goto whoisit;
} /* o_udpmode */
/* fall here for TCP */
x = sizeof (SA);
arm (2, o_wait);
if (setjmp (jbuf) == 0) {
rr = accept (nnetfd, (SA *)remend, &x);
} else
goto dol_tmo;
/* timeout */
arm (0, 0);
close (nnetfd);
/* dump the old socket */
nnetfd = rr;
/* here's our new one */
whoisit:
if (rr < 0)
goto dol_err;
/* If we can, look for any IP options. Useful for testing the receiving end of
such things, and is a good exercise in dealing with it. We do this before
the connect message, to ensure that the connect msg is uniformly the LAST
thing to emerge after all the intervening crud. Doesn't work for UDP on
any machines I've tested, but feel free to surprise me. */
#ifdef IP_OPTIONS
if (! o_verbose)
/* if we wont see it, we dont care */
goto dol_noop;
optbuf = Hmalloc (40);
x = 40;
rr = getsockopt (nnetfd, IPPROTO_IP, IP_OPTIONS, optbuf, &x);
if (rr < 0)
holler ("getsockopt failed");
Debug (("ipoptions ret len %d", x))
if (x) {
/* we've got options, lessee em... */
unsigned char * q = (unsigned char *) optbuf;
char * p = bigbuf_net;
/* local variables, yuk! */
char * pp = &bigbuf_net[128];
/* get random space farther out... */
memset (bigbuf_net, 0, 256);
/* clear it all first */
while (x > 0) {
sprintf (pp, "%2.2x ", *q);
/* clumsy, but works: turn into hex */
strcat (p, pp);
/* and build the final string */
q++; p++;
x--;
}
holler ("IP options: %s", bigbuf_net);
} /* if x, i.e. any options */
dol_noop:
#endif /* IP_OPTIONS */
/* find out what address the connection was *to* on our end, in case we're
doing a listen-on-any on a multihomed machine. This allows one to
offer different services via different alias addresses, such as the
"virtual web site" hack. */
memset (bigbuf_net, 0, 64);
cp = &bigbuf_net[32];
x = sizeof (SA);
rr = getsockname (nnetfd, (SA *) lclend, &x);
if (rr < 0)
holler ("post-rcv getsockname failed");
strcpy (cp, inet_ntoa (lclend->sin_addr));
/* now check out who it is. We don't care about mismatched DNS names here,
but any ADDR and PORT we specified had better fucking well match the caller.
Converting from addr to inet_ntoa and back again is a bit of a kludge, but
gethostpoop wants a string and there's much gnarlier code out there already,
so I don't feel bad.
The *real* question is why BFD sockets wasn't designed to allow listens for
connections *from* specific hosts/ports, instead of requiring the caller to
/* fake it */
/* udptest :
fire a couple of packets at a UDP target port, just to see if it's really
there. On BSD kernels, ICMP host/port-unreachable errors get delivered to
our socket as ECONNREFUSED write errors. On SV kernels, we lose; we'll have
to collect and analyze raw ICMP ourselves a la satan's probe_udp_ports
backend. Guess where one could swipe the appropriate code from...
Use the time delay between writes if given, otherwise use the "tcp ping"
trick for getting the RTT. [I got that idea from pluvius, and warped it.]
Return either the original fd, or clean up and return -1. */
udptest (fd, where)
int fd;
IA * where;
{
register int rr;
rr = write (fd, bigbuf_in, 1);
if (rr != 1)
holler ("udptest first write failed?! errno %d", errno);
if (o_wait)
sleep (o_wait);
else {
/* use the tcp-ping trick: try connecting to a normally refused port, which
causes us to block for the time that SYN gets there and RST gets back.
Not completely reliable, but it *does* mostly work. */
o_udpmode = 0;
/* so doconnect does TCP this time */
/* Set a temporary connect timeout, so packet filtration doesnt cause
us to hang forever, and hit it */
o_wait = 5;
/* enough that we'll notice?? */
rr = doconnect (where, SLEAZE_PORT, 0, 0);
if (rr > 0)
close (rr);
/* in case it *did* open */
o_wait = 0;
/* reset it */
o_udpmode++;
/* we *are* still doing UDP, right? */
} /* if o_wait */
errno = 0;
rr = write (fd, bigbuf_in, 1);
if (rr == 1)
return (fd);
close (fd);
return (-1);
} /* udptest */
/* oprint :
Hexdump bytes shoveled either way to a running logfile, in the format:
D offset
- - - - --- 16 bytes --- - - - # .... ascii .....
where "which" sets the direction indicator, D:
0 -- sent to network, or ">"
1 -- rcvd and printed to stdout, or "<"
and "buf" and "n" are data-block and length. If the current block generates
a partial line, so be it; we *want* that lockstep indication of who sent
what when. Adapted from dgaudet's original example -- but must be ripping
*fast*, since we don't want to be too disk-bound... */
void oprint (which, buf, n)
int which;
char * buf;
int n;
{
int bc;
/* in buffer count */
int obc;
/* current "global" offset */
int soc;
/* stage write count */
register unsigned char * p; /* main buf ptr; m.b. unsigned here */
register unsigned char * op; /* out hexdump ptr */
register unsigned char * a; /* out asc-dump ptr */
register int x;
register unsigned int y;
if (! ofd)
bail ("oprint called with no open fd?!");
if (n == 0)
return;
op = stage;
if (which) {
*op = '<';
obc = wrote_out;
} else {
*op = '>';
obc = wrote_net;
}
op++;
*op = ' ';
p = (unsigned char *) buf;
bc = n;
stage[59] = '#';
stage[60] = ' ';
while (bc) {
x = 16;
soc = 78;
if (bc < x) {
soc = soc - 16 + bc;
x = (bc * 3) + 11;
op = &stage[x];
x = 16 - bc;
/* preload "direction" */
/* preload separator */
/* for chunk-o-data ... */
/* len of whole formatted line */
/* fiddle for however much is left */
/* 2 digits + space per, after D & offset */
while (x) {
*op++ = ' ';
*op++ = ' ';
*op++ = ' ';
x--;
}
x = bc;
} /* if bc < x */
bc -= x;
/* fix wrt current line size */
sprintf (&stage[2], "%8.8x ", obc);
/* xxx: still slow? */
obc += x;
/* fix current offset */
op = &stage[11];
/* where hex starts */
a = &stage[61];
/* where ascii starts */
while (x) {
/* for line of dump, however long ... */
y = (int)(*p >> 4);
/* hi half */
*op = hexnibs[y];
op++;
y = (int)(*p & 0x0f);
/* lo half */
*op = hexnibs[y];
op++;
*op = ' ';
op++;
if ((*p > 31) && (*p < 127))
*a = *p;
/* printing */
else
*a = '.';
/* nonprinting, loose def */
a++;
p++;
x--;
} /* while x */
*a = '\n';
/* finish the line */
x = write (ofd, stage, soc);
if (x < 0)
bail ("ofd write err");
} /* while bc */
} /* oprint */
#ifdef TELNET
USHORT o_tn = 0;
/* global -t option */
/* atelnet :
Answer anything that looks like telnet negotiation with don't/won't.
This doesn't modify any data buffers, update the global output count,
or show up in a hexdump -- it just shits into the outgoing stream.
Idea and codebase from Mudge@l0pht.com. */
void atelnet (buf, size)
unsigned char * buf;
/* has to be unsigned here! */
unsigned int size;
{
static unsigned char obuf [4]; /* tiny thing to build responses into */
register int x;
register unsigned char y;
register unsigned char * p;
y = 0;
p = buf;
x = size;
while (x > 0) {
if (*p != 255)
/* IAC? */
goto notiac;
obuf[0] = 255;
p++; x--;
if ((*p == 251) || (*p == 252))
/* WILL or WONT */
y = 254;
/* -> DONT */
if ((*p == 253) || (*p == 254))
/* DO or DONT */
y = 252;
/* -> WONT */
if (y) {
obuf[1] = y;
p++; x--;
obuf[2] = *p;
/* copy actual option byte */
(void) write (netfd, obuf, 3);
/* if one wanted to bump wrote_net or do a hexdump line, here's the place */
y = 0;
} /* if y */
notiac:
p++; x--;
} /* while x */
} /* atelnet */
#endif /* TELNET */
/* readwrite :
handle stdin/stdout/network I/O. Bwahaha!! -- the select loop from hell.
In this instance, return what might become our exit status. */
int readwrite (fd)
int fd;
{
register int rr;
register char * zp;
/* stdin buf ptr */
register char * np;
/* net-in buf ptr */
unsigned int rzleft;
unsigned int rnleft;
USHORT netretry;
/* net-read retry counter */
USHORT wretry;
/* net-write sanity counter */
USHORT wfirst;
/* one-shot flag to skip first net read */
/* if you don't have all this FD_* macro hair in sys/types.h, you'll have to
either find it or do your own bit-bashing: *ding1 |= (1 << fd), etc... */
if (fd > FD_SETSIZE) {
holler ("Preposterous fd value %d", fd);
return (1);
}
FD_SET (fd, ding1);
/* global: the net is open */
netretry = 2;
wfirst = 0;
rzleft = rnleft = 0;
if (insaved) {
rzleft = insaved;
/* preload multi-mode fakeouts */
zp = bigbuf_in;
wfirst = 1;
if (Single)
/* if not scanning, this is a one-off first */
insaved = 0;
/* buffer left over from argv construction, */
else {
FD_CLR (0, ding1);
/* OR we've already got our repeat chunk, */
close (0);
/* so we won't need any more stdin */
} /* Single */
} /* insaved */
if (o_interval)
sleep (o_interval);
/* pause *before* sending stuff, too */
errno = 0;
/* clear from sleep, close, whatever */
continue;
}
if ((rzleft) || (rnleft)) {
wretry--;
goto shovel;
}
} /* while ding1:netfd is open */
/* XXX: maybe want a more graceful shutdown() here, or screw around with
linger times?? I suspect that I don't need to since I'm always doing
blocking reads and writes and my own manual "last ditch" efforts to read
the net again after a timeout. I haven't seen any screwups yet, but it's
not like my test network is particularly busy... */
close (fd);
return (0);
} /* readwrite */
/* main :
now we pull it all together... */
main (argc, argv)
int argc;
char ** argv;
{
#ifndef HAVE_GETOPT
extern char * optarg;
extern int optind, optopt;
#endif
register int x;
register char *cp;
HINF * gp;
HINF * whereto = NULL;
HINF * wherefrom = NULL;
IA * ouraddr = NULL;
IA * themaddr = NULL;
USHORT o_lport = 0;
USHORT ourport = 0;
USHORT loport = 0;
/* for scanning stuff */
USHORT hiport = 0;
USHORT curport = 0;
char * randports = NULL;
#ifdef HAVE_BIND
/* can *you* say "cc -yaddayadda netcat.c -lresolv -l44bsd" on SunLOSs? */
res_init();
#endif
/* I was in this barbershop quartet in Skokie IL ... */
/* round up the usual suspects, i.e. malloc up all the stuff we need */
lclend = (SAI *) Hmalloc (sizeof (SA));
remend = (SAI *) Hmalloc (sizeof (SA));
bigbuf_in = Hmalloc (BIGSIZ);
bigbuf_net = Hmalloc (BIGSIZ);
ding1 = (fd_set *) Hmalloc (sizeof (fd_set));
ding2 = (fd_set *) Hmalloc (sizeof (fd_set));
portpoop = (PINF *) Hmalloc (sizeof (PINF));
errno = 0;
gatesptr = 4;
h_errno = 0;
/* catch a signal or two for cleanup */
signal (SIGINT, catch);
/* important! */
/* if no args given at all, get 'em from stdin, construct an argv, and hand
anything left over to readwrite(). */
if (argc == 1) {
cp = argv[0];
argv = (char **) Hmalloc (128 * sizeof (char *)); /* XXX: 128? */
argv[0] = cp;
/* leave old prog name intact */
cp = Hmalloc (BIGSIZ);
argv[1] = cp;
/* head of new arg block */
fprintf (stderr, "Cmd line: ");
fflush (stderr);
/* I dont care if it's unbuffered or not! */
insaved = read (0, cp, BIGSIZ);
/* we're gonna fake fgets() here */
if (insaved <= 0)
bail ("wrong");
x = findline (cp, insaved);
if (x)
insaved -= x;
/* remaining chunk size to be sent */
if (insaved)
/* which might be zero... */
memcpy (bigbuf_in, &cp[x], insaved);
cp = strchr (argv[1], '\n');
if (cp)
*cp = '\0';
cp = strchr (argv[1], '\r');
/* look for ^M too */
if (cp)
*cp = '\0';
/* find and stash pointers to remaining new "args" */
cp = argv[1];
cp++;
/* skip past first char */
x = 2;
/* we know argv 0 and 1 already */
for (; *cp != '\0'; cp++) {
if (*cp == ' ') {
*cp = '\0';
/* smash all spaces */
continue;
} else {
if (*(cp-1) == '\0') {
argv[x] = cp;
x++;
}
} /* if space */
} /* for cp */
argc = x;
} /* if no args given */
/* If your shitbox doesn't have getopt, step into the nineties already. */
/* optarg, optind = next-argv-component [i.e. flag arg]; optopt = last-char */
while ((x = getopt (argc, argv, "ae:g:G:hi:lno:p:rs:tuvw:z")) != EOF) {
/* Debug (("in go: x now %c, optarg %x optind %d", x, optarg, optind)) */
switch (x) {
case 'a':
bail ("all-A-records NIY");
o_alla++; break;
#ifdef GAPING_SECURITY_HOLE
case 'e':
/* prog to exec */
pr00gie = optarg;
break;
#endif
case 'G':
/* srcrt gateways pointer val */
x = atoi (optarg);
if ((x) && (x == (x & 0x1c))) /* mask off bits of fukt values */
gatesptr = x;
else
bail ("invalid hop pointer %d, must be multiple of 4 <= 28", x);
break;
case 'g':
/* srcroute hop[s] */
if (gatesidx > 8)
bail ("too many -g hops");
if (gates == NULL)
/* eat this, Billy-boy */
gates = (HINF **) Hmalloc (sizeof (HINF *) * 10);
gp = gethostpoop (optarg, o_nflag);
if (gp)
gates[gatesidx] = gp;
gatesidx++;
break;
case 'h':
errno = 0;
#ifdef HAVE_HELP
helpme();
/* exits by itself */
#else
bail ("no help available, dork -- RTFS");
#endif
case 'i':
/* line-interval time */
o_interval = atoi (optarg) & 0xffff;
if (! o_interval)
bail ("invalid interval time %s", optarg);
break;
case 'l':
/* listen mode */
o_listen++; break;
case 'n':
/* numeric-only, no DNS lookups */
o_nflag++; break;
case 'o':
/* hexdump log */
stage = (unsigned char *) optarg;
o_wfile++; break;
case 'p':
/* local source port */
o_lport = getportpoop (optarg, 0);
if (o_lport == 0)
bail ("invalid local port %s", optarg);
break;
case 'r':
/* randomize various things */
o_random++; break;
case 's':
/* local source address */
/* do a full lookup [since everything else goes through the same mill],
unless -n was previously specified. In fact, careful placement of -n can
be useful, so we'll still pass o_nflag here instead of forcing numeric. */
wherefrom = gethostpoop (optarg, o_nflag);
ouraddr = &wherefrom->iaddrs[0];
break;
#ifdef TELNET
case 't':
/* do telnet fakeout */
o_tn++; break;
#endif /* TELNET */
case 'u':
/* use UDP */
o_udpmode++; break;
case 'v':
/* verbose */
o_verbose++; break;
case 'w':
/* wait time */
o_wait = atoi (optarg);
if (o_wait <= 0)
bail ("invalid wait-time %s", optarg);
timer1 = (struct timeval *) Hmalloc (sizeof (struct timeval));
timer2 = (struct timeval *) Hmalloc (sizeof (struct timeval));
timer1->tv_sec = o_wait;
/* we need two. see readwrite()... */
break;
case 'z':
/* little or no data xfer */
o_zero++;
break;
default:
errno = 0;
bail ("nc -h for help");
} /* switch x */
} /* while getopt */
/* other misc initialization */
Debug (("fd_set size %d", sizeof (*ding1)))
/* how big *is* it? */
FD_SET (0, ding1);
/* stdin *is* initially open */
if (o_random) {
SRAND (time (0));
randports = Hmalloc (65536);
/* big flag array for ports */
}
#ifdef GAPING_SECURITY_HOLE
if (pr00gie) {
close (0);
/* won't need stdin */
o_wfile = 0;
/* -o with -e is meaningless! */
ofd = 0;
}
#endif /* G_S_H */
if (o_wfile) {
ofd = open (stage, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (ofd <= 0)
/* must be > extant 0/1/2 */
bail ("can't open %s", stage);
stage = (unsigned char *) Hmalloc (100);
}
/* optind is now index of first non -x arg */
Debug (("after go: x now %c, optarg %x optind %d", x, optarg, optind))
/* Debug (("optind up to %d at host-arg %s", optind, argv[optind])) */
/* gonna only use first addr of host-list, like our IQ was normal; if you wanna
get fancy with addresses, look up the list yourself and plug 'em in for now.
unless we finally implement -a, that is. */
if (argv[optind])
whereto = gethostpoop (argv[optind], o_nflag);
if (whereto && whereto->iaddrs)
themaddr = &whereto->iaddrs[0];
if (themaddr)
optind++;
/* skip past valid host lookup */
errno = 0;
h_errno = 0;
/* Handle listen mode here, and exit afterward. Only does one connect;
this is arguably the right thing to do. A "persistent listen-and-fork"
mode a la inetd has been thought about, but not implemented. A tiny
wrapper script can handle such things... */
if (o_listen) {
curport = 0;
/* rem port *can* be zero here... */
if (argv[optind]) {
/* any rem-port-arg? */
curport = getportpoop (argv[optind], 0);
if (curport == 0)
/* if given, demand correctness */
bail ("invalid port %s", argv[optind]);
} /* if port-arg */
netfd = dolisten (themaddr, curport, ouraddr, o_lport);
/* dolisten does its own connect reporting, so we don't holler anything here */
if (netfd > 0) {
#ifdef GAPING_SECURITY_HOLE
if (pr00gie)
/* -e given? */
doexec (netfd);
#endif /* GAPING_SECURITY_HOLE */
x = readwrite (netfd);
/* it even works with UDP! */
if (o_verbose > 1)
/* normally we don't care */
holler (wrote_txt, wrote_net, wrote_out);
exit (x);
/* "pack out yer trash" */
} else /* if no netfd */
bail ("no connection");
} /* o_listen */
/* fall thru to outbound connects. Now we're more picky about args... */
if (! themaddr)
bail ("no destination");
if (argv[optind] == NULL)
bail ("no port[s] to connect to");
if (argv[optind + 1])
/* look ahead: any more port args given? */
Single = 0;
/* multi-mode, case A */
ourport = o_lport;
/* which can be 0 */
/* everything from here down is treated as as ports and/or ranges thereof, so
it's all enclosed in this big ol' argv-parsin' loop. Any randomization is
done within each given *range*, but in separate chunks per each succeeding
argument, so we can control the pattern somewhat. */
while (argv[optind]) {
hiport = loport = 0;
cp = strchr (argv[optind], '-');
/* nn-mm range? */
if (cp) {
*cp = '\0';
cp++;
hiport = getportpoop (cp, 0);
if (hiport == 0)
bail ("invalid port %s", cp);
} /* if found a dash */
loport = getportpoop (argv[optind], 0);
if (loport == 0)
bail ("invalid port %s", argv[optind]);
if (hiport > loport) {
/* was it genuinely a range? */
Single = 0;
/* multi-mode, case B */
curport = hiport;
/* start high by default */
if (o_random) {
/* maybe populate the random array */
loadports (randports, loport, hiport);
curport = nextport (randports);
}
} else
/* not a range, including args like "25-25" */
curport = loport;
Debug (("Single %d, curport %d", Single, curport))
/* Now start connecting to these things. curport is already preloaded. */
while (loport <= curport) {
if ((! o_lport) && (o_random)) { /* -p overrides random local-port */
ourport = (RAND() & 0xffff);
/* random local-bind -- well above */
}
curport = getportpoop (NULL, curport);
netfd = doconnect (themaddr, curport, ouraddr, ourport);
Debug (("netfd %d from port %d to port %d", netfd, ourport, curport))
if (netfd > 0)
if (o_zero && o_udpmode)
/* if UDP scanning... */
netfd = udptest (netfd, themaddr);
if (netfd > 0) {
/* Yow, are we OPEN YET?! */
x = 0;
/* pre-exit status */
holler ("%s [%s] %d (%s) open",
whereto->name, whereto->addrs[0], curport, portpoop->name);
#ifdef GAPING_SECURITY_HOLE
if (pr00gie)
/* exec is valid for outbound, too */
doexec (netfd);
#endif /* GAPING_SECURITY_HOLE */
if (! o_zero)
x = readwrite (netfd);
/* go shovel shit */
} else { /* no netfd... */
x = 1;
/* preload exit status for later */
/* if we're scanning at a "one -v" verbosity level, don't print refusals.
Give it another -v if you want to see everything. */
if ((Single || (o_verbose > 1)) || (errno != ECONNREFUSED))
holler ("%s [%s] %d (%s)",
whereto->name, whereto->addrs[0], curport, portpoop->name);
} /* if netfd */
close (netfd);
/* just in case we didn't already */
if (o_interval)
sleep (o_interval);
/* if -i, delay between ports too */
if (o_random)
curport = nextport (randports);
else
curport--;
/* just decrement... */
} /* while curport within current range */
optind++;
} /* while remaining port-args -- end of big argv-ports loop*/
errno = 0;
if (o_verbose > 1)
/* normally we don't care */
holler (wrote_txt, wrote_net, wrote_out);
if (Single)
exit (x);
/* give us status on one connection */
exit (0);
/* otherwise, we're just done */
} /* main */
#ifdef HAVE_HELP
/* unless we wanna be *really* cryptic */
/* helpme :
the obvious */
helpme()
{
o_verbose = 1;
holler ("[v1.10]\n\
connect to somewhere: nc [-options] hostname port[s] [ports] ... \n\
listen for inbound:
nc -l -p port [-options] [hostname] [port]\n\
options:");
/* sigh, this necessarily gets messy. And the trailing \ characters may be
interpreted oddly by some compilers, generating or not generating extra
newlines as they bloody please. u-fix... */
#ifdef GAPING_SECURITY_HOLE
/* needs to be separate holler() */
holler ("\
-e prog
program to exec after connect [dangerous!!]");
#endif
holler ("\
-g gateway
source-routing hop point[s], up to 8\n\
-G num
source-routing pointer: 4, 8, 12, ...\n\
-h
this cruft\n\
-i secs
delay interval for lines sent, ports scanned\n\
-l
listen mode, for inbound connects\n\
-n
numeric-only IP addresses, no DNS\n\
-o file
hex dump of traffic\n\
-p port
local port number\n\
-r
randomize local and remote ports\n\
-s addr
local source address");
#ifdef TELNET
holler ("\
-t
answer TELNET negotiation");
#endif
holler ("\
-u
UDP mode\n\
-v
verbose [use twice to be more verbose]\n\
-w secs
timeout for connects and final net reads\n\
-z
zero-I/O mode [used for scanning]");
bail ("port numbers can be individual or ranges: lo-hi [inclusive]");
} /* helpme */
#endif /* HAVE_HELP */
/* None genuine without this seal! _H*/