📄 netcat.c
字号:
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 + -