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