⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 netcat.c

📁 一个nc的源码
💻 C
📖 第 1 页 / 共 5 页
字号:
   do all the socket stuff, and return an fd for one of
	an open outbound TCP connection
	a UDP stub-socket thingie
   with appropriate socket options set up if we wanted source-routing, or
	an unconnected TCP or UDP socket to listen on.
   Examines various global o_blah flags to figure out what-all to do. */
int doconnect (rad, rp, lad, lp)
  IA * rad;
  USHORT rp;
  IA * lad;
  USHORT lp;
{
#ifndef WIN32
  register int nnetfd;
#endif
  register int rr;
  int x, y;

  errno = 0;
#ifdef WIN32
	WSASetLastError(0);
#endif
/* grab a socket; set opts */
  if (o_udpmode)
    nnetfd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  else
    nnetfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (nnetfd < 0)
    bail ("Can't get socket");
  if (nnetfd == 0)		/* might *be* zero if stdin was closed! */
    nnetfd = dup (nnetfd);	/* so fix it.  Leave the old 0 hanging. */
#ifdef WIN32
  rr = setsockopt (nnetfd, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)setsockopt_c, sizeof (setsockopt_c));
#else
  x = 1;
  rr = setsockopt (nnetfd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof (x));
#endif
  if (rr == -1)
    holler ("nnetfd reuseaddr failed");		/* ??? */
#ifdef SO_REUSEPORT	/* doesnt exist everywhere... */
#ifdef WIN32
  rr = setsockopt (nnetfd, SOL_SOCKET, SO_REUSEPORT, &c, sizeof (c));
#else
  rr = setsockopt (nnetfd, SOL_SOCKET, SO_REUSEPORT, &x, sizeof (x));
#endif
  if (rr == -1)
    holler ("nnetfd reuseport failed");		/* ??? */
#endif

/* fill in all the right sockaddr crud */
  lclend->sin_family = AF_INET;
  remend->sin_family = AF_INET;

/* if lad/lp, do appropriate binding */
  if (lad)
    memcpy (&lclend->sin_addr.s_addr, lad, sizeof (IA));
  if (lp)
    lclend->sin_port = htons (lp);
  rr = 0;
  if (lad || lp) {
    x = (int) lp;
/* try a few times for the local bind, a la ftp-data-port... */
    for (y = 4; y > 0; y--) {
      rr = bind (nnetfd, (SA *)lclend, sizeof (SA));
      if (rr == 0)
	break;
      if (errno != EADDRINUSE)
	break;
      else {
	holler ("retrying local %s:%d", inet_ntoa (lclend->sin_addr), lp);
	sleep (1);
	errno = 0;			/* clear from sleep */
      } /* if EADDRINUSE */
    } /* for y counter */
  } /* if lad or lp */
  if (rr)
    bail ("Can't grab %s:%d with bind",
	inet_ntoa(lclend->sin_addr), lp);

  if (o_listen)
    return (nnetfd);			/* thanks, that's all for today */

  memcpy (&remend->sin_addr.s_addr, rad, sizeof (IA));
  remend->sin_port = htons (rp);

/* rough format of LSRR option and explanation of weirdness.
-Option comes after IP-hdr dest addr in packet, padded to *4, and ihl > 5.
-IHL is multiples of 4, i.e. real len = ip_hl << 2.
-	type 131	1	; 0x83: copied, option class 0, number 3
-	len		1	; of *whole* option!
-	pointer		1	; nxt-hop-addr; 1-relative, not 0-relative
-	addrlist...	var	; 4 bytes per hop-addr
-	pad-to-32	var	; ones, i.e. "NOP"
-
-If we want to route A -> B via hops C and D, we must add C, D, *and* B to the
-options list.  Why?  Because when we hand the kernel A -> B with list C, D, B
-the "send shuffle" inside the kernel changes it into A -> C with list D, B and
-the outbound packet gets sent to C.  If B wasn't also in the hops list, the
-final destination would have been lost at this point.
-
-When C gets the packet, it changes it to A -> D with list C', B where C' is
-the interface address that C used to forward the packet.  This "records" the
-route hop from B's point of view, i.e. which address points "toward" B.  This
-is to make B better able to return the packets.  The pointer gets bumped by 4,
-so that D does the right thing instead of trying to forward back to C.
-
-When B finally gets the packet, it sees that the pointer is at the end of the
-LSRR list and is thus "completed".  B will then try to use the packet instead
-of forwarding it, i.e. deliver it up to some application.
-
-Note that by moving the pointer yourself, you could send the traffic directly
-to B but have it return via your preconstructed source-route.  Playing with
-this and watching "tcpdump -v" is the best way to understand what's going on.
-
-Only works for TCP in BSD-flavor kernels.  UDP is a loss; udp_input calls
-stripoptions() early on, and the code to save the srcrt is notdef'ed.
-Linux is also still a loss at 1.3.x it looks like; the lsrr code is { }...
-*/


