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                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                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 && x) {    /* we've got options, lessee em... */
 381                        bin2hex(bigbuf_net, optbuf, x);
 382                        bigbuf_net[2*x] = '\0';
 383                        fprintf(stderr, "IP options: %s\n", bigbuf_net);
 384                }
 385#endif
 386
 387        /* now check out who it is.  We don't care about mismatched DNS names here,
 388         but any ADDR and PORT we specified had better fucking well match the caller.
 389         Converting from addr to inet_ntoa and back again is a bit of a kludge, but
 390         gethostpoop wants a string and there's much gnarlier code out there already,
 391         so I don't feel bad.
 392         The *real* question is why BFD sockets wasn't designed to allow listens for
 393         connections *from* specific hosts/ports, instead of requiring the caller to
 394         accept the connection and then reject undesireable ones by closing.
 395         In other words, we need a TCP MSG_PEEK. */
 396        /* bbox: removed most of it */
 397                lcladdr = xmalloc_sockaddr2dotted(&ouraddr->u.sa);
 398                remaddr = xmalloc_sockaddr2dotted(&remend.u.sa);
 399                remhostname = o_nflag ? remaddr : xmalloc_sockaddr2host(&remend.u.sa);
 400                fprintf(stderr, "connect to %s from %s (%s)\n",
 401                                lcladdr, remhostname, remaddr);
 402                free(lcladdr);
 403                free(remaddr);
 404                if (!o_nflag)
 405                        free(remhostname);
 406        }
 407}
 408
 409/* udptest:
 410 fire a couple of packets at a UDP target port, just to see if it's really
 411 there.  On BSD kernels, ICMP host/port-unreachable errors get delivered to
 412 our socket as ECONNREFUSED write errors.  On SV kernels, we lose; we'll have
 413 to collect and analyze raw ICMP ourselves a la satan's probe_udp_ports
 414 backend.  Guess where one could swipe the appropriate code from...
 415
 416 Use the time delay between writes if given, otherwise use the "tcp ping"
 417 trick for getting the RTT.  [I got that idea from pluvius, and warped it.]
 418 Return either the original fd, or clean up and return -1. */
 419#if ENABLE_NC_EXTRA
 420static int udptest(void)
 421{
 422        int rr;
 423
 424        rr = write(netfd, bigbuf_in, 1);
 425        if (rr != 1)
 426                bb_perror_msg("udptest first write");
 427
 428        if (o_wait)
 429                sleep(o_wait); // can be interrupted! while (t) nanosleep(&t)?
 430        else {
 431        /* use the tcp-ping trick: try connecting to a normally refused port, which
 432         causes us to block for the time that SYN gets there and RST gets back.
 433         Not completely reliable, but it *does* mostly work. */
 434        /* Set a temporary connect timeout, so packet filtration doesnt cause
 435         us to hang forever, and hit it */
 436                o_wait = 5;                     /* enough that we'll notice?? */
 437                rr = xsocket(ouraddr->u.sa.sa_family, SOCK_STREAM, 0);
 438                set_nport(themaddr, htons(SLEAZE_PORT));
 439                connect_w_timeout(rr);
 440                /* don't need to restore themaddr's port, it's not used anymore */
 441                close(rr);
 442                o_wait = 0; /* restore */
 443        }
 444
 445        rr = write(netfd, bigbuf_in, 1);
 446        return (rr != 1); /* if rr == 1, return 0 (success) */
 447}
 448#else
 449int udptest(void);
 450#endif
 451
 452/* oprint:
 453 Hexdump bytes shoveled either way to a running logfile, in the format:
 454 D offset       -  - - - --- 16 bytes --- - - -  -     # .... ascii .....
 455 where "which" sets the direction indicator, D:
 456 0 -- sent to network, or ">"
 457 1 -- rcvd and printed to stdout, or "<"
 458 and "buf" and "n" are data-block and length.  If the current block generates
 459 a partial line, so be it; we *want* that lockstep indication of who sent
 460 what when.  Adapted from dgaudet's original example -- but must be ripping
 461 *fast*, since we don't want to be too disk-bound... */
 462#if ENABLE_NC_EXTRA
 463static void oprint(int direction, unsigned char *p, unsigned bc)
 464{
 465        unsigned obc;           /* current "global" offset */
 466        unsigned x;
 467        unsigned char *op;      /* out hexdump ptr */
 468        unsigned char *ap;      /* out asc-dump ptr */
 469        unsigned char stage[100];
 470
 471        if (bc == 0)
 472                return;
 473
 474        obc = wrote_net; /* use the globals! */
 475        if (direction == '<')
 476                obc = wrote_out;
 477        stage[0] = direction;
 478        stage[59] = '#'; /* preload separator */
 479        stage[60] = ' ';
 480
 481        do {    /* for chunk-o-data ... */
 482                x = 16;
 483                if (bc < 16) {
 484                        /* memset(&stage[bc*3 + 11], ' ', 16*3 - bc*3); */
 485                        memset(&stage[11], ' ', 16*3);
 486                        x = bc;
 487                }
 488                sprintf((char *)&stage[1], " %8.8x ", obc);  /* xxx: still slow? */
 489                bc -= x;          /* fix current count */
 490                obc += x;         /* fix current offset */
 491                op = &stage[11];  /* where hex starts */
 492                ap = &stage[61];  /* where ascii starts */
 493
 494                do {  /* for line of dump, however long ... */
 495                        *op++ = 0x20 | bb_hexdigits_upcase[*p >> 4];
 496                        *op++ = 0x20 | bb_hexdigits_upcase[*p & 0x0f];
 497                        *op++ = ' ';
 498                        if ((*p > 31) && (*p < 127))
 499                                *ap = *p;   /* printing */
 500                        else
 501                                *ap = '.';  /* nonprinting, loose def */
 502                        ap++;
 503                        p++;
 504                } while (--x);
 505                *ap++ = '\n';  /* finish the line */
 506                xwrite(ofd, stage, ap - stage);
 507        } while (bc);
 508}
 509#else
 510void oprint(int direction, unsigned char *p, unsigned bc);
 511#endif
 512
 513/* readwrite:
 514 handle stdin/stdout/network I/O.  Bwahaha!! -- the select loop from hell.
 515 In this instance, return what might become our exit status. */
 516static int readwrite(void)
 517{
 518        int rr;
 519        char *zp = zp; /* gcc */  /* stdin buf ptr */
 520        char *np = np;            /* net-in buf ptr */
 521        unsigned rzleft;
 522        unsigned rnleft;
 523        unsigned netretry;              /* net-read retry counter */
 524        unsigned wretry;                /* net-write sanity counter */
 525        unsigned wfirst;                /* one-shot flag to skip first net read */
 526
 527        /* if you don't have all this FD_* macro hair in sys/types.h, you'll have to
 528         either find it or do your own bit-bashing: *ding1 |= (1 << fd), etc... */
 529        FD_SET(netfd, &ding1);                /* global: the net is open */
 530        netretry = 2;
 531        wfirst = 0;
 532        rzleft = rnleft = 0;
 533        if (o_interval)
 534                sleep(o_interval);                /* pause *before* sending stuff, too */
 535
 536        errno = 0;                        /* clear from sleep, close, whatever */
 537        /* and now the big ol' select shoveling loop ... */
 538        while (FD_ISSET(netfd, &ding1)) {        /* i.e. till the *net* closes! */
 539                wretry = 8200;                        /* more than we'll ever hafta write */
 540                if (wfirst) {                        /* any saved stdin buffer? */
 541                        wfirst = 0;                        /* clear flag for the duration */
 542                        goto shovel;                        /* and go handle it first */
 543                }
 544                ding2 = ding1;                        /* FD_COPY ain't portable... */
 545        /* some systems, notably linux, crap into their select timers on return, so
 546         we create a expendable copy and give *that* to select.  */
 547                if (o_wait) {
 548                        struct timeval tmp_timer;
 549                        tmp_timer.tv_sec = o_wait;
 550                        tmp_timer.tv_usec = 0;
 551                /* highest possible fd is netfd (3) */
 552                        rr = select(netfd+1, &ding2, NULL, NULL, &tmp_timer);
 553                } else
 554                        rr = select(netfd+1, &ding2, NULL, NULL, NULL);
 555                if (rr < 0 && errno != EINTR) {                /* might have gotten ^Zed, etc */
 556                        holler_perror("select");
 557                        close(netfd);
 558                        return 1;
 559                }
 560        /* if we have a timeout AND stdin is closed AND we haven't heard anything
 561         from the net during that time, assume it's dead and close it too. */
 562                if (rr == 0) {
 563                        if (!FD_ISSET(STDIN_FILENO, &ding1))
 564                                netretry--;                        /* we actually try a coupla times. */
 565                        if (!netretry) {
 566                                if (o_verbose > 1)                /* normally we don't care */
 567                                        fprintf(stderr, "net timeout\n");
 568                                close(netfd);
 569                                return 0;                        /* not an error! */
 570                        }
 571                } /* select timeout */
 572        /* xxx: should we check the exception fds too?  The read fds seem to give
 573         us the right info, and none of the examples I found bothered. */
 574
 575        /* Ding!!  Something arrived, go check all the incoming hoppers, net first */
 576                if (FD_ISSET(netfd, &ding2)) {                /* net: ding! */
 577                        rr = read(netfd, bigbuf_net, BIGSIZ);
 578                        if (rr <= 0) {
 579                                if (rr < 0 && o_verbose > 1) {
 580                                        /* nc 1.10 doesn't do this */
 581                                        bb_perror_msg("net read");
 582                                }
 583                                FD_CLR(netfd, &ding1);                /* net closed, we'll finish up... */
 584                                rzleft = 0;                        /* can't write anymore: broken pipe */
 585                        } else {
 586                                rnleft = rr;
 587                                np = bigbuf_net;
 588                        }
 589Debug("got %d from the net, errno %d", rr, errno);
 590                } /* net:ding */
 591
 592        /* if we're in "slowly" mode there's probably still stuff in the stdin
 593         buffer, so don't read unless we really need MORE INPUT!  MORE INPUT! */
 594                if (rzleft)
 595                        goto shovel;
 596
 597        /* okay, suck more stdin */
 598                if (FD_ISSET(STDIN_FILENO, &ding2)) {                /* stdin: ding! */
 599                        rr = read(STDIN_FILENO, bigbuf_in, BIGSIZ);
 600        /* Considered making reads here smaller for UDP mode, but 8192-byte
 601         mobygrams are kinda fun and exercise the reassembler. */
 602                        if (rr <= 0) {                        /* at end, or fukt, or ... */
 603                                FD_CLR(STDIN_FILENO, &ding1);                /* disable and close stdin */
 604                                close(STDIN_FILENO);
 605// Does it make sense to shutdown(net_fd, SHUT_WR)
 606// to let other side know that we won't write anything anymore?
 607// (and what about keeping compat if we do that?)
 608                        } else {
 609                                rzleft = rr;
 610                                zp = bigbuf_in;
 611                        }
 612                } /* stdin:ding */
 613 shovel:
 614        /* now that we've dingdonged all our thingdings, send off the results.
 615         Geez, why does this look an awful lot like the big loop in "rsh"? ...
 616         not sure if the order of this matters, but write net -> stdout first. */
 617
 618        /* sanity check.  Works because they're both unsigned... */
 619                if ((rzleft > 8200) || (rnleft > 8200)) {
 620                        holler_error("bogus buffers: %u, %u", rzleft, rnleft);
 621                        rzleft = rnleft = 0;
 622                }
 623        /* net write retries sometimes happen on UDP connections */
 624                if (!wretry) {                        /* is something hung? */
 625                        holler_error("too many output retries");
 626                        return 1;
 627                }
 628                if (rnleft) {
 629                        rr = write(STDOUT_FILENO, np, rnleft);
 630                        if (rr > 0) {
 631                                if (o_ofile) /* log the stdout */
 632                                        oprint('<', (unsigned char *)np, rr);
 633                                np += rr;                        /* fix up ptrs and whatnot */
 634                                rnleft -= rr;                        /* will get sanity-checked above */
 635                                wrote_out += rr;                /* global count */
 636                        }
 637Debug("wrote %d to stdout, errno %d", rr, errno);
 638                } /* rnleft */
 639                if (rzleft) {
 640                        if (o_interval)                        /* in "slowly" mode ?? */
 641                                rr = findline(zp, rzleft);
 642                        else
 643                                rr = rzleft;
 644                        rr = write(netfd, zp, rr);        /* one line, or the whole buffer */
 645                        if (rr > 0) {
 646                                if (o_ofile) /* log what got sent */
 647                                        oprint('>', (unsigned char *)zp, rr);
 648                                zp += rr;
 649                                rzleft -= rr;
 650                                wrote_net += rr;                /* global count */
 651                        }
 652Debug("wrote %d to net, errno %d", rr, errno);
 653                } /* rzleft */
 654                if (o_interval) {                        /* cycle between slow lines, or ... */
 655                        sleep(o_interval);
 656                        errno = 0;                        /* clear from sleep */
 657                        continue;                        /* ...with hairy select loop... */
 658                }
 659                if ((rzleft) || (rnleft)) {                /* shovel that shit till they ain't */
 660                        wretry--;                        /* none left, and get another load */
 661                        goto shovel;
 662                }
 663        } /* while ding1:netfd is open */
 664
 665        /* XXX: maybe want a more graceful shutdown() here, or screw around with
 666         linger times??  I suspect that I don't need to since I'm always doing
 667         blocking reads and writes and my own manual "last ditch" efforts to read
 668         the net again after a timeout.  I haven't seen any screwups yet, but it's
 669         not like my test network is particularly busy... */
 670        close(netfd);
 671        return 0;
 672} /* readwrite */
 673
 674/* main: now we pull it all together... */
 675int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 676int nc_main(int argc, char **argv)
 677{
 678        char *str_p, *str_s;
 679        IF_NC_EXTRA(char *str_i, *str_o;)
 680        char *themdotted = themdotted; /* gcc */
 681        char **proggie;
 682        int x;
 683        unsigned o_lport = 0;
 684
 685        INIT_G();
 686
 687        /* catch a signal or two for cleanup */
 688        bb_signals(0
 689                + (1 << SIGINT)
 690                + (1 << SIGQUIT)
 691                + (1 << SIGTERM)
 692                , catch);
 693        /* and suppress others... */
 694        bb_signals(0
 695#ifdef SIGURG
 696                + (1 << SIGURG)
 697#endif
 698                + (1 << SIGPIPE) /* important! */
 699                , SIG_IGN);
 700
 701        proggie = argv;
 702        while (*++proggie) {
 703                if (strcmp(*proggie, "-e") == 0) {
 704                        *proggie = NULL;
 705                        argc = proggie - argv;
 706                        proggie++;
 707                        goto e_found;
 708                }
 709        }
 710        proggie = NULL;
 711 e_found:
 712
 713        // -g -G -t -r deleted, unimplemented -a deleted too
 714        opt_complementary = "?2:vv:w+"; /* max 2 params; -v is a counter; -w N */
 715        getopt32(argv, "hnp:s:uvw:" IF_NC_SERVER("l")
 716                        IF_NC_EXTRA("i:o:z"),
 717                        &str_p, &str_s, &o_wait
 718                        IF_NC_EXTRA(, &str_i, &str_o, &o_verbose));
 719        argv += optind;
 720#if ENABLE_NC_EXTRA
 721        if (option_mask32 & OPT_i) /* line-interval time */
 722                o_interval = xatou_range(str_i, 1, 0xffff);
 723#endif
 724        //if (option_mask32 & OPT_l) /* listen mode */
 725        //if (option_mask32 & OPT_n) /* numeric-only, no DNS lookups */
 726        //if (option_mask32 & OPT_o) /* hexdump log */
 727        if (option_mask32 & OPT_p) { /* local source port */
 728                o_lport = bb_lookup_port(str_p, o_udpmode ? "udp" : "tcp", 0);
 729                if (!o_lport)
 730                        bb_error_msg_and_die("bad local port '%s'", str_p);
 731        }
 732        //if (option_mask32 & OPT_r) /* randomize various things */
 733        //if (option_mask32 & OPT_u) /* use UDP */
 734        //if (option_mask32 & OPT_v) /* verbose */
 735        //if (option_mask32 & OPT_w) /* wait time */
 736        //if (option_mask32 & OPT_z) /* little or no data xfer */
 737
 738        /* We manage our fd's so that they are never 0,1,2 */
 739        /*bb_sanitize_stdio(); - not needed */
 740
 741        if (argv[0]) {
 742                themaddr = xhost2sockaddr(argv[0],
 743                        argv[1]
 744                        ? bb_lookup_port(argv[1], o_udpmode ? "udp" : "tcp", 0)
 745                        : 0);
 746        }
 747
 748        /* create & bind network socket */
 749        x = (o_udpmode ? SOCK_DGRAM : SOCK_STREAM);
 750        if (option_mask32 & OPT_s) { /* local address */
 751                /* if o_lport is still 0, then we will use random port */
 752                ouraddr = xhost2sockaddr(str_s, o_lport);
 753#ifdef BLOAT
 754                /* prevent spurious "UDP listen needs !0 port" */
 755                o_lport = get_nport(ouraddr);
 756                o_lport = ntohs(o_lport);
 757#endif
 758                x = xsocket(ouraddr->u.sa.sa_family, x, 0);
 759        } else {
 760                /* We try IPv6, then IPv4, unless addr family is
 761                 * implicitly set by way of remote addr/port spec */
 762                x = xsocket_type(&ouraddr,
 763                                (themaddr ? themaddr->u.sa.sa_family : AF_UNSPEC),
 764                                x);
 765                if (o_lport)
 766                        set_nport(ouraddr, htons(o_lport));
 767        }
 768        xmove_fd(x, netfd);
 769        setsockopt_reuseaddr(netfd);
 770        if (o_udpmode)
 771                socket_want_pktinfo(netfd);
 772        if (!ENABLE_FEATURE_UNIX_LOCAL
 773         || o_listen
 774         || ouraddr->u.sa.sa_family != AF_UNIX
 775        ) {
 776                xbind(netfd, &ouraddr->u.sa, ouraddr->len);
 777        }
 778#if 0
 779        setsockopt(netfd, SOL_SOCKET, SO_RCVBUF, &o_rcvbuf, sizeof o_rcvbuf);
 780        setsockopt(netfd, SOL_SOCKET, SO_SNDBUF, &o_sndbuf, sizeof o_sndbuf);
 781#endif
 782
 783#ifdef BLOAT
 784        if (OPT_l && (option_mask32 & (OPT_u|OPT_l)) == (OPT_u|OPT_l)) {
 785                /* apparently UDP can listen ON "port 0",
 786                 but that's not useful */
 787                if (!o_lport)
 788                        bb_error_msg_and_die("UDP listen needs nonzero -p port");
 789        }
 790#endif
 791
 792        FD_SET(STDIN_FILENO, &ding1);                        /* stdin *is* initially open */
 793        if (proggie) {
 794                close(0); /* won't need stdin */
 795                option_mask32 &= ~OPT_o; /* -o with -e is meaningless! */
 796        }
 797#if ENABLE_NC_EXTRA
 798        if (o_ofile)
 799                xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), ofd);
 800#endif
 801
 802        if (o_listen) {
 803                dolisten();
 804                /* dolisten does its own connect reporting */
 805                if (proggie) /* -e given? */
 806                        doexec(proggie);
 807                x = readwrite(); /* it even works with UDP! */
 808        } else {
 809                /* Outbound connects.  Now we're more picky about args... */
 810                if (!themaddr)
 811                        bb_error_msg_and_die("no destination");
 812
 813                remend = *themaddr;
 814                if (o_verbose)
 815                        themdotted = xmalloc_sockaddr2dotted(&themaddr->u.sa);
 816
 817                x = connect_w_timeout(netfd);
 818                if (o_zero && x == 0 && o_udpmode)        /* if UDP scanning... */
 819                        x = udptest();
 820                if (x == 0) {                        /* Yow, are we OPEN YET?! */
 821                        if (o_verbose)
 822                                fprintf(stderr, "%s (%s) open\n", argv[0], themdotted);
 823                        if (proggie)                        /* exec is valid for outbound, too */
 824                                doexec(proggie);
 825                        if (!o_zero)
 826                                x = readwrite();
 827                } else { /* connect or udptest wasn't successful */
 828                        x = 1;                                /* exit status */
 829                        /* if we're scanning at a "one -v" verbosity level, don't print refusals.
 830                         Give it another -v if you want to see everything. */
 831                        if (o_verbose > 1 || (o_verbose && errno != ECONNREFUSED))
 832                                bb_perror_msg("%s (%s)", argv[0], themdotted);
 833                }
 834        }
 835        if (o_verbose > 1)                /* normally we don't care */
 836                fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out);
 837        return x;
 838}
 839