I think I finally understand how it works, Thanks to you
lfasmpao.
correct me if I'm wrong but below is what I think the closest explanation I can find.
Your method of penetrating NATs is probably two-fold which will be describe below.
In order for the full tunnel to be established, the client side needs to
know the public IP address of the server, and the server needs to learn
the public IP address of the client.
However, in a true client-server model, the server doesn't know the client IP
until the client connects, and NATs will normally drop unknown incoming packets.
In openhttp-puncher, the server also does not need to know the client IP address.
I presume here is how the openhttp-puncher server learns the IP address of the client:
You get around this by having the client "pretend" to be a random hop on
the Internet. You essentially using the same technology a traceroute uses
to detect hops on the Internet, but you're doing the reverse in order to
penetrate the NAT.
Specifically, when the server starts up, it begins sending fixed ICMP echo
request packets to the fixed address something like 3.3.3.3. We expect that these packets
won't be returned.
Now, 3.3.3.3 is
not a host we have any access to, nor will we end up spoofing
it. Instead, when a client wants to connect, the client (which knows the server
IP address) sends an ICMP Time Exceeded packet to the server. The ICMP packet
includes the "original" fixed packet that the server was sending to 3.3.3.3.
Why? Well, we're pretending to be a hop on the Internet, politely telling the
server that its original "ICMP echo request" packet couldn't be delivered.
Your NAT, being the gapingly open device it is, is nice enough to notice that
the packet
inside the ICMP time exceeded packet matches the packet the server
sent out. It then forwards the ICMP time exceeded back to the server behind
the NAT,
including the full IP header from the client, thus allowing the
server to know what the client IP address is!
Server (1.2.3.4): ICMP Echo Request -> 3.3.3.3
...
Server (1.2.3.4): ICMP Echo Request -> 3.3.3.3
...
Server (1.2.3.4): ICMP Echo Request -> 3.3.3.3
...
Client (6.7.8.9): ICMP Time Exceeded (includes ICMP Echo Request to 3.3.3.3) -> 1.2.3.4
Server's NAT: Sees server's Echo Request in client's Time Exceeded packet,
sends entire packet to server because it matches server's outgoing packet
Don't believe me? Just traceroute any host behind your NAT. You'll notice
incoming packets coming in from random IP addresses your router knows
nothing about. Your router knows to send those back to you, rather than another
client on your network, based off of the data inside the ICMP time exceeded packet.
Now, the server has only learned the client IP address. We still have no
method to send any additional data. For the full communication, we use the
same method used in my previous software, chownat, to penetrate both NATs.
Example of a client behind a NAT talking to a machine NOT behind a NAT:
Machine A -> NAT A -> net -> quake server
Machine A sends a UDP packet to quake server, opening a "session".
NAT A sees this and says:
"If any UDP packets come back soon with the same host and port info,
you're routing it to machine A."
Quake server sends UDP packets back, hits NAT A, and NAT A seeing the right
hosts and ports, sends it to machine A. Machine A and quake server are now
able to communicate without any problem.
Now here is how openhttp-puncher works now that client and server know each others IP.
Goal is: Machine A (ssh client) -> NAT A -> net -> NAT B -> Machine B (ssh server)
When you start up the openhttp-puncher server on machine B, it slowly fires off
UDP packets to machine A. Of course, NAT A is not expecting these so it
drops every one of them. Machine B does not stop.
Once you begin the openhttp-puncher client on machine A, it begins sending UDP
packets to machine B. Note: openhttp-puncher defaults source and destination
ports to 8888. Any unprivileged user can set UDP source and dest ports.
Normally the UDP packets that machine A is sending to NAT B would get dropped.
However, since machine B is sending similar packets OUT, NAT B assumes
these are responses and lets them back in. Once machine B sees these packets,
it sends handshake packets back to machine A. These packets will not get
dropped by NAT A because of the same reason: NAT A sees packets going out, and
the packets coming back to the NAT look like responses to the ones going out.
Finally, both sides are fully communicating over UDP, allowing protocols that
run over TCP to tunnel through.
Note: I only assume but i'm sure that there is a keep-alive process on the openhttp-puncher server and client that
always keeps the UDP "session" active. The packets it sends usually have a 0 byte
payload and are only sent when the client is not sending data out. Otherwise,
the fastest it will possibly send the keep-alive packets is one packet every 5
seconds. If any other type of data is traveling through the tunnel, no
keep-alive packets will be transmitted.