/* if any -g arguments were given, set up source-routing.  We hit this after
   the gates are all looked up and ready to rock, any -G pointer is set,
   and gatesidx is now the *number* of hops */
  if (gatesidx) {		/* if we wanted any srcrt hops ... */
/* don't even bother compiling if we can't do IP options here! */
/* #ifdef IP_OPTIONS */
#ifndef WIN32 
    if (! optbuf) {		/* and don't already *have* a srcrt set */
      char * opp;		/* then do all this setup hair */
      optbuf = Hmalloc (48);
      opp = optbuf;
      *opp++ = IPOPT_LSRR;					/* option */
      *opp++ = (char)
	(((gatesidx + 1) * sizeof (IA)) + 3) & 0xff;		/* length */
      *opp++ = gatesptr;					/* pointer */
/* opp now points at first hop addr -- insert the intermediate gateways */
      for ( x = 0; x < gatesidx; x++) {
	memcpy (opp, gates[x]->iaddrs, sizeof (IA));
	opp += sizeof (IA);
      }
/* and tack the final destination on the end [needed!] */
      memcpy (opp, rad, sizeof (IA));
      opp += sizeof (IA);
      *opp = IPOPT_NOP;			/* alignment filler */
    } /* if empty optbuf */
/* calculate length of whole option mess, which is (3 + [hops] + [final] + 1),
   and apply it [have to do this every time through, of course] */
    x = ((gatesidx + 1) * sizeof (IA)) + 4;
    rr = setsockopt (nnetfd, IPPROTO_IP, IP_OPTIONS, optbuf, x);
    if (rr == -1)
      bail ("srcrt setsockopt fuxored");
#else /* IP_OPTIONS */
    holler ("Warning: source routing unavailable on this machine, ignoring");
#endif /* IP_OPTIONS*/
  } /* if gatesidx */

/* wrap connect inside a timer, and hit it */
  arm (1, o_wait);
  if (setjmp (jbuf) == 0) {
    rr = connect (nnetfd, (SA *)remend, sizeof (SA));
  } else {				/* setjmp: connect failed... */
    rr = -1;
#ifdef WIN32
    WSASetLastError(WSAETIMEDOUT);			/* fake it */
#else
    errno = ETIMEDOUT;			/* fake it */
#endif
  }
  arm (0, 0);
  if (rr == 0)
    return (nnetfd);
#ifdef WIN32
  errno = h_errno;
  closesocket (nnetfd);
  WSASetLastError(errno); /* don't want to lose connect error */
#else
  close (nnetfd);			/* clean up junked socket FD!! */
#endif
  return (-1);
} /* doconnect */

/* dolisten :
   just like doconnect, and in fact calls a hunk of doconnect, but listens for
   incoming and returns an open connection *from* someplace.  If we were
   given host/port args, any connections from elsewhere are rejected.  This
   in conjunction with local-address binding should limit things nicely... */
