busybox/networking/nc_bloaty.c
<<
>>
Prefs
   1/* Based on netcat 1.10 RELEASE 960320 written by hobbit@avian.org.
   2 * Released into public domain by the author.
   3 *
   4 * Copyright (C) 2007 Denys Vlasenko.
   5 *
   6 * Licensed under GPLv2, see file LICENSE in this tarball for details.
   7 */
   8
   9/* Author's comments from nc 1.10:
  10 * =====================
  11 * Netcat is entirely my own creation, although plenty of other code was used as
  12 * examples.  It is freely given away to the Internet community in the hope that
  13 * it will be useful, with no restrictions except giving credit where it is due.
  14 * No GPLs, Berkeley copyrights or any of that nonsense.  The author assumes NO
  15 * responsibility for how anyone uses it.  If netcat makes you rich somehow and
  16 * you're feeling generous, mail me a check.  If you are affiliated in any way
  17 * with Microsoft Network, get a life.  Always ski in control.  Comments,
  18 * questions, and patches to hobbit@avian.org.
  19 * ...
  20 * Netcat and the associated package is a product of Avian Research, and is freely
  21 * available in full source form with no restrictions save an obligation to give
  22 * credit where due.
  23 * ...
  24 * A damn useful little "backend" utility begun 950915 or thereabouts,
  25 * as *Hobbit*'s first real stab at some sockets programming.  Something that
  26 * should have and indeed may have existed ten years ago, but never became a
  27 * standard Unix utility.  IMHO, "nc" could take its place right next to cat,
  28 * cp, rm, mv, dd, ls, and all those other cryptic and Unix-like things.
  29 * =====================
  30 *
  31 * Much of author's comments are still retained in the code.
  32 *
  33 * Functionality removed (rationale):
  34 * - miltiple-port ranges, randomized port scanning (use nmap)
  35 * - telnet support (use telnet)
  36 * - source routing
  37 * - multiple DNS checks
  38 * Functionalty which is different from nc 1.10:
  39 * - Prog in '-e prog' can have prog's parameters and options.
  40 *   Because of this -e option must be last.
  41 * - nc doesn't redirect stderr to the network socket for the -e prog.
  42 * - numeric addresses are printed in (), not [] (IPv6 looks better),
  43 *   port numbers are inside (): (1.2.3.4:5678)
  44 * - network read errors are reported on verbose levels > 1
  45 *   (nc 1.10 treats them as EOF)
  46 * - TCP connects from wrong ip/ports (if peer ip:port is specified
  47 *   on the command line, but accept() says that it came from different addr)
  48 *   are closed, but nc doesn't exit - continues to listen/accept.
  49 */
  50
  51/* done in nc.c: #include "libbb.h" */
  52
  53enum {
  54        SLEAZE_PORT = 31337,               /* for UDP-scan RTT trick, change if ya want */
  55        BIGSIZ = 8192,                     /* big buffers */
  56
  57        netfd = 3,
  58        ofd = 4,
  59};
  60
  61struct globals {
  62        /* global cmd flags: */
  63        unsigned o_verbose;
  64        unsigned o_wait;
  65#if ENABLE_NC_EXTRA
  66        unsigned o_interval;
  67#endif
  68
  69        /*int netfd;*/
  70        /*int ofd;*/                     /* hexdump output fd */
  71#if ENABLE_LFS
  72#define SENT_N_RECV_M "sent %llu, rcvd %llu\n"
  73        unsigned long long wrote_out;          /* total stdout bytes */
  74        unsigned long long wrote_net;          /* total net bytes */
  75#else
  76#define SENT_N_RECV_M "sent %u, rcvd %u\n"
  77        unsigned wrote_out;          /* total stdout bytes */
  78        unsigned wrote_net;          /* total net bytes */
  79#endif
  80        /* ouraddr is never NULL and goes through three states as we progress:
  81         1 - local address before bind (IP/port possibly zero)
  82         2 - local address after bind (port is nonzero)
  83         3 - local address after connect??/recv/accept (IP and port are nonzero) */
  84        struct len_and_sockaddr *ouraddr;
  85        /* themaddr is NULL if no peer hostname[:port] specified on command line */
  86        struct len_and_sockaddr *themaddr;
  87        /* remend is set after connect/recv/accept to the actual ip:port of peer */
  88        struct len_and_sockaddr remend;
  89
  90        jmp_buf jbuf;                /* timer crud */
  91
  92        /* will malloc up the following globals: */
  93        fd_set ding1;                /* for select loop */
  94        fd_set ding2;
  95        char bigbuf_in[BIGSIZ];      /* data buffers */
  96        char bigbuf_net[BIGSIZ];
  97};
  98
  99#define G (*ptr_to_globals)
 100#define wrote_out  (G.wrote_out )
 101#define wrote_net  (G.wrote_net )
 102#define ouraddr    (G.ouraddr   )
 103#define themaddr   (G.themaddr  )
 104#define remend     (G.remend    )
 105#define jbuf       (G.jbuf      )
 106#define ding1      (G.ding1     )
 107#define ding2      (G.ding2     )
 108#define bigbuf_in  (G.bigbuf_in )
 109#define bigbuf_net (G.bigbuf_net)
 110#define o_verbose  (G.o_verbose )
 111#define o_wait     (G.o_wait    )
 112#if ENABLE_NC_EXTRA
 113#define o_interval (G.o_interval)
 114#else
 115#define o_interval 0
 116#endif
 117#define INIT_G() do { \
 118        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
 119} while (0)
 120
 121
 122/* Must match getopt32 call! */
 123enum {
 124        OPT_h = (1 << 0),
 125        OPT_n = (1 << 1),
 126        OPT_p = (1 << 2),
 127        OPT_s = (1 << 3),
 128        OPT_u = (1 << 4),
 129        OPT_v = (1 << 5),
 130        OPT_w = (1 << 6),
 131        OPT_l = (1 << 7) * ENABLE_NC_SERVER,
 132        OPT_i = (1 << (7+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
 133        OPT_o = (1 << (8+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
 134        OPT_z = (1 << (9+ENABLE_NC_SERVER)) * ENABLE_NC_EXTRA,
 135};
 136
 137#define o_nflag   (option_mask32 & OPT_n)
 138#define o_udpmode (option_mask32 & OPT_u)
 139#if ENABLE_NC_SERVER
 140#define o_listen  (option_mask32 & OPT_l)
 141#else
 142#define o_listen  0
 143#endif
 144#if ENABLE_NC_EXTRA
 145#define o_ofile   (option_mask32 & OPT_o)
 146#define o_zero    (option_mask32 & OPT_z)
 147#else
 148#define o_ofile   0
 149#define o_zero    0
 150#endif
 151
 152/* Debug: squirt whatever message and sleep a bit so we can see it go by. */
 153/* Beware: writes to stdOUT... */
 154#if 0
 155#define Debug(...) do { printf(__VA_ARGS__); printf("\n"); fflush(stdout); sleep(1); } while (0)
 156#else
 157#define Debug(...) do { } while (0)
 158#endif
 159
 160#define holler_error(...)  do { if (o_verbose) bb_error_msg(__VA_ARGS__); } while (0)
 161#define holler_perror(...) do { if (o_verbose) bb_perror_msg(__VA_ARGS__); } while (0)
 162
 163/* catch: no-brainer interrupt handler */
 164static void catch(int sig)
 165{
 166        if (o_verbose > 1)                /* normally we don't care */
 167                fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out);
 168        fprintf(stderr, "punt!\n");
 169        kill_myself_with_sig(sig);
 170}
 171
 172/* unarm  */
 173static void unarm(void)
 174{
 175        signal(SIGALRM, SIG_IGN);
 176        alarm(0);
 177}
 178
 179/* timeout and other signal handling cruft */
 180static void tmtravel(int sig UNUSED_PARAM)
 181{
 182        unarm();
 183        longjmp(jbuf, 1);
 184}
 185
 186/* arm: set the timer.  */
 187static void arm(unsigned secs)
 188{
 189        signal(SIGALRM, tmtravel);
 190        alarm(secs);
 191}
 192
 193/* findline:
 194 find the next newline in a buffer; return inclusive size of that "line",
 195 or the entire buffer size, so the caller knows how much to then write().
 196 Not distinguishing \n vs \r\n for the nonce; it just works as is... */
 197static unsigned findline(char *buf, unsigned siz)
 198{
 199        char * p;
 200        int x;
 201        if (!buf)                        /* various sanity checks... */
 202                return 0;
 203        if (siz > BIGSIZ)
 204                return 0;
 205        x = siz;
 206        for (p = buf; x > 0; x--) {
 207                if (*p == '\n') {
 208                        x = (int) (p - buf);
 209                        x++;                        /* 'sokay if it points just past the end! */
 210Debug("findline returning %d", x);
 211                        return x;
 212                }
 213                p++;
 214        } /* for */
 215Debug("findline returning whole thing: %d", siz);
 216        return siz;
 217} /* findline */
 218
 219/* doexec:
 220 fiddle all the file descriptors around, and hand off to another prog.  Sort
 221 of like a one-off "poor man's inetd".  This is the only section of code
 222 that would be security-critical, which is why it's ifdefed out by default.
 223 Use at your own hairy risk; if you leave shells lying around behind open
 224 listening ports you deserve to lose!! */
 225static int doexec(char **proggie) NORETURN;
 226static int doexec(char **proggie)
 227{
 228        xmove_fd(netfd, 0);
 229        dup2(0, 1);
 230        /* dup2(0, 2); - do we *really* want this? NO!
 231         * exec'ed prog can do it yourself, if needed */
 232        execvp(proggie[0], proggie);
 233        bb_perror_msg_and_die("exec");
 234}
 235
 236/* connect_w_timeout:
 237 return an fd for one of
 238 an open outbound TCP connection, a UDP stub-socket thingie, or
 239 an unconnected TCP or UDP socket to listen on.
 240 Examines various global o_blah flags to figure out what to do.
 241 lad can be NULL, then socket is not bound to any local ip[:port] */
 242static int connect_w_timeout(int fd)
 243{
 244        int rr;
 245
 246        /* wrap connect inside a timer, and hit it */
 247        arm(o_wait);
 248        if (setjmp(jbuf) == 0) {
 249                rr = connect(fd, &themaddr->u.sa, themaddr->len);
 250                unarm();
 251        } else { /* setjmp: connect failed... */
 252                rr = -1;
 253                errno = ETIMEDOUT; /* fake it */
 254        }
 255        return rr;
 256}
 257
 258/* dolisten:
 259 listens for
 260 incoming and returns an open connection *from* someplace.  If we were
 261 given host/port args, any connections from elsewhere are rejected.  This
 262 in conjunction with local-address binding should limit things nicely... */
 263static void dolisten(void)
 264{
 265        int rr;
 266
 267        if (!o_udpmode)
 268                xlisten(netfd, 1); /* TCP: gotta listen() before we can get */
 269
 270        /* Various things that follow temporarily trash bigbuf_net, which might contain
 271         a copy of any recvfrom()ed packet, but we'll read() another copy later. */
 272
 273        /* I can't believe I have to do all this to get my own goddamn bound address
 274         and port number.  It should just get filled in during bind() or something.
 275         All this is only useful if we didn't say -p for listening, since if we
 276         said -p we *know* what port we're listening on.  At any rate we won't bother
 277         with it all unless we wanted to see it, although listening quietly on a
 278         random unknown port is probably not very useful without "netstat". */
 279        if (o_verbose) {
 280                char *addr;
 281                rr = getsockname(netfd, &ouraddr->u.sa, &ouraddr->len);
 282                if (rr < 0)
 283                        bb_perror_msg_and_die("getsockname after bind");
 284                addr = xmalloc_sockaddr2dotted(&ouraddr->u.sa);
 285                fprintf(stderr, "listening on %s ...\n", addr);
 286                free(addr);
 287        }
 288
 289        if (o_udpmode) {
 290                /* UDP is a speeeeecial case -- we have to do I/O *and* get the calling
 291                 party's particulars all at once, listen() and accept() don't apply.
 292                 At least in the BSD universe, however, recvfrom/PEEK is enough to tell
 293                 us something came in, and we can set things up so straight read/write
 294                 actually does work after all.  Yow.  YMMV on strange platforms!  */
 295
 296                /* I'm not completely clear on how this works -- BSD seems to make UDP
 297                 just magically work in a connect()ed context, but we'll undoubtedly run
 298                 into systems this deal doesn't work on.  For now, we apparently have to
 299                 issue a connect() on our just-tickled socket so we can write() back.
 300                 Again, why the fuck doesn't it just get filled in and taken care of?!
 301                 This hack is anything but optimal.  Basically, if you want your listener
 302                 to also be able to send data back, you need this connect() line, which
 303                 also has the side effect that now anything from a different source or even a
 304                 different port on the other end won't show up and will cause ICMP errors.
 305                 I guess that's what they meant by "connect".
 306                 Let's try to remember what the "U" is *really* for, eh? */
 307
 308                /* If peer address is specified, connect to it */
 309                remend.len = LSA_SIZEOF_SA;
 310                if (themaddr) {
 311                        remend = *themaddr;
 312                        xconnect(netfd, &themaddr->u.sa, themaddr->len);
 313                }
 314                /* peek first packet and remember peer addr */
 315                arm(o_wait);                /* might as well timeout this, too */
 316                if (setjmp(jbuf) == 0) {       /* do timeout for initial connect */
 317                        /* (*ouraddr) is prefilled with "default" address */
 318                        /* and here we block... */
 319                        rr = recv_from_to(netfd, NULL, 0, MSG_PEEK, /*was bigbuf_net, BIGSIZ*/
 320                                &remend.u.sa, &ouraddr->u.sa, ouraddr->len);
 321                        if (rr < 0)
 322                                bb_perror_msg_and_die("recvfrom");
 323                        unarm();
 324                } else
 325                        bb_error_msg_and_die("timeout");
 326/* Now we learned *to which IP* peer has connected, and we want to anchor
 327our socket on it, so that our outbound packets will have correct local IP.
 328Unfortunately, bind() on already bound socket will fail now (EINVAL):
 329        xbind(netfd, &ouraddr->u.sa, ouraddr->len);
 330Need to read the packet, save data, close this socket and
 331create new one, and bind() it. TODO */
 332                if (!themaddr)
 333                        xconnect(netfd, &remend.u.sa, ouraddr->len);
 334        } else {
 335                /* TCP */
 336                arm(o_wait); /* wrap this in a timer, too; 0 = forever */
 337                if (setjmp(jbuf) == 0) {
 338 again:
 339                        remend.len = LSA_SIZEOF_SA;
 340                        rr = accept(netfd, &remend.u.sa, &remend.len);
 341                        if (rr < 0)
 342                                bb_perror_msg_and_die("accept");
 343                        if (themaddr && memcmp(&remend.u.sa, &themaddr->u.sa, remend.len) != 0) {
 344                                /* nc 1.10 bails out instead, and its error message
 345                                 * is not suppressed by o_verbose */
 346                                if (o_verbose) {
 347                                        char *remaddr = xmalloc_sockaddr2dotted(&remend.u.sa);
 348                                        bb_error_msg("connect from wrong ip/port %s ignored", remaddr);
 349                                        free(remaddr);
 350                                }
 351                                close(rr);
 352                                goto again;
 353                        }
 354                        unarm();
 355                } else
 356                        bb_error_msg_and_die("timeout");
 357                xmove_fd(rr, netfd); /* dump the old socket, here's our new one */
 358                /* find out what address the connection was *to* on our end, in case we're
 359                 doing a listen-on-any on a multihomed machine.  This allows one to
 360                 offer different services via different alias addresses, such as the
 361                 "virtual web site" hack. */
 362                rr = getsockname(netfd, &ouraddr->u.sa, &ouraddr->len);
 363                if (rr < 0)
 364                        bb_perror_msg_and_die("getsockname after accept");
 365        }
 366
 367        if (o_verbose) {
 368                char *lcladdr, *remaddr, *remhostname;
 369
 370#if ENABLE_NC_EXTRA && defined(IP_OPTIONS)
 371        /* If we can, look for any IP options.  Useful for testing the receiving end of
 372         such things, and is a good exercise in dealing with it.  We do this before
 373         the connect message, to ensure that the connect msg is uniformly the LAST
 374         thing to emerge after all the intervening crud.  Doesn't work for UDP on
 375         any machines I've tested, but feel free to surprise me. */
 376                char optbuf[40];
 377                socklen_t x = sizeof(optbuf);
 378
 379                rr = getsockopt(netfd, IPPROTO_IP, IP_OPTIONS, optbuf, &x);
 380                if (rr < 0)
 381                        bb_perror_msg("getsockopt failed");
 382                else if (x) {    /* we've got options, lessee em... */
 383                        bin2hex(bigbuf_net, optbuf, x);
 384                        bigbuf_net[2*x] = '\0';
 385                        fprintf(stderr, "IP options: %s\n", bigbuf_net);
 386                }
 387#endif
 388
 389        /* now check out who it is.  We don't care about mismatched DNS names here,
 390         but any ADDR and PORT we specified had better fucking well match the caller.
 391         Converting from addr to inet_ntoa and back again is a bit of a kludge, but
 392         gethostpoop wants a string and there's much gnarlier code out there already,
 393         so I don't feel bad.
 394         The *real* question is why BFD sockets wasn't designed to allow listens for
 395         connections *from* specific hosts/ports, instead of requiring the caller to
 396         accept the connection and then reject undesireable ones by closing.
 397         In other words, we need a TCP MSG_PEEK. */
 398        /* bbox: removed most of it */
 399                lcladdr = xmalloc_sockaddr2dotted(&ouraddr->u.sa);
 400                remaddr = xmalloc_sockaddr2dotted(&remend.u.sa);
 401                remhostname = o_nflag ? remaddr : xmalloc_sockaddr2host(&remend.u.sa);
 402                fprintf(stderr, "connect to %s from %s (%s)\n",
 403                                lcladdr, remhostname, remaddr);
 404                free(lcladdr);
 405                free(remaddr);
 406                if (!o_nflag)
 407                        free(remhostname);
 408        }
 409}
 410
 411/* udptest:
 412 fire a couple of packets at a UDP target port, just to see if it's really
 413 there.  On BSD kernels, ICMP host/port-unreachable errors get delivered to
 414 our socket as ECONNREFUSED write errors.  On SV kernels, we lose; we'll have
 415 to collect and analyze raw ICMP ourselves a la satan's probe_udp_ports
 416 backend.  Guess where one could swipe the appropriate code from...
 417
 418 Use the time delay between writes if given, otherwise use the "tcp ping"
 419 trick for getting the RTT.  [I got that idea from pluvius, and warped it.]
 420 Return either the original fd, or clean up and return -1. */
 421#if ENABLE_NC_EXTRA
 422static int udptest(void)
 423{
 424        int rr;
 425
 426        rr = write(netfd, bigbuf_in, 1);
 427        if (rr != 1)
 428                bb_perror_msg("udptest first write");
 429
 430        if (o_wait)
 431                sleep(o_wait); // can be interrupted! while (t) nanosleep(&t)?
 432        else {
 433        /* use the tcp-ping trick: try connecting to a normally refused port, which
 434         causes us to block for the time that SYN gets there and RST gets back.
 435         Not completely reliable, but it *does* mostly work. */
 436        /* Set a temporary connect timeout, so packet filtration doesnt cause
 437         us to hang forever, and hit it */
 438                o_wait = 5;                     /* enough that we'll notice?? */
 439                rr = xsocket(ouraddr->u.sa.sa_family, SOCK_STREAM, 0);
 440                set_nport(themaddr, htons(SLEAZE_PORT));
 441                connect_w_timeout(rr);
 442                /* don't need to restore themaddr's port, it's not used anymore */
 443                close(rr);
 444                o_wait = 0; /* restore */
 445        }
 446
 447        rr = write(netfd, bigbuf_in, 1);
 448        return (rr != 1); /* if rr == 1, return 0 (success) */
 449}
 450#else
 451int udptest(void);
 452#endif
 453
 454/* oprint:
 455 Hexdump bytes shoveled either way to a running logfile, in the format:
 456 D offset       -  - - - --- 16 bytes --- - - -  -     # .... ascii .....
 457 where "which" sets the direction indicator, D:
 458 0 -- sent to network, or ">"
 459 1 -- rcvd and printed to stdout, or "<"
 460 and "buf" and "n" are data-block and length.  If the current block generates
 461 a partial line, so be it; we *want* that lockstep indication of who sent
 462 what when.  Adapted from dgaudet's original example -- but must be ripping
 463 *fast*, since we don't want to be too disk-bound... */
 464#if ENABLE_NC_EXTRA
 465static void oprint(int direction, unsigned char *p, unsigned bc)
 466{
 467        unsigned obc;           /* current "global" offset */
 468        unsigned x;
 469        unsigned char *op;      /* out hexdump ptr */
 470        unsigned char *ap;      /* out asc-dump ptr */
 471        unsigned char stage[100];
 472
 473        if (bc == 0)
 474                return;
 475
 476        obc = wrote_net; /* use the globals! */
 477        if (direction == '<')
 478                obc = wrote_out;
 479        stage[0] = direction;
 480        stage[59] = '#'; /* preload separator */
 481        stage[60] = ' ';
 482
 483        do {    /* for chunk-o-data ... */
 484                x = 16;
 485                if (bc < 16) {
 486                        /* memset(&stage[bc*3 + 11], ' ', 16*3 - bc*3); */
 487                        memset(&stage[11], ' ', 16*3);
 488                        x = bc;
 489                }
 490                sprintf((char *)&stage[1], " %8.8x ", obc);  /* xxx: still slow? */
 491                bc -= x;          /* fix current count */
 492                obc += x;         /* fix current offset */
 493                op = &stage[11];  /* where hex starts */
 494                ap = &stage[61];  /* where ascii starts */
 495
 496                do {  /* for line of dump, however long ... */
 497                        *op++ = 0x20 | bb_hexdigits_upcase[*p >> 4];
 498                        *op++ = 0x20 | bb_hexdigits_upcase[*p & 0x0f];
 499                        *op++ = ' ';
 500                        if ((*p > 31) && (*p < 127))
 501                                *ap = *p;   /* printing */
 502                        else
 503                                *ap = '.';  /* nonprinting, loose def */
 504                        ap++;
 505                        p++;
 506                } while (--x);
 507                *ap++ = '\n';  /* finish the line */
 508                xwrite(ofd, stage, ap - stage);
 509        } while (bc);
 510}
 511#else
 512void oprint(int direction, unsigned char *p, unsigned bc);
 513#endif
 514
 515/* readwrite:
 516 handle stdin/stdout/network I/O.  Bwahaha!! -- the select loop from hell.
 517 In this instance, return what might become our exit status. */
 518static int readwrite(void)
 519{
 520        int rr;
 521        char *zp = zp; /* gcc */  /* stdin buf ptr */
 522        char *np = np;            /* net-in buf ptr */
 523        unsigned rzleft;
 524        unsigned rnleft;
 525        unsigned netretry;              /* net-read retry counter */
 526        unsigned wretry;                /* net-write sanity counter */
 527        unsigned wfirst;                /* one-shot flag to skip first net read */
 528
 529        /* if you don't have all this FD_* macro hair in sys/types.h, you'll have to
 530         either find it or do your own bit-bashing: *ding1 |= (1 << fd), etc... */
 531        FD_SET(netfd, &ding1);                /* global: the net is open */
 532        netretry = 2;
 533        wfirst = 0;
 534        rzleft = rnleft = 0;
 535        if (o_interval)
 536                sleep(o_interval);                /* pause *before* sending stuff, too */
 537
 538        errno = 0;                        /* clear from sleep, close, whatever */
 539        /* and now the big ol' select shoveling loop ... */
 540        while (FD_ISSET(netfd, &ding1)) {        /* i.e. till the *net* closes! */
 541                wretry = 8200;                        /* more than we'll ever hafta write */
 542                if (wfirst) {                        /* any saved stdin buffer? */
 543                        wfirst = 0;                        /* clear flag for the duration */
 544                        goto shovel;                        /* and go handle it first */
 545                }
 546                ding2 = ding1;                        /* FD_COPY ain't portable... */
 547        /* some systems, notably linux, crap into their select timers on return, so
 548         we create a expendable copy and give *that* to select.  */
 549                if (o_wait) {
 550                        struct timeval tmp_timer;
 551                        tmp_timer.tv_sec = o_wait;
 552                        tmp_timer.tv_usec = 0;
 553                /* highest possible fd is netfd (3) */
 554                        rr = select(netfd+1, &ding2, NULL, NULL, &tmp_timer);
 555                } else
 556                        rr = select(netfd+1, &ding2, NULL, NULL, NULL);
 557                if (rr < 0 && errno != EINTR) {                /* might have gotten ^Zed, etc */
 558                        holler_perror("select");
 559                        close(netfd);
 560                        return 1;
 561                }
 562        /* if we have a timeout AND stdin is closed AND we haven't heard anything
 563         from the net during that time, assume it's dead and close it too. */
 564                if (rr == 0) {
 565                        if (!FD_ISSET(STDIN_FILENO, &ding1))
 566                                netretry--;                        /* we actually try a coupla times. */
 567                        if (!netretry) {
 568                                if (o_verbose > 1)                /* normally we don't care */
 569                                        fprintf(stderr, "net timeout\n");
 570                                close(netfd);
 571                                return 0;                        /* not an error! */
 572                        }
 573                } /* select timeout */
 574        /* xxx: should we check the exception fds too?  The read fds seem to give
 575         us the right info, and none of the examples I found bothered. */
 576
 577        /* Ding!!  Something arrived, go check all the incoming hoppers, net first */
 578                if (FD_ISSET(netfd, &ding2)) {                /* net: ding! */
 579                        rr = read(netfd, bigbuf_net, BIGSIZ);
 580                        if (rr <= 0) {
 581                                if (rr < 0 && o_verbose > 1) {
 582                                        /* nc 1.10 doesn't do this */
 583                                        bb_perror_msg("net read");
 584                                }
 585                                FD_CLR(netfd, &ding1);                /* net closed, we'll finish up... */
 586                                rzleft = 0;                        /* can't write anymore: broken pipe */
 587                        } else {
 588                                rnleft = rr;
 589                                np = bigbuf_net;
 590                        }
 591Debug("got %d from the net, errno %d", rr, errno);
 592                } /* net:ding */
 593
 594        /* if we're in "slowly" mode there's probably still stuff in the stdin
 595         buffer, so don't read unless we really need MORE INPUT!  MORE INPUT! */
 596                if (rzleft)
 597                        goto shovel;
 598
 599        /* okay, suck more stdin */
 600                if (FD_ISSET(STDIN_FILENO, &ding2)) {                /* stdin: ding! */
 601                        rr = read(STDIN_FILENO, bigbuf_in, BIGSIZ);
 602        /* Considered making reads here smaller for UDP mode, but 8192-byte
 603         mobygrams are kinda fun and exercise the reassembler. */
 604                        if (rr <= 0) {                        /* at end, or fukt, or ... */
 605                                FD_CLR(STDIN_FILENO, &ding1);                /* disable and close stdin */
 606                                close(0);
 607                        } else {
 608                                rzleft = rr;
 609                                zp = bigbuf_in;
 610                        }
 611                } /* stdin:ding */
 612 shovel:
 613        /* now that we've dingdonged all our thingdings, send off the results.
 614         Geez, why does this look an awful lot like the big loop in "rsh"? ...
 615         not sure if the order of this matters, but write net -> stdout first. */
 616
 617        /* sanity check.  Works because they're both unsigned... */
 618                if ((rzleft > 8200) || (rnleft > 8200)) {
 619                        holler_error("bogus buffers: %u, %u", rzleft, rnleft);
 620                        rzleft = rnleft = 0;
 621                }
 622        /* net write retries sometimes happen on UDP connections */
 623                if (!wretry) {                        /* is something hung? */
 624                        holler_error("too many output retries");
 625                        return 1;
 626                }
 627                if (rnleft) {
 628                        rr = write(STDOUT_FILENO, np, rnleft);
 629                        if (rr > 0) {
 630                                if (o_ofile) /* log the stdout */
 631                                        oprint('<', (unsigned char *)np, rr);
 632                                np += rr;                        /* fix up ptrs and whatnot */
 633                                rnleft -= rr;                        /* will get sanity-checked above */
 634                                wrote_out += rr;                /* global count */
 635                        }
 636Debug("wrote %d to stdout, errno %d", rr, errno);
 637                } /* rnleft */
 638                if (rzleft) {
 639                        if (o_interval)                        /* in "slowly" mode ?? */
 640                                rr = findline(zp, rzleft);
 641                        else
 642                                rr = rzleft;
 643                        rr = write(netfd, zp, rr);        /* one line, or the whole buffer */
 644                        if (rr > 0) {
 645                                if (o_ofile) /* log what got sent */
 646                                        oprint('>', (unsigned char *)zp, rr);
 647                                zp += rr;
 648                                rzleft -= rr;
 649                                wrote_net += rr;                /* global count */
 650                        }
 651Debug("wrote %d to net, errno %d", rr, errno);
 652                } /* rzleft */
 653                if (o_interval) {                        /* cycle between slow lines, or ... */
 654                        sleep(o_interval);
 655                        errno = 0;                        /* clear from sleep */
 656                        continue;                        /* ...with hairy select loop... */
 657                }
 658                if ((rzleft) || (rnleft)) {                /* shovel that shit till they ain't */
 659                        wretry--;                        /* none left, and get another load */
 660                        goto shovel;
 661                }
 662        } /* while ding1:netfd is open */
 663
 664        /* XXX: maybe want a more graceful shutdown() here, or screw around with
 665         linger times??  I suspect that I don't need to since I'm always doing
 666         blocking reads and writes and my own manual "last ditch" efforts to read
 667         the net again after a timeout.  I haven't seen any screwups yet, but it's
 668         not like my test network is particularly busy... */
 669        close(netfd);
 670        return 0;
 671} /* readwrite */
 672
 673/* main: now we pull it all together... */
 674int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 675int nc_main(int argc, char **argv)
 676{
 677        char *str_p, *str_s;
 678        USE_NC_EXTRA(char *str_i, *str_o;)
 679        char *themdotted = themdotted; /* gcc */
 680        char **proggie;
 681        int x;
 682        unsigned o_lport = 0;
 683
 684        INIT_G();
 685
 686        /* catch a signal or two for cleanup */
 687        bb_signals(0
 688                + (1 << SIGINT)
 689                + (1 << SIGQUIT)
 690                + (1 << SIGTERM)
 691                , catch);
 692        /* and suppress others... */
 693        bb_signals(0
 694#ifdef SIGURG
 695                + (1 << SIGURG)
 696#endif
 697                + (1 << SIGPIPE) /* important! */
 698                , SIG_IGN);
 699
 700        proggie = argv;
 701        while (*++proggie) {
 702                if (strcmp(*proggie, "-e") == 0) {
 703                        *proggie = NULL;
 704                        argc = proggie - argv;
 705                        proggie++;
 706                        goto e_found;
 707                }
 708        }
 709        proggie = NULL;
 710 e_found:
 711
 712        // -g -G -t -r deleted, unimplemented -a deleted too
 713        opt_complementary = "?2:vv:w+"; /* max 2 params; -v is a counter; -w N */
 714        getopt32(argv, "hnp:s:uvw:" USE_NC_SERVER("l")
 715                        USE_NC_EXTRA("i:o:z"),
 716                        &str_p, &str_s, &o_wait
 717                        USE_NC_EXTRA(, &str_i, &str_o, &o_verbose));
 718        argv += optind;
 719#if ENABLE_NC_EXTRA
 720        if (option_mask32 & OPT_i) /* line-interval time */
 721                o_interval = xatou_range(str_i, 1, 0xffff);
 722#endif
 723        //if (option_mask32 & OPT_l) /* listen mode */
 724        //if (option_mask32 & OPT_n) /* numeric-only, no DNS lookups */
 725        //if (option_mask32 & OPT_o) /* hexdump log */
 726        if (option_mask32 & OPT_p) { /* local source port */
 727                o_lport = bb_lookup_port(str_p, o_udpmode ? "udp" : "tcp", 0);
 728                if (!o_lport)
 729                        bb_error_msg_and_die("bad local port '%s'", str_p);
 730        }
 731        //if (option_mask32 & OPT_r) /* randomize various things */
 732        //if (option_mask32 & OPT_u) /* use UDP */
 733        //if (option_mask32 & OPT_v) /* verbose */
 734        //if (option_mask32 & OPT_w) /* wait time */
 735        //if (option_mask32 & OPT_z) /* little or no data xfer */
 736
 737        /* We manage our fd's so that they are never 0,1,2 */
 738        /*bb_sanitize_stdio(); - not needed */
 739
 740        if (argv[0]) {
 741                themaddr = xhost2sockaddr(argv[0],
 742                        argv[1]
 743                        ? bb_lookup_port(argv[1], o_udpmode ? "udp" : "tcp", 0)
 744                        : 0);
 745        }
 746
 747        /* create & bind network socket */
 748        x = (o_udpmode ? SOCK_DGRAM : SOCK_STREAM);
 749        if (option_mask32 & OPT_s) { /* local address */
 750                /* if o_lport is still 0, then we will use random port */
 751                ouraddr = xhost2sockaddr(str_s, o_lport);
 752#ifdef BLOAT
 753                /* prevent spurious "UDP listen needs !0 port" */
 754                o_lport = get_nport(ouraddr);
 755                o_lport = ntohs(o_lport);
 756#endif
 757                x = xsocket(ouraddr->u.sa.sa_family, x, 0);
 758        } else {
 759                /* We try IPv6, then IPv4, unless addr family is
 760                 * implicitly set by way of remote addr/port spec */
 761                x = xsocket_type(&ouraddr,
 762                                (themaddr ? themaddr->u.sa.sa_family : AF_UNSPEC),
 763                                x);
 764                if (o_lport)
 765                        set_nport(ouraddr, htons(o_lport));
 766        }
 767        xmove_fd(x, netfd);
 768        setsockopt_reuseaddr(netfd);
 769        if (o_udpmode)
 770                socket_want_pktinfo(netfd);
 771        xbind(netfd, &ouraddr->u.sa, ouraddr->len);
 772#if 0
 773        setsockopt(netfd, SOL_SOCKET, SO_RCVBUF, &o_rcvbuf, sizeof o_rcvbuf);
 774        setsockopt(netfd, SOL_SOCKET, SO_SNDBUF, &o_sndbuf, sizeof o_sndbuf);
 775#endif
 776
 777#ifdef BLOAT
 778        if (OPT_l && (option_mask32 & (OPT_u|OPT_l)) == (OPT_u|OPT_l)) {
 779                /* apparently UDP can listen ON "port 0",
 780                 but that's not useful */
 781                if (!o_lport)
 782                        bb_error_msg_and_die("UDP listen needs nonzero -p port");
 783        }
 784#endif
 785
 786        FD_SET(STDIN_FILENO, &ding1);                        /* stdin *is* initially open */
 787        if (proggie) {
 788                close(0); /* won't need stdin */
 789                option_mask32 &= ~OPT_o; /* -o with -e is meaningless! */
 790        }
 791#if ENABLE_NC_EXTRA
 792        if (o_ofile)
 793                xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), ofd);
 794#endif
 795
 796        if (o_listen) {
 797                dolisten();
 798                /* dolisten does its own connect reporting */
 799                if (proggie) /* -e given? */
 800                        doexec(proggie);
 801                x = readwrite(); /* it even works with UDP! */
 802        } else {
 803                /* Outbound connects.  Now we're more picky about args... */
 804                if (!themaddr)
 805                        bb_error_msg_and_die("no destination");
 806
 807                remend = *themaddr;
 808                if (o_verbose)
 809                        themdotted = xmalloc_sockaddr2dotted(&themaddr->u.sa);
 810
 811                x = connect_w_timeout(netfd);
 812                if (o_zero && x == 0 && o_udpmode)        /* if UDP scanning... */
 813                        x = udptest();
 814                if (x == 0) {                        /* Yow, are we OPEN YET?! */
 815                        if (o_verbose)
 816                                fprintf(stderr, "%s (%s) open\n", argv[0], themdotted);
 817                        if (proggie)                        /* exec is valid for outbound, too */
 818                                doexec(proggie);
 819                        if (!o_zero)
 820                                x = readwrite();
 821                } else { /* connect or udptest wasn't successful */
 822                        x = 1;                                /* exit status */
 823                        /* if we're scanning at a "one -v" verbosity level, don't print refusals.
 824                         Give it another -v if you want to see everything. */
 825                        if (o_verbose > 1 || (o_verbose && errno != ECONNREFUSED))
 826                                bb_perror_msg("%s (%s)", argv[0], themdotted);
 827                }
 828        }
 829        if (o_verbose > 1)                /* normally we don't care */
 830                fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out);
 831        return x;
 832}
 833