[BACK]Return to netcat.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / nc

Annotation of src/usr.bin/nc/netcat.c, Revision 1.4

1.1       deraadt     1: /* Netcat 1.10 RELEASE 960320
                      2:
                      3:    A damn useful little "backend" utility begun 950915 or thereabouts,
                      4:    as *Hobbit*'s first real stab at some sockets programming.  Something that
                      5:    should have and indeed may have existed ten years ago, but never became a
                      6:    standard Unix utility.  IMHO, "nc" could take its place right next to cat,
                      7:    cp, rm, mv, dd, ls, and all those other cryptic and Unix-like things.
                      8:
                      9:    Read the README for the whole story, doc, applications, etc.
                     10:
                     11:    Layout:
                     12:        conditional includes:
                     13:        includes:
                     14:        handy defines:
                     15:        globals:
                     16:        malloced globals:
                     17:        cmd-flag globals:
                     18:        support routines:
                     19:        readwrite select loop:
                     20:        main:
                     21:
                     22:   bluesky:
                     23:        parse ranges of IP address as well as ports, perhaps
                     24:        RAW mode!
                     25:        backend progs to grab a pty and look like a real telnetd?!
                     26:        backend progs to do various encryption modes??!?!
                     27: */
                     28:
                     29: #include "generic.h"           /* same as with L5, skey, etc */
                     30:
                     31: /* conditional includes -- a very messy section which you may have to dink
                     32:    for your own architecture [and please send diffs...]: */
                     33: /* #undef _POSIX_SOURCE                /* might need this for something? */
                     34: #define HAVE_BIND              /* ASSUMPTION -- seems to work everywhere! */
                     35: #define HAVE_HELP              /* undefine if you dont want the help text */
                     36: /* #define ANAL                        /* if you want case-sensitive DNS matching */
                     37:
                     38: #ifdef HAVE_STDLIB_H
                     39: #include <stdlib.h>
                     40: #else
                     41: #include <malloc.h>
                     42: #endif
                     43: #ifdef HAVE_SELECT_H           /* random SV variants need this */
                     44: #include <sys/select.h>
                     45: #endif
                     46:
                     47: /* have to do this *before* including types.h. xxx: Linux still has it wrong */
                     48: #ifdef FD_SETSIZE              /* should be in types.h, butcha never know. */
                     49: #undef FD_SETSIZE              /* if we ever need more than 16 active */
                     50: #endif                         /* fd's, something is horribly wrong! */
                     51: #define FD_SETSIZE 16          /* <-- this'll give us a long anyways, wtf */
                     52: #include <sys/types.h>         /* *now* do it.  Sigh, this is broken */
                     53:
                     54: #ifdef HAVE_RANDOM             /* aficionados of ?rand48() should realize */
                     55: #define SRAND srandom          /* that this doesn't need *strong* random */
                     56: #define RAND random            /* numbers just to mix up port numbers!! */
                     57: #else
                     58: #define SRAND srand
                     59: #define RAND rand
                     60: #endif /* HAVE_RANDOM */
                     61:
                     62: /* includes: */
                     63: #include <sys/time.h>          /* timeval, time_t */
                     64: #include <setjmp.h>            /* jmp_buf et al */
                     65: #include <sys/socket.h>                /* basics, SO_ and AF_ defs, sockaddr, ... */
                     66: #include <netinet/in.h>                /* sockaddr_in, htons, in_addr */
                     67: #include <netinet/in_systm.h>  /* misc crud that netinet/ip.h references */
                     68: #include <netinet/ip.h>                /* IPOPT_LSRR, header stuff */
                     69: #include <netdb.h>             /* hostent, gethostby*, getservby* */
                     70: #include <arpa/inet.h>         /* inet_ntoa */
                     71: #include <stdio.h>
                     72: #include <string.h>            /* strcpy, strchr, yadda yadda */
                     73: #include <errno.h>
                     74: #include <signal.h>
                     75: #include <fcntl.h>             /* O_WRONLY et al */
                     76:
                     77: /* handy stuff: */
                     78: #define SA struct sockaddr     /* socket overgeneralization braindeath */
                     79: #define SAI struct sockaddr_in /* ... whoever came up with this model */
                     80: #define IA struct in_addr      /* ... should be taken out and shot, */
                     81:                                /* ... not that TLI is any better.  sigh.. */
                     82: #define SLEAZE_PORT 31337      /* for UDP-scan RTT trick, change if ya want */
                     83: #define USHORT unsigned short  /* use these for options an' stuff */
                     84: #define BIGSIZ 8192            /* big buffers */
                     85:
                     86: #ifndef INADDR_NONE
                     87: #define INADDR_NONE 0xffffffff
                     88: #endif
                     89:
                     90: struct host_poop {
                     91:   char name[MAXHOSTNAMELEN];   /* dns name */
                     92:   char addrs[8][24];           /* ascii-format IP addresses */
                     93:   struct in_addr iaddrs[8];    /* real addresses: in_addr.s_addr: ulong */
                     94: };
                     95: #define HINF struct host_poop
                     96:
                     97: struct port_poop {
                     98:   char name [64];              /* name in /etc/services */
                     99:   char anum [8];               /* ascii-format number */
                    100:   USHORT num;                  /* real host-order number */
                    101: };
                    102: #define PINF struct port_poop
                    103:
                    104: /* globals: */
                    105: jmp_buf jbuf;                  /* timer crud */
                    106: int jval = 0;                  /* timer crud */
                    107: int netfd = -1;
                    108: int ofd = 0;                   /* hexdump output fd */
                    109: static char unknown[] = "(UNKNOWN)";
                    110: static char p_tcp[] = "tcp";   /* for getservby* */
                    111: static char p_udp[] = "udp";
                    112: #ifdef HAVE_BIND
                    113: extern int h_errno;
                    114: /* stolen almost wholesale from bsd herror.c */
                    115: static char * h_errs[] = {
                    116:   "Error 0",                           /* but we *don't* use this */
                    117:   "Unknown host",                      /* 1 HOST_NOT_FOUND */
                    118:   "Host name lookup failure",          /* 2 TRY_AGAIN */
                    119:   "Unknown server error",              /* 3 NO_RECOVERY */
                    120:   "No address associated with name",   /* 4 NO_ADDRESS */
                    121: };
                    122: #else
                    123: int h_errno;                   /* just so we *do* have it available */
                    124: #endif /* HAVE_BIND */
                    125: int gatesidx = 0;              /* LSRR hop count */
                    126: int gatesptr = 4;              /* initial LSRR pointer, settable */
                    127: USHORT Single = 1;             /* zero if scanning */
                    128: unsigned int insaved = 0;      /* stdin-buffer size for multi-mode */
                    129: unsigned int wrote_out = 0;    /* total stdout bytes */
                    130: unsigned int wrote_net = 0;    /* total net bytes */
                    131: static char wrote_txt[] = " sent %d, rcvd %d";
                    132: static char hexnibs[20] = "0123456789abcdef  ";
                    133:
                    134: /* will malloc up the following globals: */
                    135: struct timeval * timer1 = NULL;
                    136: struct timeval * timer2 = NULL;
                    137: SAI * lclend = NULL;           /* sockaddr_in structs */
                    138: SAI * remend = NULL;
                    139: HINF ** gates = NULL;          /* LSRR hop hostpoop */
                    140: char * optbuf = NULL;          /* LSRR or sockopts */
                    141: char * bigbuf_in;              /* data buffers */
                    142: char * bigbuf_net;
                    143: fd_set * ding1;                        /* for select loop */
                    144: fd_set * ding2;
                    145: PINF * portpoop = NULL;                /* for getportpoop / getservby* */
                    146: unsigned char * stage = NULL;  /* hexdump line buffer */
                    147:
                    148: /* global cmd flags: */
                    149: USHORT o_alla = 0;
                    150: unsigned int o_interval = 0;
                    151: USHORT o_listen = 0;
                    152: USHORT o_nflag = 0;
                    153: USHORT o_wfile = 0;
                    154: USHORT o_random = 0;
                    155: USHORT o_udpmode = 0;
                    156: USHORT o_verbose = 0;
                    157: unsigned int o_wait = 0;
                    158: USHORT o_zero = 0;
                    159: /* o_tn in optional section */
                    160:
                    161: /* Debug macro: squirt whatever message and sleep a bit so we can see it go
                    162:    by.  need to call like Debug ((stuff)) [with no ; ] so macro args match!
                    163:    Beware: writes to stdOUT... */
                    164: #ifdef DEBUG
                    165: #define Debug(x) printf x; printf ("\n"); fflush (stdout); sleep (1);
                    166: #else
                    167: #define Debug(x)       /* nil... */
                    168: #endif
                    169:
                    170:
                    171: /* support routines -- the bulk of this thing.  Placed in such an order that
                    172:    we don't have to forward-declare anything: */
                    173:
                    174: /* holler :
                    175:    fake varargs -- need to do this way because we wind up calling through
                    176:    more levels of indirection than vanilla varargs can handle, and not all
                    177:    machines have vfprintf/vsyslog/whatever!  6 params oughta be enough. */
                    178: void holler (str, p1, p2, p3, p4, p5, p6)
                    179:   char * str;
                    180:   char * p1, * p2, * p3, * p4, * p5, * p6;
                    181: {
                    182:   if (o_verbose) {
                    183:     fprintf (stderr, str, p1, p2, p3, p4, p5, p6);
                    184: #ifdef HAVE_BIND
                    185:     if (h_errno) {             /* if host-lookup variety of error ... */
                    186:       if (h_errno > 4)         /* oh no you don't, either */
                    187:        fprintf (stderr, "preposterous h_errno: %d", h_errno);
                    188:       else
                    189:        fprintf (stderr, h_errs[h_errno]);      /* handle it here */
                    190:       h_errno = 0;                             /* and reset for next call */
                    191:     }
                    192: #endif
                    193:     if (errno) {               /* this gives funny-looking messages, but */
                    194:       perror (" ");            /* it's more portable than sys_errlist[]... */
                    195:     } else                     /* xxx: do something better?  */
                    196:       fprintf (stderr, "\n");
                    197:     fflush (stderr);
                    198:   }
                    199: } /* holler */
                    200:
                    201: /* bail :
                    202:    error-exit handler, callable from anywhere */
                    203: void bail (str, p1, p2, p3, p4, p5, p6)
                    204:   char * str;
                    205:   char * p1, * p2, * p3, * p4, * p5, * p6;
                    206: {
                    207:   o_verbose = 1;
                    208:   holler (str, p1, p2, p3, p4, p5, p6);
                    209:   close (netfd);
                    210:   sleep (1);
                    211:   exit (1);
                    212: } /* bail */
                    213:
                    214: /* catch :
                    215:    no-brainer interrupt handler */
                    216: void catch ()
                    217: {
                    218:   errno = 0;
                    219:   if (o_verbose > 1)           /* normally we don't care */
                    220:     bail (wrote_txt, wrote_net, wrote_out);
                    221:   bail (" punt!");
                    222: }
                    223:
                    224: /* timeout and other signal handling cruft */
                    225: void tmtravel ()
                    226: {
                    227:   signal (SIGALRM, SIG_IGN);
                    228:   alarm (0);
                    229:   if (jval == 0)
                    230:     bail ("spurious timer interrupt!");
                    231:   longjmp (jbuf, jval);
                    232: }
                    233:
                    234: /* arm :
                    235:    set the timer.  Zero secs arg means unarm */
                    236: void arm (num, secs)
                    237:   unsigned int num;
                    238:   unsigned int secs;
                    239: {
                    240:   if (secs == 0) {                     /* reset */
                    241:     signal (SIGALRM, SIG_IGN);
                    242:     alarm (0);
                    243:     jval = 0;
                    244:   } else {                             /* set */
                    245:     signal (SIGALRM, tmtravel);
                    246:     alarm (secs);
                    247:     jval = num;
                    248:   } /* if secs */
                    249: } /* arm */
                    250:
                    251: /* Hmalloc :
                    252:    malloc up what I want, rounded up to *4, and pre-zeroed.  Either succeeds
                    253:    or bails out on its own, so that callers don't have to worry about it. */
                    254: char * Hmalloc (size)
                    255:   unsigned int size;
                    256: {
                    257:   unsigned int s = (size + 4) & 0xfffffffc;    /* 4GB?! */
                    258:   char * p = malloc (s);
                    259:   if (p != NULL)
                    260:     memset (p, 0, s);
                    261:   else
                    262:     bail ("Hmalloc %d failed", s);
                    263:   return (p);
                    264: } /* Hmalloc */
                    265:
                    266: /* findline :
                    267:    find the next newline in a buffer; return inclusive size of that "line",
                    268:    or the entire buffer size, so the caller knows how much to then write().
                    269:    Not distinguishing \n vs \r\n for the nonce; it just works as is... */
                    270: unsigned int findline (buf, siz)
                    271:   char * buf;
                    272:   unsigned int siz;
                    273: {
                    274:   register char * p;
                    275:   register int x;
                    276:   if (! buf)                   /* various sanity checks... */
                    277:     return (0);
                    278:   if (siz > BIGSIZ)
                    279:     return (0);
                    280:   x = siz;
                    281:   for (p = buf; x > 0; x--) {
                    282:     if (*p == '\n') {
                    283:       x = (int) (p - buf);
                    284:       x++;                     /* 'sokay if it points just past the end! */
                    285: Debug (("findline returning %d", x))
                    286:       return (x);
                    287:     }
                    288:     p++;
                    289:   } /* for */
                    290: Debug (("findline returning whole thing: %d", siz))
                    291:   return (siz);
                    292: } /* findline */
                    293:
                    294: /* comparehosts :
                    295:    cross-check the host_poop we have so far against new gethostby*() info,
                    296:    and holler about mismatches.  Perhaps gratuitous, but it can't hurt to
                    297:    point out when someone's DNS is fukt.  Returns 1 if mismatch, in case
                    298:    someone else wants to do something about it. */
                    299: int comparehosts (poop, hp)
                    300:   HINF * poop;
                    301:   struct hostent * hp;
                    302: {
                    303:   errno = 0;
                    304:   h_errno = 0;
                    305: /* The DNS spec is officially case-insensitive, but for those times when you
                    306:    *really* wanna see any and all discrepancies, by all means define this. */
                    307: #ifdef ANAL
                    308:   if (strcmp (poop->name, hp->h_name) != 0) {          /* case-sensitive */
                    309: #else
                    310:   if (strcasecmp (poop->name, hp->h_name) != 0) {      /* normal */
                    311: #endif
                    312:     holler ("DNS fwd/rev mismatch: %s != %s", poop->name, hp->h_name);
                    313:     return (1);
                    314:   }
                    315:   return (0);
                    316: /* ... do we need to do anything over and above that?? */
                    317: } /* comparehosts */
                    318:
                    319: /* gethostpoop :
                    320:    resolve a host 8 ways from sunday; return a new host_poop struct with its
                    321:    info.  The argument can be a name or [ascii] IP address; it will try its
                    322:    damndest to deal with it.  "numeric" governs whether we do any DNS at all,
                    323:    and we also check o_verbose for what's appropriate work to do. */
                    324: HINF * gethostpoop (name, numeric)
                    325:   char * name;
                    326:   USHORT numeric;
                    327: {
                    328:   struct hostent * hostent;
                    329:   struct in_addr iaddr;
                    330:   register HINF * poop = NULL;
                    331:   register int x;
                    332:
                    333: /* I really want to strangle the twit who dreamed up all these sockaddr and
                    334:    hostent abstractions, and then forced them all to be incompatible with
                    335:    each other so you *HAVE* to do all this ridiculous casting back and forth.
                    336:    If that wasn't bad enough, all the doc insists on referring to local ports
                    337:    and addresses as "names", which makes NO sense down at the bare metal.
                    338:
                    339:    What an absolutely horrid paradigm, and to think of all the people who
                    340:    have been wasting significant amounts of time fighting with this stupid
                    341:    deliberate obfuscation over the last 10 years... then again, I like
                    342:    languages wherein a pointer is a pointer, what you put there is your own
                    343:    business, the compiler stays out of your face, and sheep are nervous.
                    344:    Maybe that's why my C code reads like assembler half the time... */
                    345:
                    346: /* If we want to see all the DNS stuff, do the following hair --
1.4     ! deraadt   347:    if inet_aton, do reverse and forward with any warnings; otherwise try
1.1       deraadt   348:    to do forward and reverse with any warnings.  In other words, as long
                    349:    as we're here, do a complete DNS check on these clowns.  Yes, it slows
                    350:    things down a bit for a first run, but once it's cached, who cares? */
                    351:
                    352:   errno = 0;
                    353:   h_errno = 0;
                    354:   if (name)
                    355:     poop = (HINF *) Hmalloc (sizeof (HINF));
                    356:   if (! poop)
                    357:     bail ("gethostpoop fuxored");
1.4     ! deraadt   358:   strlcpy (poop->name, unknown, sizeof(poop->name));   /* preload it */
        !           359:   if (inet_aton (name, &iaddr) == 0) { /* here's the great split: names... */
1.1       deraadt   360:
                    361:     if (numeric)
                    362:       bail ("Can't parse %s as an IP address", name);
                    363:     hostent = gethostbyname (name);
                    364:     if (! hostent)
                    365: /* failure to look up a name is fatal, since we can't do anything with it */
                    366:       bail ("%s: forward host lookup failed: ", name);
1.3       deraadt   367:     strncpy (poop->name, hostent->h_name, MAXHOSTNAMELEN - 1);
                    368:     poop->name[MAXHOSTNAMELEN - 1] = '\0';
1.1       deraadt   369:     for (x = 0; hostent->h_addr_list[x] && (x < 8); x++) {
                    370:       memcpy (&poop->iaddrs[x], hostent->h_addr_list[x], sizeof (IA));
                    371:       strncpy (poop->addrs[x], inet_ntoa (poop->iaddrs[x]),
1.3       deraadt   372:        sizeof (poop->addrs[0])-1);
                    373:       poop->addrs[x][sizeof (poop->addrs[0]) - 1] = '\0';
1.1       deraadt   374:     } /* for x -> addrs, part A */
                    375:     if (! o_verbose)                   /* if we didn't want to see the */
                    376:       return (poop);                   /* inverse stuff, we're done. */
                    377: /* do inverse lookups in separate loop based on our collected forward addrs,
                    378:    since gethostby* tends to crap into the same buffer over and over */
                    379:     for (x = 0; poop->iaddrs[x].s_addr && (x < 8); x++) {
                    380:       hostent = gethostbyaddr ((char *)&poop->iaddrs[x],
                    381:                                sizeof (IA), AF_INET);
                    382:       if ((! hostent) || (! hostent-> h_name))
                    383:        holler ("Warning: inverse host lookup failed for %s: ",
                    384:          poop->addrs[x]);
                    385:       else
                    386:        (void) comparehosts (poop, hostent);
                    387:     } /* for x -> addrs, part B */
                    388:
                    389:   } else {  /* not INADDR_NONE: numeric addresses... */
                    390:     memcpy (poop->iaddrs, &iaddr, sizeof (IA));
1.3       deraadt   391:     strncpy (poop->addrs[0], inet_ntoa (iaddr), sizeof (poop->addrs)-1);
                    392:     poop->addrs[0][sizeof (poop->addrs)-1] = '\0';
1.1       deraadt   393:     if (numeric)                       /* if numeric-only, we're done */
                    394:       return (poop);
                    395:     if (! o_verbose)                   /* likewise if we don't want */
                    396:       return (poop);                   /* the full DNS hair */
                    397:     hostent = gethostbyaddr ((char *) &iaddr, sizeof (IA), AF_INET);
                    398: /* numeric or not, failure to look up a PTR is *not* considered fatal */
                    399:     if (! hostent)
                    400:        holler ("%s: inverse host lookup failed: ", name);
                    401:     else {
1.3       deraadt   402:        strncpy (poop->name, hostent->h_name, MAXHOSTNAMELEN - 1);
                    403:        poop->name[MAXHOSTNAMELEN-1] = '\0';
1.1       deraadt   404:        hostent = gethostbyname (poop->name);
                    405:        if ((! hostent) || (! hostent->h_addr_list[0]))
                    406:          holler ("Warning: forward host lookup failed for %s: ",
                    407:                poop->name);
                    408:        else
                    409:          (void) comparehosts (poop, hostent);
                    410:     } /* if hostent */
                    411:   } /* INADDR_NONE Great Split */
                    412:
                    413: /* whatever-all went down previously, we should now have a host_poop struct
                    414:    with at least one IP address in it. */
                    415:   h_errno = 0;
                    416:   return (poop);
                    417: } /* gethostpoop */
                    418:
                    419: /* getportpoop :
                    420:    Same general idea as gethostpoop -- look up a port in /etc/services, fill
                    421:    in global port_poop, but return the actual port *number*.  Pass ONE of:
                    422:        pstring to resolve stuff like "23" or "exec";
                    423:        pnum to reverse-resolve something that's already a number.
                    424:    If o_nflag is on, fill in what we can but skip the getservby??? stuff.
                    425:    Might as well have consistent behavior here, and it *is* faster. */
                    426: USHORT getportpoop (pstring, pnum)
                    427:   char * pstring;
                    428:   unsigned int pnum;
                    429: {
                    430:   struct servent * servent;
                    431:   register int x;
                    432:   register int y;
                    433:   char * whichp = p_tcp;
                    434:   if (o_udpmode)
                    435:     whichp = p_udp;
                    436:   portpoop->name[0] = '?';             /* fast preload */
                    437:   portpoop->name[1] = '\0';
                    438:
                    439: /* case 1: reverse-lookup of a number; placed first since this case is much
                    440:    more frequent if we're scanning */
                    441:   if (pnum) {
                    442:     if (pstring)                       /* one or the other, pleeze */
                    443:       return (0);
                    444:     x = pnum;
                    445:     if (o_nflag)                       /* go faster, skip getservbyblah */
                    446:       goto gp_finish;
                    447:     y = htons (x);                     /* gotta do this -- see Fig.1 below */
                    448:     servent = getservbyport (y, whichp);
                    449:     if (servent) {
                    450:       y = ntohs (servent->s_port);
                    451:       if (x != y)                      /* "never happen" */
                    452:        holler ("Warning: port-bynum mismatch, %d != %d", x, y);
1.3       deraadt   453:       strncpy (portpoop->name, servent->s_name, sizeof (portpoop->name)-1);
                    454:       portpoop->name[sizeof (portpoop->name)-1] = '\0';
1.1       deraadt   455:     } /* if servent */
                    456:     goto gp_finish;
                    457:   } /* if pnum */
                    458:
                    459: /* case 2: resolve a string, but we still give preference to numbers instead
                    460:    of trying to resolve conflicts.  None of the entries in *my* extensive
                    461:    /etc/services begins with a digit, so this should "always work" unless
                    462:    you're at 3com and have some company-internal services defined... */
                    463:   if (pstring) {
                    464:     if (pnum)                          /* one or the other, pleeze */
                    465:       return (0);
                    466:     x = atoi (pstring);
                    467:     if (x)
                    468:       return (getportpoop (NULL, x));  /* recurse for numeric-string-arg */
                    469:     if (o_nflag)                       /* can't use names! */
                    470:       return (0);
                    471:     servent = getservbyname (pstring, whichp);
                    472:     if (servent) {
1.3       deraadt   473:       strncpy (portpoop->name, servent->s_name, sizeof (portpoop->name)-1);
                    474:       portpoop->name[sizeof (portpoop->name)-1] = '\0';
1.1       deraadt   475:       x = ntohs (servent->s_port);
                    476:       goto gp_finish;
                    477:     } /* if servent */
                    478:   } /* if pstring */
                    479:
                    480:   return (0);                          /* catches any problems so far */
                    481:
                    482: /* Obligatory netdb.h-inspired rant: servent.s_port is supposed to be an int.
                    483:    Despite this, we still have to treat it as a short when copying it around.
                    484:    Not only that, but we have to convert it *back* into net order for
                    485:    getservbyport to work.  Manpages generally aren't clear on all this, but
                    486:    there are plenty of examples in which it is just quietly done.  More BSD
                    487:    lossage... since everything getserv* ever deals with is local to our own
                    488:    host, why bother with all this network-order/host-order crap at all?!
                    489:    That should be saved for when we want to actually plug the port[s] into
                    490:    some real network calls -- and guess what, we have to *re*-convert at that
                    491:    point as well.  Fuckheads. */
                    492:
                    493: gp_finish:
                    494: /* Fall here whether or not we have a valid servent at this point, with
                    495:    x containing our [host-order and therefore useful, dammit] port number */
                    496:   sprintf (portpoop->anum, "%d", x);   /* always load any numeric specs! */
                    497:   portpoop->num = (x & 0xffff);                /* ushort, remember... */
                    498:   return (portpoop->num);
                    499: } /* getportpoop */
                    500:
                    501: /* nextport :
                    502:    Come up with the next port to try, be it random or whatever.  "block" is
                    503:    a ptr to randports array, whose bytes [so far] carry these meanings:
                    504:        0       ignore
                    505:        1       to be tested
                    506:        2       tested [which is set as we find them here]
                    507:    returns a USHORT random port, or 0 if all the t-b-t ones are used up. */
                    508: USHORT nextport (block)
                    509:   char * block;
                    510: {
                    511:   register unsigned int x;
                    512:   register unsigned int y;
                    513:
                    514:   y = 70000;                   /* high safety count for rnd-tries */
                    515:   while (y > 0) {
                    516:     x = (RAND() & 0xffff);
                    517:     if (block[x] == 1) {       /* try to find a not-done one... */
                    518:       block[x] = 2;
                    519:       break;
                    520:     }
                    521:     x = 0;                     /* bummer. */
                    522:     y--;
                    523:   } /* while y */
                    524:   if (x)
                    525:     return (x);
                    526:
                    527:   y = 65535;                   /* no random one, try linear downsearch */
                    528:   while (y > 0) {              /* if they're all used, we *must* be sure! */
                    529:     if (block[y] == 1) {
                    530:       block[y] = 2;
                    531:       break;
                    532:     }
                    533:     y--;
                    534:   } /* while y */
                    535:   if (y)
                    536:     return (y);                        /* at least one left */
                    537:
                    538:   return (0);                  /* no more left! */
                    539: } /* nextport */
                    540:
                    541: /* loadports :
                    542:    set "to be tested" indications in BLOCK, from LO to HI.  Almost too small
                    543:    to be a separate routine, but makes main() a little cleaner... */
                    544: void loadports (block, lo, hi)
                    545:   char * block;
                    546:   USHORT lo;
                    547:   USHORT hi;
                    548: {
                    549:   USHORT x;
                    550:
                    551:   if (! block)
                    552:     bail ("loadports: no block?!");
                    553:   if ((! lo) || (! hi))
                    554:     bail ("loadports: bogus values %d, %d", lo, hi);
                    555:   x = hi;
                    556:   while (lo <= x) {
                    557:     block[x] = 1;
                    558:     x--;
                    559:   }
                    560: } /* loadports */
                    561:
                    562: #ifdef GAPING_SECURITY_HOLE
                    563: char * pr00gie = NULL;                 /* global ptr to -e arg */
                    564:
                    565: /* doexec :
                    566:    fiddle all the file descriptors around, and hand off to another prog.  Sort
                    567:    of like a one-off "poor man's inetd".  This is the only section of code
                    568:    that would be security-critical, which is why it's ifdefed out by default.
                    569:    Use at your own hairy risk; if you leave shells lying around behind open
                    570:    listening ports you deserve to lose!! */
                    571: doexec (fd)
                    572:   int fd;
                    573: {
                    574:   register char * p;
                    575:
                    576:   dup2 (fd, 0);                                /* the precise order of fiddlage */
                    577:   close (fd);                          /* is apparently crucial; this is */
                    578:   dup2 (0, 1);                         /* swiped directly out of "inetd". */
                    579:   dup2 (0, 2);
                    580:   p = strrchr (pr00gie, '/');          /* shorter argv[0] */
                    581:   if (p)
                    582:     p++;
                    583:   else
                    584:     p = pr00gie;
                    585: Debug (("gonna exec %s as %s...", pr00gie, p))
                    586:   execl (pr00gie, p, NULL);
                    587:   bail ("exec %s failed", pr00gie);    /* this gets sent out.  Hmm... */
                    588: } /* doexec */
                    589: #endif /* GAPING_SECURITY_HOLE */
                    590:
                    591: /* doconnect :
                    592:    do all the socket stuff, and return an fd for one of
                    593:        an open outbound TCP connection
                    594:        a UDP stub-socket thingie
                    595:    with appropriate socket options set up if we wanted source-routing, or
                    596:        an unconnected TCP or UDP socket to listen on.
                    597:    Examines various global o_blah flags to figure out what-all to do. */
                    598: int doconnect (rad, rp, lad, lp)
                    599:   IA * rad;
                    600:   USHORT rp;
                    601:   IA * lad;
                    602:   USHORT lp;
                    603: {
                    604:   register int nnetfd;
                    605:   register int rr;
                    606:   int x, y;
                    607:   errno = 0;
                    608:
                    609: /* grab a socket; set opts */
                    610: newskt:
                    611:   if (o_udpmode)
                    612:     nnetfd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
                    613:   else
                    614:     nnetfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
                    615:   if (nnetfd < 0)
                    616:     bail ("Can't get socket");
                    617:   if (nnetfd == 0)             /* if stdin was closed this might *be* 0, */
                    618:     goto newskt;               /* so grab another.  See text for why... */
                    619:   x = 1;
                    620:   rr = setsockopt (nnetfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x));
                    621:   if (rr == -1)
                    622:     holler ("nnetfd reuseaddr failed");                /* ??? */
                    623: #ifdef SO_REUSEPORT    /* doesnt exist everywhere... */
                    624:   rr = setsockopt (nnetfd, SOL_SOCKET, SO_REUSEPORT, &x, sizeof (x));
                    625:   if (rr == -1)
                    626:     holler ("nnetfd reuseport failed");                /* ??? */
                    627: #endif
                    628: #if 0
                    629: /* If you want to screw with RCVBUF/SNDBUF, do it here.  Liudvikas Bukys at
                    630:    Rochester sent this example, which would involve YET MORE options and is
                    631:    just archived here in case you want to mess with it.  o_xxxbuf are global
                    632:    integers set in main() getopt loop, and check for rr == 0 afterward. */
                    633:   rr = setsockopt(nnetfd, SOL_SOCKET, SO_RCVBUF, &o_rcvbuf, sizeof o_rcvbuf);
                    634:   rr = setsockopt(nnetfd, SOL_SOCKET, SO_SNDBUF, &o_sndbuf, sizeof o_sndbuf);
                    635: #endif
                    636:
                    637:   /* fill in all the right sockaddr crud */
                    638:     lclend->sin_family = AF_INET;
                    639:
                    640: /* fill in all the right sockaddr crud */
                    641:   lclend->sin_family = AF_INET;
                    642:   remend->sin_family = AF_INET;
                    643:
                    644: /* if lad/lp, do appropriate binding */
                    645:   if (lad)
                    646:     memcpy (&lclend->sin_addr.s_addr, lad, sizeof (IA));
                    647:   if (lp)
                    648:     lclend->sin_port = htons (lp);
                    649:   rr = 0;
                    650:   if (lad || lp) {
                    651:     x = (int) lp;
                    652: /* try a few times for the local bind, a la ftp-data-port... */
                    653:     for (y = 4; y > 0; y--) {
                    654:       rr = bind (nnetfd, (SA *)lclend, sizeof (SA));
                    655:       if (rr == 0)
                    656:        break;
                    657:       if (errno != EADDRINUSE)
                    658:        break;
                    659:       else {
                    660:        holler ("retrying local %s:%d", inet_ntoa (lclend->sin_addr), lp);
                    661:        sleep (2);
                    662:        errno = 0;                      /* clear from sleep */
                    663:       } /* if EADDRINUSE */
                    664:     } /* for y counter */
                    665:   } /* if lad or lp */
                    666:   if (rr)
                    667:     bail ("Can't grab %s:%d with bind",
                    668:        inet_ntoa(lclend->sin_addr), lp);
                    669:
                    670:   if (o_listen)
                    671:     return (nnetfd);                   /* thanks, that's all for today */
                    672:
                    673:   memcpy (&remend->sin_addr.s_addr, rad, sizeof (IA));
                    674:   remend->sin_port = htons (rp);
                    675:
                    676: /* rough format of LSRR option and explanation of weirdness.
                    677: Option comes after IP-hdr dest addr in packet, padded to *4, and ihl > 5.
                    678: IHL is multiples of 4, i.e. real len = ip_hl << 2.
                    679:        type 131        1       ; 0x83: copied, option class 0, number 3
                    680:        len             1       ; of *whole* option!
                    681:        pointer         1       ; nxt-hop-addr; 1-relative, not 0-relative
                    682:        addrlist...     var     ; 4 bytes per hop-addr
                    683:        pad-to-32       var     ; ones, i.e. "NOP"
                    684:
                    685: If we want to route A -> B via hops C and D, we must add C, D, *and* B to the
                    686: options list.  Why?  Because when we hand the kernel A -> B with list C, D, B
                    687: the "send shuffle" inside the kernel changes it into A -> C with list D, B and
                    688: the outbound packet gets sent to C.  If B wasn't also in the hops list, the
                    689: final destination would have been lost at this point.
                    690:
                    691: When C gets the packet, it changes it to A -> D with list C', B where C' is
                    692: the interface address that C used to forward the packet.  This "records" the
                    693: route hop from B's point of view, i.e. which address points "toward" B.  This
                    694: is to make B better able to return the packets.  The pointer gets bumped by 4,
                    695: so that D does the right thing instead of trying to forward back to C.
                    696:
                    697: When B finally gets the packet, it sees that the pointer is at the end of the
                    698: LSRR list and is thus "completed".  B will then try to use the packet instead
                    699: of forwarding it, i.e. deliver it up to some application.
                    700:
                    701: Note that by moving the pointer yourself, you could send the traffic directly
                    702: to B but have it return via your preconstructed source-route.  Playing with
                    703: this and watching "tcpdump -v" is the best way to understand what's going on.
                    704:
                    705: Only works for TCP in BSD-flavor kernels.  UDP is a loss; udp_input calls
                    706: stripoptions() early on, and the code to save the srcrt is notdef'ed.
                    707: Linux is also still a loss at 1.3.x it looks like; the lsrr code is { }...
                    708: */
                    709:
                    710: /* if any -g arguments were given, set up source-routing.  We hit this after
                    711:    the gates are all looked up and ready to rock, any -G pointer is set,
                    712:    and gatesidx is now the *number* of hops */
                    713:   if (gatesidx) {              /* if we wanted any srcrt hops ... */
                    714: /* don't even bother compiling if we can't do IP options here! */
                    715: #ifdef IP_OPTIONS
                    716:     if (! optbuf) {            /* and don't already *have* a srcrt set */
                    717:       char * opp;              /* then do all this setup hair */
                    718:       optbuf = Hmalloc (48);
                    719:       opp = optbuf;
                    720:       *opp++ = IPOPT_LSRR;                                     /* option */
                    721:       *opp++ = (char)
                    722:        (((gatesidx + 1) * sizeof (IA)) + 3) & 0xff;            /* length */
                    723:       *opp++ = gatesptr;                                       /* pointer */
                    724: /* opp now points at first hop addr -- insert the intermediate gateways */
                    725:       for ( x = 0; x < gatesidx; x++) {
                    726:        memcpy (opp, gates[x]->iaddrs, sizeof (IA));
                    727:        opp += sizeof (IA);
                    728:       }
                    729: /* and tack the final destination on the end [needed!] */
                    730:       memcpy (opp, rad, sizeof (IA));
                    731:       opp += sizeof (IA);
                    732:       *opp = IPOPT_NOP;                        /* alignment filler */
                    733:     } /* if empty optbuf */
                    734: /* calculate length of whole option mess, which is (3 + [hops] + [final] + 1),
                    735:    and apply it [have to do this every time through, of course] */
                    736:     x = ((gatesidx + 1) * sizeof (IA)) + 4;
                    737:     rr = setsockopt (nnetfd, IPPROTO_IP, IP_OPTIONS, optbuf, x);
                    738:     if (rr == -1)
                    739:       bail ("srcrt setsockopt fuxored");
                    740: #else /* IP_OPTIONS */
                    741:     holler ("Warning: source routing unavailable on this machine, ignoring");
                    742: #endif /* IP_OPTIONS*/
                    743:   } /* if gatesidx */
                    744:
                    745: /* wrap connect inside a timer, and hit it */
                    746:   arm (1, o_wait);
                    747:   if (setjmp (jbuf) == 0) {
                    748:     rr = connect (nnetfd, (SA *)remend, sizeof (SA));
                    749:   } else {                             /* setjmp: connect failed... */
                    750:     rr = -1;
                    751:     errno = ETIMEDOUT;                 /* fake it */
                    752:   }
                    753:   arm (0, 0);
                    754:   if (rr == 0)
                    755:     return (nnetfd);
                    756:   close (nnetfd);                      /* clean up junked socket FD!! */
                    757:   return (-1);
                    758: } /* doconnect */
                    759:
                    760: /* dolisten :
                    761:    just like doconnect, and in fact calls a hunk of doconnect, but listens for
                    762:    incoming and returns an open connection *from* someplace.  If we were
                    763:    given host/port args, any connections from elsewhere are rejected.  This
                    764:    in conjunction with local-address binding should limit things nicely... */
                    765: int dolisten (rad, rp, lad, lp)
                    766:   IA * rad;
                    767:   USHORT rp;
                    768:   IA * lad;
                    769:   USHORT lp;
                    770: {
                    771:   register int nnetfd;
                    772:   register int rr;
                    773:   HINF * whozis = NULL;
                    774:   int x;
                    775:   char * cp;
                    776:   USHORT z;
                    777:   errno = 0;
                    778:
                    779: /* Pass everything off to doconnect, who in o_listen mode just gets a socket */
                    780:   nnetfd = doconnect (rad, rp, lad, lp);
                    781:   if (nnetfd <= 0)
                    782:     return (-1);
                    783:   if (o_udpmode) {                     /* apparently UDP can listen ON */
                    784:     if (! lp)                          /* "port 0",  but that's not useful */
                    785:       bail ("UDP listen needs -p arg");
                    786:   } else {
                    787:     rr = listen (nnetfd, 1);           /* gotta listen() before we can get */
                    788:     if (rr < 0)                                /* our local random port.  sheesh. */
                    789:       bail ("local listen fuxored");
                    790:   }
                    791:
                    792: /* Various things that follow temporarily trash bigbuf_net, which might contain
                    793:    a copy of any recvfrom()ed packet, but we'll read() another copy later. */
                    794:
                    795: /* I can't believe I have to do all this to get my own goddamn bound address
                    796:    and port number.  It should just get filled in during bind() or something.
                    797:    All this is only useful if we didn't say -p for listening, since if we
                    798:    said -p we *know* what port we're listening on.  At any rate we won't bother
                    799:    with it all unless we wanted to see it, although listening quietly on a
                    800:    random unknown port is probably not very useful without "netstat". */
                    801:   if (o_verbose) {
                    802:     x = sizeof (SA);           /* how 'bout getsockNUM instead, pinheads?! */
                    803:     rr = getsockname (nnetfd, (SA *) lclend, &x);
                    804:     if (rr < 0)
                    805:       holler ("local getsockname failed");
                    806:     strcpy (bigbuf_net, "listening on [");     /* buffer reuse... */
                    807:     if (lclend->sin_addr.s_addr)
                    808:       strcat (bigbuf_net, inet_ntoa (lclend->sin_addr));
                    809:     else
                    810:       strcat (bigbuf_net, "any");
                    811:     strcat (bigbuf_net, "] %d ...");
                    812:     z = ntohs (lclend->sin_port);
                    813:     holler (bigbuf_net, z);
                    814:   } /* verbose -- whew!! */
                    815:
                    816: /* UDP is a speeeeecial case -- we have to do I/O *and* get the calling
                    817:    party's particulars all at once, listen() and accept() don't apply.
                    818:    At least in the BSD universe, however, recvfrom/PEEK is enough to tell
                    819:    us something came in, and we can set things up so straight read/write
                    820:    actually does work after all.  Yow.  YMMV on strange platforms!  */
                    821:   if (o_udpmode) {
                    822:     x = sizeof (SA);           /* retval for recvfrom */
                    823:     arm (2, o_wait);           /* might as well timeout this, too */
                    824:     if (setjmp (jbuf) == 0) {  /* do timeout for initial connect */
                    825:       rr = recvfrom            /* and here we block... */
                    826:        (nnetfd, bigbuf_net, BIGSIZ, MSG_PEEK, (SA *) remend, &x);
                    827: Debug (("dolisten/recvfrom ding, rr = %d, netbuf %s ", rr, bigbuf_net))
                    828:     } else
                    829:       goto dol_tmo;            /* timeout */
                    830:     arm (0, 0);
                    831: /* I'm not completely clear on how this works -- BSD seems to make UDP
                    832:    just magically work in a connect()ed context, but we'll undoubtedly run
                    833:    into systems this deal doesn't work on.  For now, we apparently have to
                    834:    issue a connect() on our just-tickled socket so we can write() back.
                    835:    Again, why the fuck doesn't it just get filled in and taken care of?!
                    836:    This hack is anything but optimal.  Basically, if you want your listener
                    837:    to also be able to send data back, you need this connect() line, which
                    838:    also has the side effect that now anything from a different source or even a
                    839:    different port on the other end won't show up and will cause ICMP errors.
                    840:    I guess that's what they meant by "connect".
                    841:    Let's try to remember what the "U" is *really* for, eh? */
                    842:     rr = connect (nnetfd, (SA *)remend, sizeof (SA));
                    843:     goto whoisit;
                    844:   } /* o_udpmode */
                    845:
                    846: /* fall here for TCP */
                    847:   x = sizeof (SA);             /* retval for accept */
                    848:   arm (2, o_wait);             /* wrap this in a timer, too; 0 = forever */
                    849:   if (setjmp (jbuf) == 0) {
                    850:     rr = accept (nnetfd, (SA *)remend, &x);
                    851:   } else
                    852:     goto dol_tmo;              /* timeout */
                    853:   arm (0, 0);
                    854:   close (nnetfd);              /* dump the old socket */
                    855:   nnetfd = rr;                 /* here's our new one */
                    856:
                    857: whoisit:
                    858:   if (rr < 0)
                    859:     goto dol_err;              /* bail out if any errors so far */
                    860:
                    861: /* If we can, look for any IP options.  Useful for testing the receiving end of
                    862:    such things, and is a good exercise in dealing with it.  We do this before
                    863:    the connect message, to ensure that the connect msg is uniformly the LAST
                    864:    thing to emerge after all the intervening crud.  Doesn't work for UDP on
                    865:    any machines I've tested, but feel free to surprise me. */
                    866: #ifdef IP_OPTIONS
                    867:   if (! o_verbose)                     /* if we wont see it, we dont care */
                    868:     goto dol_noop;
                    869:   optbuf = Hmalloc (40);
                    870:   x = 40;
                    871:   rr = getsockopt (nnetfd, IPPROTO_IP, IP_OPTIONS, optbuf, &x);
                    872:   if (rr < 0)
                    873:     holler ("getsockopt failed");
                    874: Debug (("ipoptions ret len %d", x))
                    875:   if (x) {                             /* we've got options, lessee em... */
                    876:     unsigned char * q = (unsigned char *) optbuf;
                    877:     char * p = bigbuf_net;             /* local variables, yuk! */
                    878:     char * pp = &bigbuf_net[128];      /* get random space farther out... */
                    879:     memset (bigbuf_net, 0, 256);       /* clear it all first */
                    880:     while (x > 0) {
                    881:        sprintf (pp, "%2.2x ", *q);     /* clumsy, but works: turn into hex */
                    882:        strcat (p, pp);                 /* and build the final string */
                    883:        q++; p++;
                    884:        x--;
                    885:     }
                    886:     holler ("IP options: %s", bigbuf_net);
                    887:   } /* if x, i.e. any options */
                    888: dol_noop:
                    889: #endif /* IP_OPTIONS */
                    890:
                    891: /* find out what address the connection was *to* on our end, in case we're
                    892:    doing a listen-on-any on a multihomed machine.  This allows one to
                    893:    offer different services via different alias addresses, such as the
                    894:    "virtual web site" hack. */
                    895:   memset (bigbuf_net, 0, 64);
                    896:   cp = &bigbuf_net[32];
                    897:   x = sizeof (SA);
                    898:   rr = getsockname (nnetfd, (SA *) lclend, &x);
                    899:   if (rr < 0)
                    900:     holler ("post-rcv getsockname failed");
                    901:   strcpy (cp, inet_ntoa (lclend->sin_addr));
                    902:
                    903: /* now check out who it is.  We don't care about mismatched DNS names here,
                    904:    but any ADDR and PORT we specified had better fucking well match the caller.
                    905:    Converting from addr to inet_ntoa and back again is a bit of a kludge, but
                    906:    gethostpoop wants a string and there's much gnarlier code out there already,
                    907:    so I don't feel bad.
                    908:    The *real* question is why BFD sockets wasn't designed to allow listens for
                    909:    connections *from* specific hosts/ports, instead of requiring the caller to
                    910:    accept the connection and then reject undesireable ones by closing.  In
                    911:    other words, we need a TCP MSG_PEEK. */
                    912:   z = ntohs (remend->sin_port);
                    913:   strcpy (bigbuf_net, inet_ntoa (remend->sin_addr));
                    914:   whozis = gethostpoop (bigbuf_net, o_nflag);
                    915:   errno = 0;
                    916:   x = 0;                               /* use as a flag... */
                    917:   if (rad)     /* xxx: fix to go down the *list* if we have one? */
                    918:     if (memcmp (rad, whozis->iaddrs, sizeof (SA)))
                    919:       x = 1;
                    920:   if (rp)
                    921:     if (z != rp)
                    922:       x = 1;
                    923:   if (x)                                       /* guilty! */
                    924:     bail ("invalid connection to [%s] from %s [%s] %d",
                    925:        cp, whozis->name, whozis->addrs[0], z);
                    926:   holler ("connect to [%s] from %s [%s] %d",           /* oh, you're okay.. */
                    927:        cp, whozis->name, whozis->addrs[0], z);
                    928:   return (nnetfd);                             /* open! */
                    929:
                    930: dol_tmo:
                    931:   errno = ETIMEDOUT;                   /* fake it */
                    932: dol_err:
                    933:   close (nnetfd);
                    934:   return (-1);
                    935: } /* dolisten */
                    936:
                    937: /* udptest :
                    938:    fire a couple of packets at a UDP target port, just to see if it's really
                    939:    there.  On BSD kernels, ICMP host/port-unreachable errors get delivered to
                    940:    our socket as ECONNREFUSED write errors.  On SV kernels, we lose; we'll have
                    941:    to collect and analyze raw ICMP ourselves a la satan's probe_udp_ports
                    942:    backend.  Guess where one could swipe the appropriate code from...
                    943:
                    944:    Use the time delay between writes if given, otherwise use the "tcp ping"
                    945:    trick for getting the RTT.  [I got that idea from pluvius, and warped it.]
                    946:    Return either the original fd, or clean up and return -1. */
                    947: udptest (fd, where)
                    948:   int fd;
                    949:   IA * where;
                    950: {
                    951:   register int rr;
                    952:
                    953:   rr = write (fd, bigbuf_in, 1);
                    954:   if (rr != 1)
                    955:     holler ("udptest first write failed?! errno %d", errno);
                    956:   if (o_wait)
                    957:     sleep (o_wait);
                    958:   else {
                    959: /* use the tcp-ping trick: try connecting to a normally refused port, which
                    960:    causes us to block for the time that SYN gets there and RST gets back.
                    961:    Not completely reliable, but it *does* mostly work. */
                    962:     o_udpmode = 0;                     /* so doconnect does TCP this time */
                    963: /* Set a temporary connect timeout, so packet filtration doesnt cause
                    964:    us to hang forever, and hit it */
                    965:     o_wait = 5;                                /* enough that we'll notice?? */
                    966:     rr = doconnect (where, SLEAZE_PORT, 0, 0);
                    967:     if (rr > 0)
                    968:       close (rr);                      /* in case it *did* open */
                    969:     o_wait = 0;                                /* reset it */
                    970:     o_udpmode++;                       /* we *are* still doing UDP, right? */
                    971:   } /* if o_wait */
                    972:   errno = 0;                           /* clear from sleep */
                    973:   rr = write (fd, bigbuf_in, 1);
                    974:   if (rr == 1)                         /* if write error, no UDP listener */
                    975:     return (fd);
                    976:   close (fd);                          /* use it or lose it! */
                    977:   return (-1);
                    978: } /* udptest */
                    979:
                    980: /* oprint :
                    981:    Hexdump bytes shoveled either way to a running logfile, in the format:
                    982: D offset       -  - - - --- 16 bytes --- - - -  -     # .... ascii .....
                    983:    where "which" sets the direction indicator, D:
                    984:        0 -- sent to network, or ">"
                    985:        1 -- rcvd and printed to stdout, or "<"
                    986:    and "buf" and "n" are data-block and length.  If the current block generates
                    987:    a partial line, so be it; we *want* that lockstep indication of who sent
                    988:    what when.  Adapted from dgaudet's original example -- but must be ripping
                    989:    *fast*, since we don't want to be too disk-bound... */
                    990: void oprint (which, buf, n)
                    991:   int which;
                    992:   char * buf;
                    993:   int n;
                    994: {
                    995:   int bc;                      /* in buffer count */
                    996:   int obc;                     /* current "global" offset */
                    997:   int soc;                     /* stage write count */
                    998:   register unsigned char * p;  /* main buf ptr; m.b. unsigned here */
                    999:   register unsigned char * op; /* out hexdump ptr */
                   1000:   register unsigned char * a;  /* out asc-dump ptr */
                   1001:   register int x;
                   1002:   register unsigned int y;
                   1003:
                   1004:   if (! ofd)
                   1005:     bail ("oprint called with no open fd?!");
                   1006:   if (n == 0)
                   1007:     return;
                   1008:
                   1009:   op = stage;
                   1010:   if (which) {
                   1011:     *op = '<';
                   1012:     obc = wrote_out;           /* use the globals! */
                   1013:   } else {
                   1014:     *op = '>';
                   1015:     obc = wrote_net;
                   1016:   }
                   1017:   op++;                                /* preload "direction" */
                   1018:   *op = ' ';
                   1019:   p = (unsigned char *) buf;
                   1020:   bc = n;
                   1021:   stage[59] = '#';             /* preload separator */
                   1022:   stage[60] = ' ';
                   1023:
                   1024:   while (bc) {                 /* for chunk-o-data ... */
                   1025:     x = 16;
                   1026:     soc = 78;                  /* len of whole formatted line */
                   1027:     if (bc < x) {
                   1028:       soc = soc - 16 + bc;     /* fiddle for however much is left */
                   1029:       x = (bc * 3) + 11;       /* 2 digits + space per, after D & offset */
                   1030:       op = &stage[x];
                   1031:       x = 16 - bc;
                   1032:       while (x) {
                   1033:        *op++ = ' ';            /* preload filler spaces */
                   1034:        *op++ = ' ';
                   1035:        *op++ = ' ';
                   1036:        x--;
                   1037:       }
                   1038:       x = bc;                  /* re-fix current linecount */
                   1039:     } /* if bc < x */
                   1040:
                   1041:     bc -= x;                   /* fix wrt current line size */
                   1042:     sprintf (&stage[2], "%8.8x ", obc);                /* xxx: still slow? */
                   1043:     obc += x;                  /* fix current offset */
                   1044:     op = &stage[11];           /* where hex starts */
                   1045:     a = &stage[61];            /* where ascii starts */
                   1046:
                   1047:     while (x) {                        /* for line of dump, however long ... */
                   1048:       y = (int)(*p >> 4);      /* hi half */
                   1049:       *op = hexnibs[y];
                   1050:       op++;
                   1051:       y = (int)(*p & 0x0f);    /* lo half */
                   1052:       *op = hexnibs[y];
                   1053:       op++;
                   1054:       *op = ' ';
                   1055:       op++;
                   1056:       if ((*p > 31) && (*p < 127))
                   1057:        *a = *p;                /* printing */
                   1058:       else
                   1059:        *a = '.';               /* nonprinting, loose def */
                   1060:       a++;
                   1061:       p++;
                   1062:       x--;
                   1063:     } /* while x */
                   1064:     *a = '\n';                 /* finish the line */
                   1065:     x = write (ofd, stage, soc);
                   1066:     if (x < 0)
                   1067:       bail ("ofd write err");
                   1068:   } /* while bc */
                   1069: } /* oprint */
                   1070:
                   1071: #ifdef TELNET
                   1072: USHORT o_tn = 0;               /* global -t option */
                   1073:
                   1074: /* atelnet :
                   1075:    Answer anything that looks like telnet negotiation with don't/won't.
                   1076:    This doesn't modify any data buffers, update the global output count,
                   1077:    or show up in a hexdump -- it just shits into the outgoing stream.
                   1078:    Idea and codebase from Mudge@l0pht.com. */
                   1079: void atelnet (buf, size)
                   1080:   unsigned char * buf;         /* has to be unsigned here! */
                   1081:   unsigned int size;
                   1082: {
                   1083:   static unsigned char obuf [4];  /* tiny thing to build responses into */
                   1084:   register int x;
                   1085:   register unsigned char y;
                   1086:   register unsigned char * p;
                   1087:
                   1088:   y = 0;
                   1089:   p = buf;
                   1090:   x = size;
                   1091:   while (x > 0) {
                   1092:     if (*p != 255)                     /* IAC? */
                   1093:       goto notiac;
                   1094:     obuf[0] = 255;
                   1095:     p++; x--;
                   1096:     if ((*p == 251) || (*p == 252))    /* WILL or WONT */
                   1097:       y = 254;                         /* -> DONT */
                   1098:     if ((*p == 253) || (*p == 254))    /* DO or DONT */
                   1099:       y = 252;                         /* -> WONT */
                   1100:     if (y) {
                   1101:       obuf[1] = y;
                   1102:       p++; x--;
                   1103:       obuf[2] = *p;                    /* copy actual option byte */
                   1104:       (void) write (netfd, obuf, 3);
                   1105: /* if one wanted to bump wrote_net or do a hexdump line, here's the place */
                   1106:       y = 0;
                   1107:     } /* if y */
                   1108: notiac:
                   1109:     p++; x--;
                   1110:   } /* while x */
                   1111: } /* atelnet */
                   1112: #endif /* TELNET */
                   1113:
                   1114: /* readwrite :
                   1115:    handle stdin/stdout/network I/O.  Bwahaha!! -- the select loop from hell.
                   1116:    In this instance, return what might become our exit status. */
                   1117: int readwrite (fd)
                   1118:   int fd;
                   1119: {
                   1120:   register int rr;
                   1121:   register char * zp;          /* stdin buf ptr */
                   1122:   register char * np;          /* net-in buf ptr */
                   1123:   unsigned int rzleft;
                   1124:   unsigned int rnleft;
                   1125:   USHORT netretry;             /* net-read retry counter */
                   1126:   USHORT wretry;               /* net-write sanity counter */
                   1127:   USHORT wfirst;               /* one-shot flag to skip first net read */
                   1128:
                   1129: /* if you don't have all this FD_* macro hair in sys/types.h, you'll have to
                   1130:    either find it or do your own bit-bashing: *ding1 |= (1 << fd), etc... */
                   1131:   if (fd > FD_SETSIZE) {
                   1132:     holler ("Preposterous fd value %d", fd);
                   1133:     return (1);
                   1134:   }
                   1135:   FD_SET (fd, ding1);          /* global: the net is open */
                   1136:   netretry = 2;
                   1137:   wfirst = 0;
                   1138:   rzleft = rnleft = 0;
                   1139:   if (insaved) {
                   1140:     rzleft = insaved;          /* preload multi-mode fakeouts */
                   1141:     zp = bigbuf_in;
                   1142:     wfirst = 1;
                   1143:     if (Single)                        /* if not scanning, this is a one-off first */
                   1144:       insaved = 0;             /* buffer left over from argv construction, */
                   1145:     else {
                   1146:       FD_CLR (0, ding1);       /* OR we've already got our repeat chunk, */
                   1147:       close (0);               /* so we won't need any more stdin */
                   1148:     } /* Single */
                   1149:   } /* insaved */
                   1150:   if (o_interval)
                   1151:     sleep (o_interval);                /* pause *before* sending stuff, too */
                   1152:   errno = 0;                   /* clear from sleep, close, whatever */
                   1153:
                   1154: /* and now the big ol' select shoveling loop ... */
                   1155:   while (FD_ISSET (fd, ding1)) {       /* i.e. till the *net* closes! */
                   1156:     wretry = 8200;                     /* more than we'll ever hafta write */
                   1157:     if (wfirst) {                      /* any saved stdin buffer? */
                   1158:       wfirst = 0;                      /* clear flag for the duration */
                   1159:       goto shovel;                     /* and go handle it first */
                   1160:     }
                   1161:     *ding2 = *ding1;                   /* FD_COPY ain't portable... */
                   1162: /* some systems, notably linux, crap into their select timers on return, so
                   1163:    we create a expendable copy and give *that* to select.  *Fuck* me ... */
                   1164:     if (timer1)
                   1165:       memcpy (timer2, timer1, sizeof (struct timeval));
                   1166:     rr = select (16, ding2, 0, 0, timer2);     /* here it is, kiddies */
                   1167:     if (rr < 0) {
                   1168:        if (errno != EINTR) {           /* might have gotten ^Zed, etc ?*/
                   1169:          holler ("select fuxored");
                   1170:          close (fd);
                   1171:          return (1);
                   1172:        }
                   1173:     } /* select fuckup */
                   1174: /* if we have a timeout AND stdin is closed AND we haven't heard anything
                   1175:    from the net during that time, assume it's dead and close it too. */
                   1176:     if (rr == 0) {
                   1177:        if (! FD_ISSET (0, ding1))
                   1178:          netretry--;                   /* we actually try a coupla times. */
                   1179:        if (! netretry) {
                   1180:          if (o_verbose > 1)            /* normally we don't care */
                   1181:            holler ("net timeout");
                   1182:          close (fd);
                   1183:          return (0);                   /* not an error! */
                   1184:        }
                   1185:     } /* select timeout */
                   1186: /* xxx: should we check the exception fds too?  The read fds seem to give
                   1187:    us the right info, and none of the examples I found bothered. */
                   1188:
                   1189: /* Ding!!  Something arrived, go check all the incoming hoppers, net first */
                   1190:     if (FD_ISSET (fd, ding2)) {                /* net: ding! */
                   1191:        rr = read (fd, bigbuf_net, BIGSIZ);
                   1192:        if (rr <= 0) {
                   1193:          FD_CLR (fd, ding1);           /* net closed, we'll finish up... */
                   1194:          rzleft = 0;                   /* can't write anymore: broken pipe */
                   1195:        } else {
                   1196:          rnleft = rr;
                   1197:          np = bigbuf_net;
                   1198: #ifdef TELNET
                   1199:          if (o_tn)
                   1200:            atelnet (np, rr);           /* fake out telnet stuff */
                   1201: #endif /* TELNET */
                   1202:        } /* if rr */
                   1203: Debug (("got %d from the net, errno %d", rr, errno))
                   1204:     } /* net:ding */
                   1205:
                   1206: /* if we're in "slowly" mode there's probably still stuff in the stdin
                   1207:    buffer, so don't read unless we really need MORE INPUT!  MORE INPUT! */
                   1208:     if (rzleft)
                   1209:        goto shovel;
                   1210:
                   1211: /* okay, suck more stdin */
                   1212:     if (FD_ISSET (0, ding2)) {         /* stdin: ding! */
                   1213:        rr = read (0, bigbuf_in, BIGSIZ);
                   1214: /* Considered making reads here smaller for UDP mode, but 8192-byte
                   1215:    mobygrams are kinda fun and exercise the reassembler. */
                   1216:        if (rr <= 0) {                  /* at end, or fukt, or ... */
                   1217:          FD_CLR (0, ding1);            /* disable and close stdin */
                   1218:          close (0);
                   1219:        } else {
                   1220:          rzleft = rr;
                   1221:          zp = bigbuf_in;
                   1222: /* special case for multi-mode -- we'll want to send this one buffer to every
                   1223:    open TCP port or every UDP attempt, so save its size and clean up stdin */
                   1224:          if (! Single) {               /* we might be scanning... */
                   1225:            insaved = rr;               /* save len */
                   1226:            FD_CLR (0, ding1);          /* disable further junk from stdin */
                   1227:            close (0);                  /* really, I mean it */
                   1228:          } /* Single */
                   1229:        } /* if rr/read */
                   1230:     } /* stdin:ding */
                   1231:
                   1232: shovel:
                   1233: /* now that we've dingdonged all our thingdings, send off the results.
                   1234:    Geez, why does this look an awful lot like the big loop in "rsh"? ...
                   1235:    not sure if the order of this matters, but write net -> stdout first. */
                   1236:
                   1237: /* sanity check.  Works because they're both unsigned... */
                   1238:     if ((rzleft > 8200) || (rnleft > 8200)) {
                   1239:        holler ("Bogus buffers: %d, %d", rzleft, rnleft);
                   1240:        rzleft = rnleft = 0;
                   1241:     }
                   1242: /* net write retries sometimes happen on UDP connections */
                   1243:     if (! wretry) {                    /* is something hung? */
                   1244:        holler ("too many output retries");
                   1245:        return (1);
                   1246:     }
                   1247:     if (rnleft) {
                   1248:        rr = write (1, np, rnleft);
                   1249:        if (rr > 0) {
                   1250:          if (o_wfile)
                   1251:            oprint (1, np, rr);         /* log the stdout */
                   1252:          np += rr;                     /* fix up ptrs and whatnot */
                   1253:          rnleft -= rr;                 /* will get sanity-checked above */
                   1254:          wrote_out += rr;              /* global count */
                   1255:        }
                   1256: Debug (("wrote %d to stdout, errno %d", rr, errno))
                   1257:     } /* rnleft */
                   1258:     if (rzleft) {
                   1259:        if (o_interval)                 /* in "slowly" mode ?? */
                   1260:          rr = findline (zp, rzleft);
                   1261:        else
                   1262:          rr = rzleft;
                   1263:        rr = write (fd, zp, rr);        /* one line, or the whole buffer */
                   1264:        if (rr > 0) {
                   1265:          if (o_wfile)
                   1266:            oprint (0, zp, rr);         /* log what got sent */
                   1267:          zp += rr;
                   1268:          rzleft -= rr;
                   1269:          wrote_net += rr;              /* global count */
                   1270:        }
                   1271: Debug (("wrote %d to net, errno %d", rr, errno))
                   1272:     } /* rzleft */
                   1273:     if (o_interval) {                  /* cycle between slow lines, or ... */
                   1274:        sleep (o_interval);
                   1275:        errno = 0;                      /* clear from sleep */
                   1276:        continue;                       /* ...with hairy select loop... */
                   1277:     }
                   1278:     if ((rzleft) || (rnleft)) {                /* shovel that shit till they ain't */
                   1279:        wretry--;                       /* none left, and get another load */
                   1280:        goto shovel;
                   1281:     }
                   1282:   } /* while ding1:netfd is open */
                   1283:
                   1284: /* XXX: maybe want a more graceful shutdown() here, or screw around with
                   1285:    linger times??  I suspect that I don't need to since I'm always doing
                   1286:    blocking reads and writes and my own manual "last ditch" efforts to read
                   1287:    the net again after a timeout.  I haven't seen any screwups yet, but it's
                   1288:    not like my test network is particularly busy... */
                   1289:   close (fd);
                   1290:   return (0);
                   1291: } /* readwrite */
                   1292:
                   1293: /* main :
                   1294:    now we pull it all together... */
                   1295: main (argc, argv)
                   1296:   int argc;
                   1297:   char ** argv;
                   1298: {
                   1299: #ifndef HAVE_GETOPT
                   1300:   extern char * optarg;
                   1301:   extern int optind, optopt;
                   1302: #endif
                   1303:   register int x;
                   1304:   register char *cp;
                   1305:   HINF * gp;
                   1306:   HINF * whereto = NULL;
                   1307:   HINF * wherefrom = NULL;
                   1308:   IA * ouraddr = NULL;
                   1309:   IA * themaddr = NULL;
                   1310:   USHORT o_lport = 0;
                   1311:   USHORT ourport = 0;
                   1312:   USHORT loport = 0;           /* for scanning stuff */
                   1313:   USHORT hiport = 0;
                   1314:   USHORT curport = 0;
                   1315:   char * randports = NULL;
                   1316:
                   1317: #ifdef HAVE_BIND
                   1318: /* can *you* say "cc -yaddayadda netcat.c -lresolv -l44bsd" on SunLOSs? */
                   1319:   res_init();
                   1320: #endif
                   1321: /* I was in this barbershop quartet in Skokie IL ... */
                   1322: /* round up the usual suspects, i.e. malloc up all the stuff we need */
                   1323:   lclend = (SAI *) Hmalloc (sizeof (SA));
                   1324:   remend = (SAI *) Hmalloc (sizeof (SA));
                   1325:   bigbuf_in = Hmalloc (BIGSIZ);
                   1326:   bigbuf_net = Hmalloc (BIGSIZ);
                   1327:   ding1 = (fd_set *) Hmalloc (sizeof (fd_set));
                   1328:   ding2 = (fd_set *) Hmalloc (sizeof (fd_set));
                   1329:   portpoop = (PINF *) Hmalloc (sizeof (PINF));
                   1330:
                   1331:   errno = 0;
                   1332:   gatesptr = 4;
                   1333:   h_errno = 0;
                   1334:
                   1335: /* catch a signal or two for cleanup */
                   1336:   signal (SIGINT, catch);
                   1337:   signal (SIGQUIT, catch);
                   1338:   signal (SIGTERM, catch);
                   1339: /* and suppress others... */
                   1340: #ifdef SIGURG
                   1341:   signal (SIGURG, SIG_IGN);
                   1342: #endif
                   1343: #ifdef SIGPIPE
                   1344:   signal (SIGPIPE, SIG_IGN);           /* important! */
                   1345: #endif
                   1346:
                   1347: /* if no args given at all, get 'em from stdin, construct an argv, and hand
                   1348:    anything left over to readwrite(). */
                   1349:   if (argc == 1) {
                   1350:     cp = argv[0];
                   1351:     argv = (char **) Hmalloc (128 * sizeof (char *));  /* XXX: 128? */
                   1352:     argv[0] = cp;                      /* leave old prog name intact */
                   1353:     cp = Hmalloc (BIGSIZ);
                   1354:     argv[1] = cp;                      /* head of new arg block */
                   1355:     fprintf (stderr, "Cmd line: ");
                   1356:     fflush (stderr);           /* I dont care if it's unbuffered or not! */
                   1357:     insaved = read (0, cp, BIGSIZ);    /* we're gonna fake fgets() here */
                   1358:     if (insaved <= 0)
                   1359:       bail ("wrong");
                   1360:     x = findline (cp, insaved);
                   1361:     if (x)
                   1362:       insaved -= x;            /* remaining chunk size to be sent */
                   1363:     if (insaved)               /* which might be zero... */
                   1364:       memcpy (bigbuf_in, &cp[x], insaved);
                   1365:     cp = strchr (argv[1], '\n');
                   1366:     if (cp)
                   1367:       *cp = '\0';
                   1368:     cp = strchr (argv[1], '\r');       /* look for ^M too */
                   1369:     if (cp)
                   1370:       *cp = '\0';
                   1371:
                   1372: /* find and stash pointers to remaining new "args" */
                   1373:     cp = argv[1];
                   1374:     cp++;                              /* skip past first char */
                   1375:     x = 2;                             /* we know argv 0 and 1 already */
                   1376:     for (; *cp != '\0'; cp++) {
                   1377:       if (*cp == ' ') {
                   1378:        *cp = '\0';                     /* smash all spaces */
                   1379:        continue;
                   1380:       } else {
                   1381:        if (*(cp-1) == '\0') {
                   1382:          argv[x] = cp;
                   1383:          x++;
                   1384:        }
                   1385:       } /* if space */
                   1386:     } /* for cp */
                   1387:     argc = x;
                   1388:   } /* if no args given */
                   1389:
                   1390: /* If your shitbox doesn't have getopt, step into the nineties already. */
                   1391: /* optarg, optind = next-argv-component [i.e. flag arg]; optopt = last-char */
1.2       millert  1392:   while ((x = getopt (argc, argv, "ae:g:G:hi:lno:p:rs:tuvw:z")) != -1) {
1.1       deraadt  1393: /* Debug (("in go: x now %c, optarg %x optind %d", x, optarg, optind)) */
                   1394:     switch (x) {
                   1395:       case 'a':
                   1396:        bail ("all-A-records NIY");
                   1397:        o_alla++; break;
                   1398: #ifdef GAPING_SECURITY_HOLE
                   1399:       case 'e':                                /* prog to exec */
                   1400:        pr00gie = optarg;
                   1401:        break;
                   1402: #endif
                   1403:       case 'G':                                /* srcrt gateways pointer val */
                   1404:        x = atoi (optarg);
                   1405:        if ((x) && (x == (x & 0x1c)))   /* mask off bits of fukt values */
                   1406:          gatesptr = x;
                   1407:        else
                   1408:          bail ("invalid hop pointer %d, must be multiple of 4 <= 28", x);
                   1409:        break;
                   1410:       case 'g':                                /* srcroute hop[s] */
                   1411:        if (gatesidx > 8)
                   1412:          bail ("too many -g hops");
                   1413:        if (gates == NULL)              /* eat this, Billy-boy */
                   1414:          gates = (HINF **) Hmalloc (sizeof (HINF *) * 10);
                   1415:        gp = gethostpoop (optarg, o_nflag);
                   1416:        if (gp)
                   1417:          gates[gatesidx] = gp;
                   1418:        gatesidx++;
                   1419:        break;
                   1420:       case 'h':
                   1421:        errno = 0;
                   1422: #ifdef HAVE_HELP
                   1423:        helpme();                       /* exits by itself */
                   1424: #else
                   1425:        bail ("no help available, dork -- RTFS");
                   1426: #endif
                   1427:       case 'i':                                /* line-interval time */
                   1428:        o_interval = atoi (optarg) & 0xffff;
                   1429:        if (! o_interval)
                   1430:          bail ("invalid interval time %s", optarg);
                   1431:        break;
                   1432:       case 'l':                                /* listen mode */
                   1433:        o_listen++; break;
                   1434:       case 'n':                                /* numeric-only, no DNS lookups */
                   1435:        o_nflag++; break;
                   1436:       case 'o':                                /* hexdump log */
                   1437:        stage = (unsigned char *) optarg;
                   1438:        o_wfile++; break;
                   1439:       case 'p':                                /* local source port */
                   1440:        o_lport = getportpoop (optarg, 0);
                   1441:        if (o_lport == 0)
                   1442:          bail ("invalid local port %s", optarg);
                   1443:        break;
                   1444:       case 'r':                                /* randomize various things */
                   1445:        o_random++; break;
                   1446:       case 's':                                /* local source address */
                   1447: /* do a full lookup [since everything else goes through the same mill],
                   1448:    unless -n was previously specified.  In fact, careful placement of -n can
                   1449:    be useful, so we'll still pass o_nflag here instead of forcing numeric.  */
                   1450:        wherefrom = gethostpoop (optarg, o_nflag);
                   1451:        ouraddr = &wherefrom->iaddrs[0];
                   1452:        break;
                   1453: #ifdef TELNET
                   1454:       case 't':                                /* do telnet fakeout */
                   1455:        o_tn++; break;
                   1456: #endif /* TELNET */
                   1457:       case 'u':                                /* use UDP */
                   1458:        o_udpmode++; break;
                   1459:       case 'v':                                /* verbose */
                   1460:        o_verbose++; break;
                   1461:       case 'w':                                /* wait time */
                   1462:        o_wait = atoi (optarg);
                   1463:        if (o_wait <= 0)
                   1464:          bail ("invalid wait-time %s", optarg);
                   1465:        timer1 = (struct timeval *) Hmalloc (sizeof (struct timeval));
                   1466:        timer2 = (struct timeval *) Hmalloc (sizeof (struct timeval));
                   1467:        timer1->tv_sec = o_wait;        /* we need two.  see readwrite()... */
                   1468:        break;
                   1469:       case 'z':                                /* little or no data xfer */
                   1470:        o_zero++;
                   1471:        break;
                   1472:       default:
                   1473:        errno = 0;
                   1474:        bail ("nc -h for help");
                   1475:     } /* switch x */
                   1476:   } /* while getopt */
                   1477:
                   1478: /* other misc initialization */
                   1479: Debug (("fd_set size %d", sizeof (*ding1)))    /* how big *is* it? */
                   1480:   FD_SET (0, ding1);                   /* stdin *is* initially open */
                   1481:   if (o_random) {
                   1482:     SRAND (time (0));
                   1483:     randports = Hmalloc (65536);       /* big flag array for ports */
                   1484:   }
                   1485: #ifdef GAPING_SECURITY_HOLE
                   1486:   if (pr00gie) {
                   1487:     close (0);                         /* won't need stdin */
                   1488:     o_wfile = 0;                       /* -o with -e is meaningless! */
                   1489:     ofd = 0;
                   1490:   }
                   1491: #endif /* G_S_H */
                   1492:   if (o_wfile) {
                   1493:     ofd = open (stage, O_WRONLY | O_CREAT | O_TRUNC, 0664);
                   1494:     if (ofd <= 0)                      /* must be > extant 0/1/2 */
                   1495:       bail ("can't open %s", stage);
                   1496:     stage = (unsigned char *) Hmalloc (100);
                   1497:   }
                   1498:
                   1499: /* optind is now index of first non -x arg */
                   1500: Debug (("after go: x now %c, optarg %x optind %d", x, optarg, optind))
                   1501: /* Debug (("optind up to %d at host-arg %s", optind, argv[optind])) */
                   1502: /* gonna only use first addr of host-list, like our IQ was normal; if you wanna
                   1503:    get fancy with addresses, look up the list yourself and plug 'em in for now.
                   1504:    unless we finally implement -a, that is. */
                   1505:   if (argv[optind])
                   1506:     whereto = gethostpoop (argv[optind], o_nflag);
                   1507:   if (whereto && whereto->iaddrs)
                   1508:     themaddr = &whereto->iaddrs[0];
                   1509:   if (themaddr)
                   1510:     optind++;                          /* skip past valid host lookup */
                   1511:   errno = 0;
                   1512:   h_errno = 0;
                   1513:
                   1514: /* Handle listen mode here, and exit afterward.  Only does one connect;
                   1515:    this is arguably the right thing to do.  A "persistent listen-and-fork"
                   1516:    mode a la inetd has been thought about, but not implemented.  A tiny
                   1517:    wrapper script can handle such things... */
                   1518:   if (o_listen) {
                   1519:     curport = 0;                       /* rem port *can* be zero here... */
                   1520:     if (argv[optind]) {                        /* any rem-port-arg? */
                   1521:       curport = getportpoop (argv[optind], 0);
                   1522:       if (curport == 0)                        /* if given, demand correctness */
                   1523:        bail ("invalid port %s", argv[optind]);
                   1524:     } /* if port-arg */
                   1525:     netfd = dolisten (themaddr, curport, ouraddr, o_lport);
                   1526: /* dolisten does its own connect reporting, so we don't holler anything here */
                   1527:     if (netfd > 0) {
                   1528: #ifdef GAPING_SECURITY_HOLE
                   1529:       if (pr00gie)                     /* -e given? */
                   1530:        doexec (netfd);
                   1531: #endif /* GAPING_SECURITY_HOLE */
                   1532:       x = readwrite (netfd);           /* it even works with UDP! */
                   1533:       if (o_verbose > 1)               /* normally we don't care */
                   1534:        holler (wrote_txt, wrote_net, wrote_out);
                   1535:       exit (x);                                /* "pack out yer trash" */
                   1536:     } else /* if no netfd */
                   1537:       bail ("no connection");
                   1538:   } /* o_listen */
                   1539:
                   1540: /* fall thru to outbound connects.  Now we're more picky about args... */
                   1541:   if (! themaddr)
                   1542:     bail ("no destination");
                   1543:   if (argv[optind] == NULL)
                   1544:     bail ("no port[s] to connect to");
                   1545:   if (argv[optind + 1])                /* look ahead: any more port args given? */
                   1546:     Single = 0;                                /* multi-mode, case A */
                   1547:   ourport = o_lport;                   /* which can be 0 */
                   1548:
                   1549: /* everything from here down is treated as as ports and/or ranges thereof, so
                   1550:    it's all enclosed in this big ol' argv-parsin' loop.  Any randomization is
                   1551:    done within each given *range*, but in separate chunks per each succeeding
                   1552:    argument, so we can control the pattern somewhat. */
                   1553:   while (argv[optind]) {
                   1554:     hiport = loport = 0;
                   1555:     cp = strchr (argv[optind], '-');   /* nn-mm range? */
                   1556:     if (cp) {
                   1557:       *cp = '\0';
                   1558:       cp++;
                   1559:       hiport = getportpoop (cp, 0);
                   1560:       if (hiport == 0)
                   1561:        bail ("invalid port %s", cp);
                   1562:     } /* if found a dash */
                   1563:     loport = getportpoop (argv[optind], 0);
                   1564:     if (loport == 0)
                   1565:       bail ("invalid port %s", argv[optind]);
                   1566:     if (hiport > loport) {             /* was it genuinely a range? */
                   1567:       Single = 0;                      /* multi-mode, case B */
                   1568:       curport = hiport;                        /* start high by default */
                   1569:       if (o_random) {                  /* maybe populate the random array */
                   1570:        loadports (randports, loport, hiport);
                   1571:        curport = nextport (randports);
                   1572:       }
                   1573:     } else                     /* not a range, including args like "25-25" */
                   1574:       curport = loport;
                   1575: Debug (("Single %d, curport %d", Single, curport))
                   1576:
                   1577: /* Now start connecting to these things.  curport is already preloaded. */
                   1578:     while (loport <= curport) {
                   1579:       if ((! o_lport) && (o_random)) { /* -p overrides random local-port */
                   1580:        ourport = (RAND() & 0xffff);    /* random local-bind -- well above */
                   1581:        if (ourport < 8192)             /* resv and any likely listeners??? */
                   1582:          ourport += 8192;              /* if it *still* conflicts, use -s. */
                   1583:       }
                   1584:       curport = getportpoop (NULL, curport);
                   1585:       netfd = doconnect (themaddr, curport, ouraddr, ourport);
                   1586: Debug (("netfd %d from port %d to port %d", netfd, ourport, curport))
                   1587:       if (netfd > 0)
                   1588:        if (o_zero && o_udpmode)        /* if UDP scanning... */
                   1589:          netfd = udptest (netfd, themaddr);
                   1590:       if (netfd > 0) {                 /* Yow, are we OPEN YET?! */
                   1591:        x = 0;                          /* pre-exit status */
                   1592:        holler ("%s [%s] %d (%s) open",
                   1593:          whereto->name, whereto->addrs[0], curport, portpoop->name);
                   1594: #ifdef GAPING_SECURITY_HOLE
                   1595:        if (pr00gie)                    /* exec is valid for outbound, too */
                   1596:          doexec (netfd);
                   1597: #endif /* GAPING_SECURITY_HOLE */
                   1598:        if (! o_zero)
                   1599:          x = readwrite (netfd);        /* go shovel shit */
                   1600:       } else { /* no netfd... */
                   1601:        x = 1;                          /* preload exit status for later */
                   1602: /* if we're scanning at a "one -v" verbosity level, don't print refusals.
                   1603:    Give it another -v if you want to see everything. */
                   1604:        if ((Single || (o_verbose > 1)) || (errno != ECONNREFUSED))
                   1605:          holler ("%s [%s] %d (%s)",
                   1606:            whereto->name, whereto->addrs[0], curport, portpoop->name);
                   1607:       } /* if netfd */
                   1608:       close (netfd);                   /* just in case we didn't already */
                   1609:       if (o_interval)
                   1610:        sleep (o_interval);             /* if -i, delay between ports too */
                   1611:       if (o_random)
                   1612:        curport = nextport (randports);
                   1613:       else
                   1614:        curport--;                      /* just decrement... */
                   1615:     } /* while curport within current range */
                   1616:     optind++;
                   1617:   } /* while remaining port-args -- end of big argv-ports loop*/
                   1618:
                   1619:   errno = 0;
                   1620:   if (o_verbose > 1)           /* normally we don't care */
                   1621:     holler (wrote_txt, wrote_net, wrote_out);
                   1622:   if (Single)
                   1623:     exit (x);                  /* give us status on one connection */
                   1624:   exit (0);                    /* otherwise, we're just done */
                   1625: } /* main */
                   1626:
                   1627: #ifdef HAVE_HELP               /* unless we wanna be *really* cryptic */
                   1628: /* helpme :
                   1629:    the obvious */
                   1630: helpme()
                   1631: {
                   1632:   o_verbose = 1;
                   1633:   holler ("[v1.10]\n\
                   1634: connect to somewhere:  nc [-options] hostname port[s] [ports] ... \n\
                   1635: listen for inbound:    nc -l -p port [-options] [hostname] [port]\n\
                   1636: options:");
                   1637: /* sigh, this necessarily gets messy.  And the trailing \ characters may be
                   1638:    interpreted oddly by some compilers, generating or not generating extra
                   1639:    newlines as they bloody please.  u-fix... */
                   1640: #ifdef GAPING_SECURITY_HOLE    /* needs to be separate holler() */
                   1641:   holler ("\
                   1642:        -e prog                 program to exec after connect [dangerous!!]");
                   1643: #endif
                   1644:   holler ("\
                   1645:        -g gateway              source-routing hop point[s], up to 8\n\
                   1646:        -G num                  source-routing pointer: 4, 8, 12, ...\n\
                   1647:        -h                      this cruft\n\
                   1648:        -i secs                 delay interval for lines sent, ports scanned\n\
                   1649:        -l                      listen mode, for inbound connects\n\
                   1650:        -n                      numeric-only IP addresses, no DNS\n\
                   1651:        -o file                 hex dump of traffic\n\
                   1652:        -p port                 local port number\n\
                   1653:        -r                      randomize local and remote ports\n\
                   1654:        -s addr                 local source address");
                   1655: #ifdef TELNET
                   1656:   holler ("\
                   1657:        -t                      answer TELNET negotiation");
                   1658: #endif
                   1659:   holler ("\
                   1660:        -u                      UDP mode\n\
                   1661:        -v                      verbose [use twice to be more verbose]\n\
                   1662:        -w secs                 timeout for connects and final net reads\n\
                   1663:        -z                      zero-I/O mode [used for scanning]");
                   1664:   bail ("port numbers can be individual or ranges: lo-hi [inclusive]");
                   1665: } /* helpme */
                   1666: #endif /* HAVE_HELP */
                   1667:
                   1668: /* None genuine without this seal!  _H*/