int dolisten (rad, rp, lad, lp)
  IA * rad;
  USHORT rp;
  IA * lad;
  USHORT lp;
{
  register int nnetfd;
  register int rr;
  HINF * whozis = NULL;
  int x;
  char * cp;
  USHORT z;
  errno = 0;

/* Pass everything off to doconnect, who in o_listen mode just gets a socket */
  nnetfd = doconnect (rad, rp, lad, lp);
  if (nnetfd <= 0)
    return (-1);
  if (o_udpmode) {			/* apparently UDP can listen ON */
    if (! lp)				/* "port 0",  but that's not useful */
      bail ("UDP listen needs -p arg");
  } else {
    rr = listen (nnetfd, 1);		/* gotta listen() before we can get */
    if (rr < 0)				/* our local random port.  sheesh. */
      bail ("local listen fuxored");
  }

/* I can't believe I have to do all this to get my own goddamn bound address
   and port number.  It should just get filled in during bind() or something.
   All this is only useful if we didn't say -p for listening, since if we
   said -p we *know* what port we're listening on.  At any rate we won't bother
   with it all unless we wanted to see it, although listening quietly on a
   random unknown port is probably not very useful without "netstat". */
  if (o_verbose) {
    x = sizeof (SA);		/* how 'bout getsockNUM instead, pinheads?! */
    rr = getsockname (nnetfd, (SA *) lclend, &x);
      if (rr < 0)
	holler ("local getsockname failed");
    strcpy (bigbuf_net, "listening on [");	/* buffer reuse... */
      if (lclend->sin_addr.s_addr)
	strcat (bigbuf_net, inet_ntoa (lclend->sin_addr));
      else
	strcat (bigbuf_net, "any");
    strcat (bigbuf_net, "] %d ...");
    z = ntohs (lclend->sin_port);
    holler (bigbuf_net, z);
  } /* verbose -- whew!! */

/* UDP is a speeeeecial case -- we have to do I/O *and* get the calling
   party's particulars all at once, listen() and accept() don't apply.
   At least in the BSD universe, however, recvfrom/PEEK is enough to tell
   us something came in, and we can set things up so straight read/write
   actually does work after all.  Yow.  YMMV on strange platforms!  */
  if (o_udpmode) {
    x = sizeof (SA);		/* retval for recvfrom */
    arm (2, o_wait);		/* might as well timeout this, too */
    if (setjmp (jbuf) == 0) {	/* do timeout for initial connect */
      rr = recvfrom		/* and here we block... */
	(nnetfd, bigbuf_net, BIGSIZ, MSG_PEEK, (SA *) remend, &x);
Debug (("dolisten/recvfrom ding, rr = %d, netbuf %s ", rr, bigbuf_net))
    } else
      goto dol_tmo;		/* timeout */
    arm (0, 0);
/* I'm not completely clear on how this works -- BSD seems to make UDP
   just magically work in a connect()ed context, but we'll undoubtedly run
   into systems this deal doesn't work on.  For now, we apparently have to
   issue a connect() on our just-tickled socket so we can write() back.
   Again, why the fuck doesn't it just get filled in and taken care of?!
   This hack is anything but optimal.  Basically, if you want your listener
   to also be able to send data back, you need this connect() line, which
   also has the side effect that now anything from a different source or even a
   different port on the other end won't show up and will cause ICMP errors.
   I guess that's what they meant by "connect".
   Let's try to remember what the "U" is *really* for, eh?
*/
    rr = connect (nnetfd, (SA *)remend, sizeof (SA));
    goto whoisit;
  } /* o_udpmode */

/* fall here for TCP */
  x = sizeof (SA);		/* retval for accept */
  arm (2, o_wait);		/* wrap this in a timer, too; 0 = forever */
  if (setjmp (jbuf) == 0) {
    rr = accept (nnetfd, (SA *)remend, &x);
  } else
    goto dol_tmo;		/* timeout */
  arm (0, 0);
#ifdef WIN32
  closesocket (nnetfd);
#else
  close (nnetfd);		/* dump the old socket */
#endif
  nnetfd = rr;			/* here's our new one */

whoisit:
  if (rr < 0)
    goto dol_err;		/* bail out if any errors so far */

/* Various things that follow temporarily trash bigbuf_net, which might contain
   a copy of any recvfrom()ed packet, but we'll read() another copy later. */

/* If we can, look for any IP options.  Useful for testing the receiving end of
   such things, and is a good exercise in dealing with it.  We do this before
   the connect message, to ensure that the connect msg is uniformly the LAST
   thing to emerge after all the intervening crud.  Doesn't work for UDP on
   any machines I've tested, but feel free to surprise me. */
/* #ifdef IP_OPTIONS */
#ifndef WIN32
  if (! o_verbose)			/* if we wont see it, we dont care */
    goto dol_noop;
  optbuf = Hmalloc (40);
  x = 40;
  rr = getsockopt (nnetfd, IPPROTO_IP, IP_OPTIONS, optbuf, &x);
  if (rr < 0)
    holler ("getsockopt failed");
Debug (("ipoptions ret len %d", x))
  if (x) {				/* we've got options, lessee em... */
    unsigned char * q = (unsigned char *) optbuf;
    char * p = bigbuf_net;		/* local variables, yuk! */
    char * pp = &bigbuf_net[128];	/* get random space farther out... */
    memset (bigbuf_net, 0, 256);	/* clear it all first */
    while (x > 0) {
	sprintf (pp, "%2.2x ", *q);	/* clumsy, but works: turn into hex */
	strcat (p, pp);			/* and build the final string */
	q++; p++;
	x--;
    }
    holler ("IP options: %s", bigbuf_net);
  } /* if x, i.e. any options */
dol_noop:
#endif /* IP_OPTIONS */

/* find out what address the connection was *to* on our end, in case we're
   doing a listen-on-any on a multihomed machine.  This allows one to
   offer different services via different alias addresses, such as the
   "virtual web site" hack. */
  memset (bigbuf_net, 0, 64);
  cp = &bigbuf_net[32];
  x = sizeof (SA);
  rr = getsockname (nnetfd, (SA *) lclend, &x);
  if (rr < 0)
    holler ("post-rcv getsockname failed");
  strcpy (cp, inet_ntoa (lclend->sin_addr));


/* now check out who it is.  We don't care about mismatched DNS names here,
   but any ADDR and PORT we specified had better fucking well match the caller.
   Converting from addr to inet_ntoa and back again is a bit of a kludge, but
   gethostpoop wants a string and there's much gnarlier code out there already,
   so I don't feel bad.
   The *real* question is why BFD sockets wasn't designed to allow listens for
   connections *from* specific hosts/ports, instead of requiring the caller to
   accept the connection and then reject undesireable ones by closing. */
  z = ntohs (remend->sin_port);
  strcpy (bigbuf_net, inet_ntoa (remend->sin_addr));
  whozis = gethostpoop (bigbuf_net, o_nflag);
  errno = 0;
  x = 0;				/* use as a flag... */
  if (rad)
    if (memcmp (rad, whozis->iaddrs, sizeof (SA)))
      x = 1;
  if (rp)
    if (z != rp)
      x = 1;
  if (x)					/* guilty! */
	bail ("invalid connection to [%s] from %s [%s] %d",
	cp, whozis->name, whozis->addrs[0], z);
  	holler ("connect to [%s] from %s [%s] %d",		/* oh, you're okay.. */
	cp, whozis->name, whozis->addrs[0], z);

  return (nnetfd);				/* open! */

dol_tmo:
  errno = ETIMEDOUT;			/* fake it */
dol_err:
#ifdef WIN32
  closesocket (nnetfd);
#else
  close (nnetfd);
#endif
  return (-1);
} /* dolisten */

/* udptest :
   fire a couple of packets at a UDP target port, just to see if it's really
   there.  On BSD kernels, ICMP host/port-unreachable errors get delivered to
   our socket as ECONNREFUSED write errors.  On SV kernels, we lose; we'll have
   to collect and analyze raw ICMP ourselves a la satan's probe_udp_ports
   backend.  Guess where could swipe the appropriate code from...

   Use the time delay between writes if given, otherwise use the "tcp ping"
   trick for getting the RTT.  [I got that idea from pluvius, and warped it.]
   Return either the original fd, or clean up and return -1. */
udptest (fd, where)
  int fd;
  IA * where;
{
  register int rr;

#ifdef WIN32
  rr = send (fd, bigbuf_in, 1, 0);
#else
  rr = write (fd, bigbuf_in, 1);
#endif
  if (rr != 1)
    holler ("udptest first write failed?! errno %d", errno);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -