aboutsummaryrefslogtreecommitdiff
path: root/src/nat
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2011-06-27 20:22:33 +0000
committerChristian Grothoff <christian@grothoff.org>2011-06-27 20:22:33 +0000
commit436f9a3345ae1f9276593ad34d8606a5de8fceeb (patch)
tree8a1173c23815cf211c0c1ae91d35dc784043682c /src/nat
parent836d9e75481db1162d1c93c891f0be2d8f2766f4 (diff)
downloadgnunet-436f9a3345ae1f9276593ad34d8606a5de8fceeb.tar.gz
gnunet-436f9a3345ae1f9276593ad34d8606a5de8fceeb.zip
moving NAT client/server to NAT library
Diffstat (limited to 'src/nat')
-rwxr-xr-xsrc/nat/gnunet-nat-client-script.sh4
-rw-r--r--src/nat/gnunet-nat-client-windows.c550
-rw-r--r--src/nat/gnunet-nat-client.c527
-rwxr-xr-xsrc/nat/gnunet-nat-server-script.sh4
-rw-r--r--src/nat/gnunet-nat-server-windows.c653
-rw-r--r--src/nat/gnunet-nat-server.c651
6 files changed, 2389 insertions, 0 deletions
diff --git a/src/nat/gnunet-nat-client-script.sh b/src/nat/gnunet-nat-client-script.sh
new file mode 100755
index 000000000..4e4ccafad
--- /dev/null
+++ b/src/nat/gnunet-nat-client-script.sh
@@ -0,0 +1,4 @@
1#!/bin/sh
2IP=`ifconfig | grep inet | head -n1 | awk '{print $2}' | sed -e "s/addr://"`
3echo "Using IP $IP, trying to connect to $1"
4./gnunet-nat-client-udp $IP $1
diff --git a/src/nat/gnunet-nat-client-windows.c b/src/nat/gnunet-nat-client-windows.c
new file mode 100644
index 000000000..2e7c8a86e
--- /dev/null
+++ b/src/nat/gnunet-nat-client-windows.c
@@ -0,0 +1,550 @@
1/*
2 This file is part of GNUnet.
3 (C) 2010 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/transport/gnunet-nat-client-windows.c
23 * @brief Tool to help bypass NATs using ICMP method; must run as
24 * root (SUID will do) or administrator on W32
25 * This code will work under GNU/Linux or W32.
26 * @author Nathan Evans
27 *
28 * This program will send ONE ICMP message using RAW sockets
29 * to the IP address specified as the second argument. Since
30 * it uses RAW sockets, it must be installed SUID or run as 'root'.
31 * In order to keep the security risk of the resulting SUID binary
32 * minimal, the program ONLY opens the RAW socket with root
33 * privileges, then drops them and only then starts to process
34 * command line arguments. The code also does not link against
35 * any shared libraries (except libc) and is strictly minimal
36 * (except for checking for errors). The following list of people
37 * have reviewed this code and considered it safe since the last
38 * modification (if you reviewed it, please have your name added
39 * to the list):
40 *
41 * - Christian Grothoff
42 * - Nathan Evans
43 */
44#define _GNU_SOURCE
45
46#include <winsock2.h>
47#include <ws2tcpip.h>
48#include <sys/time.h>
49#include <sys/types.h>
50#include <unistd.h>
51#include <stdio.h>
52#include <string.h>
53#include <errno.h>
54#include <stdlib.h>
55#include <stdint.h>
56#include <time.h>
57
58
59#define ICMP_ECHO 8
60#define IPDEFTTL 64
61#define ICMP_TIME_EXCEEDED 11
62
63/**
64 * Must match IP given in the server.
65 */
66#define DUMMY_IP "192.0.2.86"
67
68#define NAT_TRAV_PORT 22225
69
70/**
71 * IPv4 header.
72 */
73struct ip_header
74{
75
76 /**
77 * Version (4 bits) + Internet header length (4 bits)
78 */
79 uint8_t vers_ihl;
80
81 /**
82 * Type of service
83 */
84 uint8_t tos;
85
86 /**
87 * Total length
88 */
89 uint16_t pkt_len;
90
91 /**
92 * Identification
93 */
94 uint16_t id;
95
96 /**
97 * Flags (3 bits) + Fragment offset (13 bits)
98 */
99 uint16_t flags_frag_offset;
100
101 /**
102 * Time to live
103 */
104 uint8_t ttl;
105
106 /**
107 * Protocol
108 */
109 uint8_t proto;
110
111 /**
112 * Header checksum
113 */
114 uint16_t checksum;
115
116 /**
117 * Source address
118 */
119 uint32_t src_ip;
120
121 /**
122 * Destination address
123 */
124 uint32_t dst_ip;
125};
126
127
128/**
129 * Format of ICMP packet.
130 */
131struct icmp_ttl_exceeded_header
132{
133 uint8_t type;
134
135 uint8_t code;
136
137 uint16_t checksum;
138
139 uint32_t unused;
140
141 /* followed by original payload */
142};
143
144struct icmp_echo_header
145{
146 uint8_t type;
147
148 uint8_t code;
149
150 uint16_t checksum;
151
152 uint32_t reserved;
153};
154
155/**
156 * Beginning of UDP packet.
157 */
158struct udp_header
159{
160 uint16_t src_port;
161
162 uint16_t dst_port;
163
164 uint16_t length;
165
166 uint16_t crc;
167};
168
169
170/**
171 * Socket we use to send our ICMP packets.
172 */
173static SOCKET rawsock;
174
175/**
176 * Target "dummy" address.
177 */
178static struct in_addr dummy;
179
180/**
181 * Port we are listening on (communicated to the server).
182 */
183static uint16_t port;
184
185
186
187/**
188 * Convert IPv4 address from text to binary form.
189 *
190 * @param af address family
191 * @param cp the address to print
192 * @param buf where to write the address result
193 * @return 1 on success
194 */
195static int
196inet_pton (int af,
197 const char *cp,
198 struct in_addr *buf)
199{
200 buf->s_addr = inet_addr(cp);
201 if (buf->s_addr == INADDR_NONE)
202 {
203 fprintf(stderr,
204 "Error %d handling address %s",
205 WSAGetLastError(),
206 cp);
207 return 0;
208 }
209 return 1;
210}
211
212
213/**
214 * CRC-16 for IP/ICMP headers.
215 *
216 * @param data what to calculate the CRC over
217 * @param bytes number of bytes in data (must be multiple of 2)
218 * @return the CRC 16.
219 */
220static uint16_t
221calc_checksum(const uint16_t *data,
222 unsigned int bytes)
223{
224 uint32_t sum;
225 unsigned int i;
226
227 sum = 0;
228 for (i=0;i<bytes/2;i++)
229 sum += data[i];
230 sum = (sum & 0xffff) + (sum >> 16);
231 sum = htons(0xffff - sum);
232 return sum;
233}
234
235
236/**
237 * Send an ICMP message to the target.
238 *
239 * @param my_ip source address
240 * @param other target address
241 */
242static void
243send_icmp_udp (const struct in_addr *my_ip,
244 const struct in_addr *other)
245{
246 char packet[sizeof(struct ip_header) * 2 +
247 sizeof(struct icmp_ttl_exceeded_header) +
248 sizeof(struct udp_header)];
249 struct ip_header ip_pkt;
250 struct icmp_ttl_exceeded_header icmp_pkt;
251 struct udp_header udp_pkt;
252 struct sockaddr_in dst;
253 size_t off;
254 int err;
255
256 /* ip header: send to (known) ip address */
257 off = 0;
258 ip_pkt.vers_ihl = 0x45;
259 ip_pkt.tos = 0;
260 ip_pkt.pkt_len = htons(sizeof (packet));
261 ip_pkt.id = htons(256);
262 ip_pkt.flags_frag_offset = 0;
263 ip_pkt.ttl = 128;
264 ip_pkt.proto = IPPROTO_ICMP;
265 ip_pkt.checksum = 0;
266 ip_pkt.src_ip = my_ip->s_addr;
267 ip_pkt.dst_ip = other->s_addr;
268 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt,
269 sizeof (struct ip_header)));
270 memcpy(&packet[off],
271 &ip_pkt,
272 sizeof(struct ip_header));
273 off += sizeof(struct ip_header);
274
275 icmp_pkt.type = ICMP_TIME_EXCEEDED;
276 icmp_pkt.code = 0;
277 icmp_pkt.checksum = 0;
278 icmp_pkt.unused = 0;
279 memcpy(&packet[off],
280 &icmp_pkt,
281 sizeof(struct icmp_ttl_exceeded_header));
282 off += sizeof(struct icmp_ttl_exceeded_header);
283
284 /* ip header of the presumably 'lost' udp packet */
285 ip_pkt.vers_ihl = 0x45;
286 ip_pkt.tos = 0;
287 ip_pkt.pkt_len = htons(sizeof (struct ip_header) +
288 sizeof (struct udp_header));
289 ip_pkt.id = htons(0);
290 ip_pkt.flags_frag_offset = 0;
291 ip_pkt.ttl = 128;
292 ip_pkt.proto = IPPROTO_UDP;
293 ip_pkt.checksum = 0;
294 ip_pkt.src_ip = other->s_addr;
295 ip_pkt.dst_ip = dummy.s_addr;
296 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt,
297 sizeof (struct ip_header)));
298 memcpy(&packet[off],
299 &ip_pkt,
300 sizeof(struct ip_header));
301 off += sizeof(struct ip_header);
302
303 /* build UDP header */
304 udp_pkt.src_port = htons(NAT_TRAV_PORT);
305 udp_pkt.dst_port = htons(NAT_TRAV_PORT);
306 udp_pkt.length = htons (port);
307 udp_pkt.crc = 0;
308 memcpy(&packet[off],
309 &udp_pkt,
310 sizeof(struct udp_header));
311 off += sizeof(struct udp_header);
312
313 /* no go back to calculate ICMP packet checksum */
314 icmp_pkt.checksum = htons(calc_checksum((uint16_t*)&packet[off],
315 sizeof (struct icmp_ttl_exceeded_header) +
316 sizeof (struct ip_header) +
317 sizeof (struct udp_header)));
318 memcpy (&packet[sizeof (struct ip_header)],
319 &icmp_pkt,
320 sizeof (struct icmp_ttl_exceeded_header));
321
322 memset (&dst, 0, sizeof (dst));
323 dst.sin_family = AF_INET;
324 dst.sin_addr = *other;
325 err = sendto(rawsock,
326 packet,
327 sizeof (packet), 0,
328 (struct sockaddr*)&dst,
329 sizeof(dst));
330 if (err < 0)
331 {
332 fprintf(stderr,
333 "sendto failed: %s\n", strerror(errno));
334 }
335 else if (sizeof (packet) != (size_t) err)
336 {
337 fprintf(stderr,
338 "Error: partial send of ICMP message\n");
339 }
340}
341
342
343/**
344 * Send an ICMP message to the target.
345 *
346 * @param my_ip source address
347 * @param other target address
348 */
349static void
350send_icmp (const struct in_addr *my_ip,
351 const struct in_addr *other)
352{
353 struct ip_header ip_pkt;
354 struct icmp_ttl_exceeded_header icmp_ttl;
355 struct icmp_echo_header icmp_echo;
356 struct sockaddr_in dst;
357 char packet[sizeof (struct ip_header) * 2 +
358 sizeof (struct icmp_ttl_exceeded_header) +
359 sizeof(struct icmp_echo_header)];
360 size_t off;
361 int err;
362
363 /* ip header: send to (known) ip address */
364 off = 0;
365 ip_pkt.vers_ihl = 0x45;
366 ip_pkt.tos = 0;
367 ip_pkt.pkt_len = htons (sizeof (packet));
368 ip_pkt.id = htons(256);
369 ip_pkt.flags_frag_offset = 0;
370 ip_pkt.ttl = IPDEFTTL;
371 ip_pkt.proto = IPPROTO_ICMP;
372 ip_pkt.checksum = 0;
373 ip_pkt.src_ip = my_ip->s_addr;
374 ip_pkt.dst_ip = other->s_addr;
375 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt,
376 sizeof (struct ip_header)));
377 memcpy (&packet[off],
378 &ip_pkt,
379 sizeof (struct ip_header));
380 off += sizeof (ip_pkt);
381
382 /* icmp reply: time exceeded */
383 icmp_ttl.type = ICMP_TIME_EXCEEDED;
384 icmp_ttl.code = 0;
385 icmp_ttl.checksum = 0;
386 icmp_ttl.unused = 0;
387 memcpy (&packet[off],
388 &icmp_ttl,
389 sizeof (struct icmp_ttl_exceeded_header));
390 off += sizeof (struct icmp_ttl_exceeded_header);
391
392 /* ip header of the presumably 'lost' udp packet */
393 ip_pkt.vers_ihl = 0x45;
394 ip_pkt.tos = 0;
395 ip_pkt.pkt_len = htons(sizeof (struct ip_header) + sizeof (struct icmp_echo_header));
396 ip_pkt.id = htons (256);
397 ip_pkt.flags_frag_offset = 0;
398 ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
399 ip_pkt.proto = IPPROTO_ICMP;
400 ip_pkt.src_ip = other->s_addr;
401 ip_pkt.dst_ip = dummy.s_addr;
402 ip_pkt.checksum = 0;
403 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt,
404 sizeof (struct ip_header)));
405 memcpy (&packet[off],
406 &ip_pkt,
407 sizeof (struct ip_header));
408 off += sizeof (struct ip_header);
409
410 icmp_echo.type = ICMP_ECHO;
411 icmp_echo.code = 0;
412 icmp_echo.reserved = htonl(port);
413 icmp_echo.checksum = 0;
414 icmp_echo.checksum = htons(calc_checksum((uint16_t*) &icmp_echo,
415 sizeof (struct icmp_echo_header)));
416 memcpy (&packet[off],
417 &icmp_echo,
418 sizeof(struct icmp_echo_header));
419
420 /* no go back to calculate ICMP packet checksum */
421 off = sizeof (struct ip_header);
422 icmp_ttl.checksum = htons(calc_checksum((uint16_t*) &packet[off],
423 sizeof (struct icmp_ttl_exceeded_header) +
424 sizeof (struct ip_header) +
425 sizeof (struct icmp_echo_header)));
426 memcpy (&packet[off],
427 &icmp_ttl,
428 sizeof (struct icmp_ttl_exceeded_header));
429
430 memset (&dst, 0, sizeof (dst));
431 dst.sin_family = AF_INET;
432 dst.sin_addr = *other;
433
434 err = sendto(rawsock,
435 packet,
436 sizeof (packet), 0,
437 (struct sockaddr*)&dst,
438 sizeof(dst));
439
440 if (err < 0)
441 {
442 fprintf(stderr,
443 "sendto failed: %s\n", strerror(errno));
444 }
445 else if (sizeof (packet) != (size_t) err)
446 {
447 fprintf(stderr,
448 "Error: partial send of ICMP message\n");
449 }
450}
451
452
453/**
454 * Create an ICMP raw socket.
455 *
456 * @return INVALID_SOCKET on error
457 */
458static SOCKET
459make_raw_socket ()
460{
461 DWORD bOptVal = TRUE;
462 int bOptLen = sizeof(bOptVal);
463 SOCKET ret;
464
465 ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
466 if (INVALID_SOCKET == ret)
467 {
468 fprintf (stderr,
469 "Error opening RAW socket: %s\n",
470 strerror (errno));
471 return INVALID_SOCKET;
472 }
473 if (0 != setsockopt(ret, SOL_SOCKET, SO_BROADCAST, (char*)&bOptVal, bOptLen))
474 {
475 fprintf(stderr,
476 "Error setting SO_BROADCAST to ON: %s\n",
477 strerror (errno));
478 closesocket(rawsock);
479 return INVALID_SOCKET;
480 }
481
482 if (0 != setsockopt(ret, IPPROTO_IP, IP_HDRINCL, (char*)&bOptVal, bOptLen))
483 {
484 fprintf(stderr,
485 "Error setting IP_HDRINCL to ON: %s\n",
486 strerror (errno));
487 closesocket(rawsock);
488 return INVALID_SOCKET;
489 }
490 return ret;
491}
492
493
494int
495main (int argc, char *const *argv)
496{
497 struct in_addr external;
498 struct in_addr target;
499 WSADATA wsaData;
500
501 unsigned int p;
502
503 if (argc != 4)
504 {
505 fprintf (stderr,
506 "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
507 return 1;
508 }
509 if ( (1 != inet_pton (AF_INET, argv[1], &external)) ||
510 (1 != inet_pton (AF_INET, argv[2], &target)) )
511 {
512 fprintf (stderr,
513 "Error parsing IPv4 address: %s\n",
514 strerror (errno));
515 return 1;
516 }
517 if ( (1 != sscanf (argv[3], "%u", &p) ) ||
518 (0 == p) ||
519 (0xFFFF < p) )
520 {
521 fprintf (stderr,
522 "Error parsing port value `%s'\n",
523 argv[3]);
524 return 1;
525 }
526 port = (uint16_t) p;
527
528 if (0 != WSAStartup (MAKEWORD (2, 1), &wsaData))
529 {
530 fprintf (stderr, "Failed to find Winsock 2.1 or better.\n");
531 return 2;
532 }
533 if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
534 {
535 fprintf (stderr,
536 "Internal error converting dummy IP to binary.\n");
537 return 2;
538 }
539 if (-1 == (rawsock = make_raw_socket()))
540 return 3;
541 send_icmp (&external,
542 &target);
543 send_icmp_udp (&external,
544 &target);
545 closesocket (rawsock);
546 WSACleanup ();
547 return 0;
548}
549
550/* end of gnunet-nat-client-windows.c */
diff --git a/src/nat/gnunet-nat-client.c b/src/nat/gnunet-nat-client.c
new file mode 100644
index 000000000..3e35aa8f4
--- /dev/null
+++ b/src/nat/gnunet-nat-client.c
@@ -0,0 +1,527 @@
1/*
2 This file is part of GNUnet.
3 (C) 2010 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/transport/gnunet-nat-client.c
23 * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do)
24 * This code will work under GNU/Linux only.
25 * @author Christian Grothoff
26 *
27 * This program will send ONE ICMP message using RAW sockets
28 * to the IP address specified as the second argument. Since
29 * it uses RAW sockets, it must be installed SUID or run as 'root'.
30 * In order to keep the security risk of the resulting SUID binary
31 * minimal, the program ONLY opens the RAW socket with root
32 * privileges, then drops them and only then starts to process
33 * command line arguments. The code also does not link against
34 * any shared libraries (except libc) and is strictly minimal
35 * (except for checking for errors). The following list of people
36 * have reviewed this code and considered it safe since the last
37 * modification (if you reviewed it, please have your name added
38 * to the list):
39 *
40 * - Christian Grothoff
41 * - Nathan Evans
42 * - Benjamin Kuperman (22 Aug 2010)
43 */
44#if HAVE_CONFIG_H
45/* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */
46#include "gnunet_config.h"
47#else
48#define _GNU_SOURCE
49#endif
50#include <sys/types.h>
51#include <sys/socket.h>
52#include <arpa/inet.h>
53#include <sys/types.h>
54#include <unistd.h>
55#include <stdio.h>
56#include <string.h>
57#include <errno.h>
58#include <stdlib.h>
59#include <stdint.h>
60#include <netinet/ip.h>
61#include <netinet/ip_icmp.h>
62#include <netinet/in.h>
63
64/**
65 * Must match IP given in the server.
66 */
67#define DUMMY_IP "192.0.2.86"
68
69#define NAT_TRAV_PORT 22225
70
71/**
72 * IPv4 header.
73 */
74struct ip_header
75{
76
77 /**
78 * Version (4 bits) + Internet header length (4 bits)
79 */
80 uint8_t vers_ihl;
81
82 /**
83 * Type of service
84 */
85 uint8_t tos;
86
87 /**
88 * Total length
89 */
90 uint16_t pkt_len;
91
92 /**
93 * Identification
94 */
95 uint16_t id;
96
97 /**
98 * Flags (3 bits) + Fragment offset (13 bits)
99 */
100 uint16_t flags_frag_offset;
101
102 /**
103 * Time to live
104 */
105 uint8_t ttl;
106
107 /**
108 * Protocol
109 */
110 uint8_t proto;
111
112 /**
113 * Header checksum
114 */
115 uint16_t checksum;
116
117 /**
118 * Source address
119 */
120 uint32_t src_ip;
121
122 /**
123 * Destination address
124 */
125 uint32_t dst_ip;
126};
127
128/**
129 * Format of ICMP packet.
130 */
131struct icmp_ttl_exceeded_header
132{
133 uint8_t type;
134
135 uint8_t code;
136
137 uint16_t checksum;
138
139 uint32_t unused;
140
141 /* followed by original payload */
142};
143
144struct icmp_echo_header
145{
146 uint8_t type;
147
148 uint8_t code;
149
150 uint16_t checksum;
151
152 uint32_t reserved;
153};
154
155/**
156 * Beginning of UDP packet.
157 */
158struct udp_header
159{
160 uint16_t src_port;
161
162 uint16_t dst_port;
163
164 uint16_t length;
165
166 uint16_t crc;
167};
168
169/**
170 * Socket we use to send our fake ICMP replies.
171 */
172static int rawsock;
173
174/**
175 * Target "dummy" address of the packet we pretend to respond to.
176 */
177static struct in_addr dummy;
178
179/**
180 * Our "source" port.
181 */
182static uint16_t port;
183
184
185/**
186 * CRC-16 for IP/ICMP headers.
187 *
188 * @param data what to calculate the CRC over
189 * @param bytes number of bytes in data (must be multiple of 2)
190 * @return the CRC 16.
191 */
192static uint16_t
193calc_checksum (const uint16_t *data,
194 unsigned int bytes)
195{
196 uint32_t sum;
197 unsigned int i;
198
199 sum = 0;
200 for (i=0;i<bytes/2;i++)
201 sum += data[i];
202 sum = (sum & 0xffff) + (sum >> 16);
203 sum = htons(0xffff - sum);
204 return sum;
205}
206
207
208/**
209 * Send an ICMP message to the target.
210 *
211 * @param my_ip source address
212 * @param other target address
213 */
214static void
215send_icmp_udp (const struct in_addr *my_ip,
216 const struct in_addr *other)
217{
218 char packet[sizeof(struct ip_header) * 2 +
219 sizeof(struct icmp_ttl_exceeded_header) +
220 sizeof(struct udp_header)];
221 struct ip_header ip_pkt;
222 struct icmp_ttl_exceeded_header icmp_pkt;
223 struct udp_header udp_pkt;
224 struct sockaddr_in dst;
225 size_t off;
226 int err;
227
228 /* ip header: send to (known) ip address */
229 off = 0;
230 ip_pkt.vers_ihl = 0x45;
231 ip_pkt.tos = 0;
232 ip_pkt.pkt_len = htons (sizeof (packet));
233 ip_pkt.id = htons(256);
234 ip_pkt.flags_frag_offset = 0;
235 ip_pkt.ttl = 128;
236 ip_pkt.proto = IPPROTO_ICMP;
237 ip_pkt.checksum = 0;
238 ip_pkt.src_ip = my_ip->s_addr;
239 ip_pkt.dst_ip = other->s_addr;
240 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt,
241 sizeof (struct ip_header)));
242 memcpy(&packet[off],
243 &ip_pkt,
244 sizeof(struct ip_header));
245 off += sizeof(struct ip_header);
246
247 icmp_pkt.type = ICMP_TIME_EXCEEDED;
248 icmp_pkt.code = 0;
249 icmp_pkt.checksum = 0;
250 icmp_pkt.unused = 0;
251 memcpy(&packet[off],
252 &icmp_pkt,
253 sizeof(struct icmp_ttl_exceeded_header));
254 off += sizeof(struct icmp_ttl_exceeded_header);
255
256 /* ip header of the presumably 'lost' udp packet */
257 ip_pkt.vers_ihl = 0x45;
258 ip_pkt.tos = 0;
259 ip_pkt.pkt_len = htons(sizeof (struct ip_header) +
260 sizeof (struct udp_header));
261 ip_pkt.id = htons(0);
262 ip_pkt.flags_frag_offset = 0;
263 ip_pkt.ttl = 128;
264 ip_pkt.proto = IPPROTO_UDP;
265 ip_pkt.checksum = 0;
266 ip_pkt.src_ip = other->s_addr;
267 ip_pkt.dst_ip = dummy.s_addr;
268 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt,
269 sizeof (struct ip_header)));
270 memcpy(&packet[off],
271 &ip_pkt,
272 sizeof(struct ip_header));
273 off += sizeof(struct ip_header);
274
275 /* build UDP header */
276 udp_pkt.src_port = htons(NAT_TRAV_PORT);
277 udp_pkt.dst_port = htons(NAT_TRAV_PORT);
278 udp_pkt.length = htons (port);
279 udp_pkt.crc = 0;
280 memcpy(&packet[off],
281 &udp_pkt,
282 sizeof(struct udp_header));
283 off += sizeof(struct udp_header);
284
285 /* set ICMP checksum */
286 icmp_pkt.checksum = htons(calc_checksum((uint16_t*)&packet[sizeof(struct ip_header)],
287 sizeof (struct icmp_ttl_exceeded_header) +
288 sizeof (struct ip_header) +
289 sizeof (struct udp_header)));
290 memcpy (&packet[sizeof(struct ip_header)],
291 &icmp_pkt,
292 sizeof (struct icmp_ttl_exceeded_header));
293
294 memset (&dst, 0, sizeof (dst));
295 dst.sin_family = AF_INET;
296#if HAVE_SOCKADDR_IN_SIN_LEN
297 dst.sin_len = sizeof (struct sockaddr_in);
298#endif
299 dst.sin_addr = *other;
300 err = sendto(rawsock,
301 packet,
302 sizeof (packet), 0,
303 (struct sockaddr*)&dst,
304 sizeof(dst));
305 if (err < 0)
306 {
307 fprintf(stderr,
308 "sendto failed: %s\n", strerror(errno));
309 }
310 else if (sizeof (packet) != (size_t) err)
311 {
312 fprintf(stderr,
313 "Error: partial send of ICMP message\n");
314 }
315}
316
317
318/**
319 * Send an ICMP message to the target.
320 *
321 * @param my_ip source address
322 * @param other target address
323 */
324static void
325send_icmp (const struct in_addr *my_ip,
326 const struct in_addr *other)
327{
328 struct ip_header ip_pkt;
329 struct icmp_ttl_exceeded_header icmp_ttl;
330 struct icmp_echo_header icmp_echo;
331 struct sockaddr_in dst;
332 char packet[sizeof (struct ip_header) * 2 +
333 sizeof (struct icmp_ttl_exceeded_header) +
334 sizeof (struct icmp_echo_header)];
335 size_t off;
336 int err;
337
338 /* ip header: send to (known) ip address */
339 off = 0;
340 ip_pkt.vers_ihl = 0x45;
341 ip_pkt.tos = 0;
342 ip_pkt.pkt_len = htons (sizeof (packet));
343 ip_pkt.id = htons (256);
344 ip_pkt.flags_frag_offset = 0;
345 ip_pkt.ttl = IPDEFTTL;
346 ip_pkt.proto = IPPROTO_ICMP;
347 ip_pkt.checksum = 0;
348 ip_pkt.src_ip = my_ip->s_addr;
349 ip_pkt.dst_ip = other->s_addr;
350 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt,
351 sizeof (struct ip_header)));
352 memcpy (&packet[off],
353 &ip_pkt,
354 sizeof (struct ip_header));
355 off = sizeof (ip_pkt);
356
357 /* icmp reply: time exceeded */
358 icmp_ttl.type = ICMP_TIME_EXCEEDED;
359 icmp_ttl.code = 0;
360 icmp_ttl.checksum = 0;
361 icmp_ttl.unused = 0;
362 memcpy (&packet[off],
363 &icmp_ttl,
364 sizeof (struct icmp_ttl_exceeded_header));
365 off += sizeof (struct icmp_ttl_exceeded_header);
366
367 /* ip header of the presumably 'lost' udp packet */
368 ip_pkt.vers_ihl = 0x45;
369 ip_pkt.tos = 0;
370 ip_pkt.pkt_len = htons (sizeof (struct ip_header) + sizeof (struct icmp_echo_header));
371 ip_pkt.id = htons (256);
372 ip_pkt.flags_frag_offset = 0;
373 ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
374 ip_pkt.proto = IPPROTO_ICMP;
375 ip_pkt.src_ip = other->s_addr;
376 ip_pkt.dst_ip = dummy.s_addr;
377 ip_pkt.checksum = 0;
378 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt,
379 sizeof (struct ip_header)));
380 memcpy (&packet[off],
381 &ip_pkt,
382 sizeof (struct ip_header));
383 off += sizeof (struct ip_header);
384
385 icmp_echo.type = ICMP_ECHO;
386 icmp_echo.code = 0;
387 icmp_echo.reserved = htonl (port);
388 icmp_echo.checksum = 0;
389 icmp_echo.checksum = htons(calc_checksum((uint16_t*) &icmp_echo,
390 sizeof (struct icmp_echo_header)));
391 memcpy (&packet[off],
392 &icmp_echo,
393 sizeof(struct icmp_echo_header));
394
395 /* no go back to calculate ICMP packet checksum */
396 off = sizeof (struct ip_header);
397 icmp_ttl.checksum = htons(calc_checksum((uint16_t*) &packet[off],
398 sizeof (struct icmp_ttl_exceeded_header) +
399 sizeof (struct ip_header) +
400 sizeof (struct icmp_echo_header)));
401 memcpy (&packet[off],
402 &icmp_ttl,
403 sizeof (struct icmp_ttl_exceeded_header));
404
405 /* prepare for transmission */
406 memset (&dst, 0, sizeof (dst));
407 dst.sin_family = AF_INET;
408#if HAVE_SOCKADDR_IN_SIN_LEN
409 dst.sin_len = sizeof (struct sockaddr_in);
410#endif
411 dst.sin_addr = *other;
412 err = sendto(rawsock,
413 packet,
414 sizeof (packet), 0,
415 (struct sockaddr*)&dst,
416 sizeof(dst));
417 if (err < 0)
418 {
419 fprintf(stderr,
420 "sendto failed: %s\n", strerror(errno));
421 }
422 else if (sizeof (packet) != (size_t) err)
423 {
424 fprintf(stderr,
425 "Error: partial send of ICMP message\n");
426 }
427}
428
429
430/**
431 * Create an ICMP raw socket for writing.
432 *
433 * @return -1 on error
434 */
435static int
436make_raw_socket ()
437{
438 const int one = 1;
439 int ret;
440
441 ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
442 if (-1 == ret)
443 {
444 fprintf (stderr,
445 "Error opening RAW socket: %s\n",
446 strerror (errno));
447 return -1;
448 }
449 if (0 != setsockopt(ret, SOL_SOCKET, SO_BROADCAST,
450 (char *)&one, sizeof(one)))
451 {
452 fprintf(stderr,
453 "setsockopt failed: %s\n",
454 strerror (errno));
455 close (ret);
456 return -1;
457 }
458 if (0 != setsockopt(ret, IPPROTO_IP, IP_HDRINCL,
459 (char *)&one, sizeof(one)))
460 {
461 fprintf(stderr,
462 "setsockopt failed: %s\n",
463 strerror (errno));
464 close (ret);
465 return -1;
466 }
467 return ret;
468}
469
470
471int
472main (int argc, char *const *argv)
473{
474 struct in_addr external;
475 struct in_addr target;
476 uid_t uid;
477 unsigned int p;
478
479 if (4 != argc)
480 {
481 fprintf (stderr,
482 "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
483 return 1;
484 }
485 if ( (1 != inet_pton (AF_INET, argv[1], &external)) ||
486 (1 != inet_pton (AF_INET, argv[2], &target)) )
487 {
488 fprintf (stderr,
489 "Error parsing IPv4 address: %s\n",
490 strerror (errno));
491 return 1;
492 }
493 if ( (1 != sscanf (argv[3], "%u", &p) ) ||
494 (0 == p) ||
495 (0xFFFF < p) )
496 {
497 fprintf (stderr,
498 "Error parsing port value `%s'\n",
499 argv[3]);
500 return 1;
501 }
502 port = (uint16_t) p;
503 if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
504 {
505 fprintf (stderr,
506 "Internal error converting dummy IP to binary.\n");
507 return 2;
508 }
509 if (-1 == (rawsock = make_raw_socket()))
510 return 2;
511 uid = getuid ();
512 if (0 != setresuid (uid, uid, uid))
513 {
514 fprintf (stderr,
515 "Failed to setresuid: %s\n",
516 strerror (errno));
517 /* not critical, continue anyway */
518 }
519 send_icmp (&external,
520 &target);
521 send_icmp_udp (&external,
522 &target);
523 close (rawsock);
524 return 0;
525}
526
527/* end of gnunet-nat-client.c */
diff --git a/src/nat/gnunet-nat-server-script.sh b/src/nat/gnunet-nat-server-script.sh
new file mode 100755
index 000000000..42a8e639a
--- /dev/null
+++ b/src/nat/gnunet-nat-server-script.sh
@@ -0,0 +1,4 @@
1#!/bin/sh
2IP=`ifconfig | grep inet | head -n1 | awk '{print $2}' | sed -e "s/addr://"`
3echo "Using IP $IP"
4./gnunet-nat-server $IP | sed -u -e "s/.*/.\/gnunet-nat-server-udp $IP &\&/" | sh
diff --git a/src/nat/gnunet-nat-server-windows.c b/src/nat/gnunet-nat-server-windows.c
new file mode 100644
index 000000000..8cfad2ff2
--- /dev/null
+++ b/src/nat/gnunet-nat-server-windows.c
@@ -0,0 +1,653 @@
1/*
2 This file is part of GNUnet.
3 (C) 2010 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/transport/gnunet-nat-server-windows.c
23 * @brief Windows tool to help bypass NATs using ICMP method
24 * This code will work under W32 only
25 * @author Christian Grothoff
26 *
27 * This program will send ONE ICMP message every 500 ms RAW sockets
28 * to a DUMMY IP address and also listens for ICMP replies. Since
29 * it uses RAW sockets, it must be run as an administrative user.
30 * In order to keep the security risk of the resulting binary
31 * minimal, the program ONLY opens the two RAW sockets with administrative
32 * privileges, then drops them and only then starts to process
33 * command line arguments. The code also does not link against
34 * any shared libraries (except libc) and is strictly minimal
35 * (except for checking for errors). The following list of people
36 * have reviewed this code and considered it safe since the last
37 * modification (if you reviewed it, please have your name added
38 * to the list):
39 *
40 * - Nathan Evans
41 * - Christian Grothoff
42 */
43#define _GNU_SOURCE
44
45
46#include <winsock2.h>
47#include <ws2tcpip.h>
48#include <sys/time.h>
49#include <sys/types.h>
50#include <unistd.h>
51#include <stdio.h>
52#include <string.h>
53#include <errno.h>
54#include <stdlib.h>
55#include <stdint.h>
56#include <time.h>
57
58/**
59 * Should we print some debug output?
60 */
61#define VERBOSE 0
62
63/**
64 * Must match IP given in the client.
65 */
66#define DUMMY_IP "192.0.2.86"
67
68/**
69 * Default Port
70 */
71#define NAT_TRAV_PORT 22225
72
73/**
74 * TTL to use for our outgoing messages.
75 */
76#define IPDEFTTL 64
77
78#define ICMP_ECHO 8
79
80#define ICMP_TIME_EXCEEDED 11
81
82/**
83 * How often do we send our ICMP messages to receive replies?
84 */
85#define ICMP_SEND_FREQUENCY_MS 500
86
87/**
88 * IPv4 header.
89 */
90struct ip_header
91{
92
93 /**
94 * Version (4 bits) + Internet header length (4 bits)
95 */
96 uint8_t vers_ihl;
97
98 /**
99 * Type of service
100 */
101 uint8_t tos;
102
103 /**
104 * Total length
105 */
106 uint16_t pkt_len;
107
108 /**
109 * Identification
110 */
111 uint16_t id;
112
113 /**
114 * Flags (3 bits) + Fragment offset (13 bits)
115 */
116 uint16_t flags_frag_offset;
117
118 /**
119 * Time to live
120 */
121 uint8_t ttl;
122
123 /**
124 * Protocol
125 */
126 uint8_t proto;
127
128 /**
129 * Header checksum
130 */
131 uint16_t checksum;
132
133 /**
134 * Source address
135 */
136 uint32_t src_ip;
137
138 /**
139 * Destination address
140 */
141 uint32_t dst_ip;
142};
143
144/**
145 * Format of ICMP packet.
146 */
147struct icmp_ttl_exceeded_header
148{
149 uint8_t type;
150
151 uint8_t code;
152
153 uint16_t checksum;
154
155 uint32_t unused;
156
157 /* followed by original payload */
158};
159
160struct icmp_echo_header
161{
162 uint8_t type;
163
164 uint8_t code;
165
166 uint16_t checksum;
167
168 uint32_t reserved;
169};
170
171/**
172 * Beginning of UDP packet.
173 */
174struct udp_header
175{
176 uint16_t src_port;
177
178 uint16_t dst_port;
179
180 uint16_t length;
181
182 uint16_t crc;
183};
184
185/**
186 * Socket we use to receive "fake" ICMP replies.
187 */
188static SOCKET icmpsock;
189
190/**
191 * Socket we use to send our ICMP requests.
192 */
193static SOCKET rawsock;
194
195/**
196 * Socket we use to send our UDP requests.
197 */
198static SOCKET udpsock;
199
200/**
201 * Target "dummy" address.
202 */
203static struct in_addr dummy;
204
205
206/**
207 * CRC-16 for IP/ICMP headers.
208 *
209 * @param data what to calculate the CRC over
210 * @param bytes number of bytes in data (must be multiple of 2)
211 * @return the CRC 16.
212 */
213static uint16_t
214calc_checksum(const uint16_t *data,
215 unsigned int bytes)
216{
217 uint32_t sum;
218 unsigned int i;
219
220 sum = 0;
221 for (i=0;i<bytes/2;i++)
222 sum += data[i];
223 sum = (sum & 0xffff) + (sum >> 16);
224 sum = htons(0xffff - sum);
225 return sum;
226}
227
228
229/**
230 * Convert IPv4 address from text to binary form.
231 *
232 * @param af address family
233 * @param cp the address to print
234 * @param buf where to write the address result
235 * @return 1 on success
236 */
237static int
238inet_pton (int af,
239 const char *cp,
240 struct in_addr *buf)
241{
242 buf->s_addr = inet_addr(cp);
243 if (buf->s_addr == INADDR_NONE)
244 {
245 fprintf(stderr,
246 "Error %d handling address %s",
247 WSAGetLastError(),
248 cp);
249 return 0;
250 }
251 return 1;
252}
253
254
255/**
256 * Send an ICMP message to the dummy IP.
257 *
258 * @param my_ip source address (our ip address)
259 */
260static void
261send_icmp_echo (const struct in_addr *my_ip)
262{
263 char packet[sizeof (struct ip_header) + sizeof (struct icmp_echo_header)];
264 struct icmp_echo_header icmp_echo;
265 struct ip_header ip_pkt;
266 struct sockaddr_in dst;
267 size_t off;
268 int err;
269
270 off = 0;
271 ip_pkt.vers_ihl = 0x45;
272 ip_pkt.tos = 0;
273 ip_pkt.pkt_len = htons (sizeof (packet));
274 ip_pkt.id = htons (256);
275 ip_pkt.flags_frag_offset = 0;
276 ip_pkt.ttl = IPDEFTTL;
277 ip_pkt.proto = IPPROTO_ICMP;
278 ip_pkt.checksum = 0;
279 ip_pkt.src_ip = my_ip->s_addr;
280 ip_pkt.dst_ip = dummy.s_addr;
281 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt,
282 sizeof (struct ip_header)));
283 memcpy (&packet[off],
284 &ip_pkt,
285 sizeof (struct ip_header));
286 off += sizeof (struct ip_header);
287
288 icmp_echo.type = ICMP_ECHO;
289 icmp_echo.code = 0;
290 icmp_echo.reserved = 0;
291 icmp_echo.checksum = 0;
292 icmp_echo.checksum = htons(calc_checksum((uint16_t*) &icmp_echo,
293 sizeof (struct icmp_echo_header)));
294 memcpy (&packet[off],
295 &icmp_echo,
296 sizeof (struct icmp_echo_header));
297 off += sizeof (struct icmp_echo_header);
298
299 memset (&dst, 0, sizeof (dst));
300 dst.sin_family = AF_INET;
301 dst.sin_addr = dummy;
302 err = sendto(rawsock,
303 packet, off, 0,
304 (struct sockaddr*)&dst,
305 sizeof(dst));
306 if (err < 0)
307 {
308#if VERBOSE
309 fprintf(stderr,
310 "sendto failed: %s\n", strerror(errno));
311#endif
312 }
313 else if (err != off)
314 {
315 fprintf(stderr,
316 "Error: partial send of ICMP message\n");
317 }
318}
319
320
321/**
322 * Send a UDP message to the dummy IP.
323 */
324static void
325send_udp ()
326{
327 struct sockaddr_in dst;
328 ssize_t err;
329
330 memset (&dst, 0, sizeof (dst));
331 dst.sin_family = AF_INET;
332 dst.sin_addr = dummy;
333 dst.sin_port = htons (NAT_TRAV_PORT);
334 err = sendto(udpsock,
335 NULL, 0, 0,
336 (struct sockaddr*)&dst,
337 sizeof(dst));
338 if (err < 0)
339 {
340#if VERBOSE
341 fprintf(stderr,
342 "sendto failed: %s\n", strerror(errno));
343#endif
344 }
345 else if (0 != err)
346 {
347 fprintf(stderr,
348 "Error: partial send of ICMP message\n");
349 }
350}
351
352
353/**
354 * We've received an ICMP response. Process it.
355 */
356static void
357process_icmp_response ()
358{
359 char buf[65536];
360 ssize_t have;
361 struct in_addr source_ip;
362 struct ip_header ip_pkt;
363 struct icmp_ttl_exceeded_header icmp_ttl;
364 struct icmp_echo_header icmp_echo;
365 struct udp_header udp_pkt;
366 size_t off;
367 uint16_t port;
368 DWORD ssize;
369
370 have = read (icmpsock, buf, sizeof (buf));
371 if (have == -1)
372 {
373 fprintf (stderr,
374 "Error reading raw socket: %s\n",
375 strerror (errno));
376 return;
377 }
378#if VERBOSE
379 fprintf (stderr,
380 "Received message of %u bytes\n",
381 (unsigned int) have);
382#endif
383 if (have < (ssize_t) (sizeof (struct ip_header) + sizeof (struct icmp_ttl_exceeded_header) + sizeof (struct ip_header)))
384 {
385 /* malformed */
386 return;
387 }
388 off = 0;
389 memcpy (&ip_pkt,
390 &buf[off],
391 sizeof (struct ip_header));
392 off += sizeof (struct ip_header);
393 memcpy(&source_ip,
394 &ip_pkt.src_ip,
395 sizeof (source_ip));
396 memcpy (&icmp_ttl,
397 &buf[off],
398 sizeof (struct icmp_ttl_exceeded_header));
399 off += sizeof (struct icmp_ttl_exceeded_header);
400 if ( (ICMP_TIME_EXCEEDED != icmp_ttl.type) ||
401 (0 != icmp_ttl.code) )
402 {
403 /* different type than what we want */
404 return;
405 }
406 /* skip 2nd IP header */
407 memcpy (&ip_pkt,
408 &buf[off],
409 sizeof (struct ip_header));
410 off += sizeof (struct ip_header);
411
412 switch (ip_pkt.proto)
413 {
414 case IPPROTO_ICMP:
415 if (have != (sizeof (struct ip_header) * 2 +
416 sizeof (struct icmp_ttl_exceeded_header) +
417 sizeof (struct icmp_echo_header)) )
418 {
419 /* malformed */
420 return;
421 }
422 /* grab ICMP ECHO content */
423 memcpy (&icmp_echo,
424 &buf[off],
425 sizeof (struct icmp_echo_header));
426 port = (uint16_t) ntohl (icmp_echo.reserved);
427 break;
428 case IPPROTO_UDP:
429 if (have != (sizeof (struct ip_header) * 2 +
430 sizeof (struct icmp_ttl_exceeded_header) +
431 sizeof (struct udp_header)) )
432 {
433 /* malformed */
434 return;
435 }
436 /* grab UDP content */
437 memcpy (&udp_pkt,
438 &buf[off],
439 sizeof (struct udp_header));
440 port = ntohs (udp_pkt.length);
441 break;
442 default:
443 /* different type than what we want */
444 return;
445 }
446
447 ssize = sizeof(buf);
448 WSAAddressToString((LPSOCKADDR)&source_ip,
449 sizeof(source_ip),
450 NULL,
451 buf,
452 &ssize);
453 if (port == 0)
454 fprintf (stdout,
455 "%s\n",
456 buf);
457 else
458 fprintf (stdout,
459 "%s:%u\n",
460 buf,
461 (unsigned int) port);
462 fflush (stdout);
463}
464
465
466/**
467 * Create an ICMP raw socket for reading.
468 *
469 * @return INVALID_SOCKET on error
470 */
471static SOCKET
472make_icmp_socket ()
473{
474 SOCKET ret;
475
476 ret = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
477 if (INVALID_SOCKET == ret)
478 {
479 fprintf (stderr,
480 "Error opening RAW socket: %s\n",
481 strerror (errno));
482 return INVALID_SOCKET;
483 }
484 return ret;
485}
486
487
488/**
489 * Create an ICMP raw socket for writing.
490 *
491 * @return INVALID_SOCKET on error
492 */
493static SOCKET
494make_raw_socket ()
495{
496 DWORD bOptVal = TRUE;
497 int bOptLen = sizeof(bOptVal);
498
499 rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
500 if (INVALID_SOCKET == rawsock)
501 {
502 fprintf (stderr,
503 "Error opening RAW socket: %s\n",
504 strerror (errno));
505 return INVALID_SOCKET;
506 }
507
508 if (0 != setsockopt(rawsock,
509 SOL_SOCKET,
510 SO_BROADCAST,
511 (char*)&bOptVal, bOptLen))
512 {
513 fprintf(stderr,
514 "Error setting SO_BROADCAST to ON: %s\n",
515 strerror (errno));
516 closesocket(rawsock);
517 return INVALID_SOCKET;
518 }
519 if (0 != setsockopt(rawsock,
520 IPPROTO_IP,
521 IP_HDRINCL,
522 (char*)&bOptVal, bOptLen))
523 {
524 fprintf(stderr,
525 "Error setting IP_HDRINCL to ON: %s\n",
526 strerror (errno));
527 closesocket(rawsock);
528 return INVALID_SOCKET;
529 }
530 return rawsock;
531}
532
533
534/**
535 * Create a UDP socket for writing.
536 *
537 * @param my_ip source address (our ip address)
538 * @return INVALID_SOCKET on error
539 */
540static SOCKET
541make_udp_socket (const struct in_addr *my_ip)
542{
543 SOCKET ret;
544 struct sockaddr_in addr;
545
546 ret = socket (AF_INET, SOCK_DGRAM, 0);
547 if (INVALID_SOCKET == ret)
548 {
549 fprintf (stderr,
550 "Error opening UDP socket: %s\n",
551 strerror (errno));
552 return INVALID_SOCKET;
553 }
554 memset (&addr, 0, sizeof (addr));
555 addr.sin_family = AF_INET;
556 addr.sin_addr = *my_ip;
557 addr.sin_port = htons (NAT_TRAV_PORT);
558 if (0 != bind (ret,
559 (struct sockaddr *)&addr,
560 sizeof(addr)))
561 {
562 fprintf (stderr,
563 "Error binding UDP socket to port %u: %s\n",
564 NAT_TRAV_PORT,
565 strerror (errno));
566 /* likely problematic, but not certain, try to continue */
567 }
568 return ret;
569}
570
571
572int
573main (int argc,
574 char *const *argv)
575{
576 struct in_addr external;
577 fd_set rs;
578 struct timeval tv;
579 WSADATA wsaData;
580 unsigned int alt;
581
582 alt = 0;
583 if (2 != argc)
584 {
585 fprintf (stderr,
586 "This program must be started with our (internal NAT) IP as the only argument.\n");
587 return 1;
588 }
589 if (1 != inet_pton (AF_INET, argv[1], &external))
590 {
591 fprintf (stderr,
592 "Error parsing IPv4 address: %s, error %s\n",
593 argv[1], strerror (errno));
594 return 1;
595 }
596 if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
597 {
598 fprintf (stderr,
599 "Internal error converting dummy IP to binary.\n");
600 return 2;
601 }
602 if (WSAStartup (MAKEWORD (2, 1), &wsaData) != 0)
603 {
604 fprintf (stderr, "Failed to find Winsock 2.1 or better.\n");
605 return 2;
606 }
607 if (INVALID_SOCKET == (icmpsock = make_icmp_socket()))
608 {
609 return 3;
610 }
611 if (INVALID_SOCKET == (make_raw_socket()))
612 {
613 closesocket (icmpsock);
614 return 3;
615 }
616 if (INVALID_SOCKET == (udpsock = make_udp_socket(&external)))
617 {
618 closesocket (icmpsock);
619 closesocket (rawsock);
620 return 3;
621 }
622 while (1)
623 {
624 FD_ZERO (&rs);
625 FD_SET (icmpsock, &rs);
626 tv.tv_sec = 0;
627 tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000;
628 if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv))
629 {
630 if (errno == EINTR)
631 continue;
632 fprintf (stderr,
633 "select failed: %s\n",
634 strerror (errno));
635 break;
636 }
637 if (FD_ISSET (icmpsock, &rs))
638 process_icmp_response ();
639 if (0 == (++alt % 2))
640 send_icmp_echo (&external);
641 else
642 send_udp ();
643 }
644 /* select failed (internal error or OS out of resources) */
645 closesocket(icmpsock);
646 closesocket(rawsock);
647 closesocket(udpsock);
648 WSACleanup ();
649 return 4;
650}
651
652
653/* end of gnunet-nat-server-windows.c */
diff --git a/src/nat/gnunet-nat-server.c b/src/nat/gnunet-nat-server.c
new file mode 100644
index 000000000..ab99b5f28
--- /dev/null
+++ b/src/nat/gnunet-nat-server.c
@@ -0,0 +1,651 @@
1/*
2 This file is part of GNUnet.
3 (C) 2010 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file src/transport/gnunet-nat-server.c
23 * @brief Tool to help bypass NATs using ICMP method; must run as root (SUID will do)
24 * This code will work under GNU/Linux only (or maybe BSDs, but never W32)
25 * @author Christian Grothoff
26 *
27 * This program will send ONE ICMP message every 500 ms RAW sockets
28 * to a DUMMY IP address and also listens for ICMP replies. Since
29 * it uses RAW sockets, it must be installed SUID or run as 'root'.
30 * In order to keep the security risk of the resulting SUID binary
31 * minimal, the program ONLY opens the two RAW sockets with root
32 * privileges, then drops them and only then starts to process
33 * command line arguments. The code also does not link against
34 * any shared libraries (except libc) and is strictly minimal
35 * (except for checking for errors). The following list of people
36 * have reviewed this code and considered it safe since the last
37 * modification (if you reviewed it, please have your name added
38 * to the list):
39 *
40 * - Christian Grothoff
41 * - Nathan Evans
42 * - Benjamin Kuperman (22 Aug 2010)
43 */
44#if HAVE_CONFIG_H
45/* Just needed for HAVE_SOCKADDR_IN_SIN_LEN test macro! */
46#include "gnunet_config.h"
47#else
48#define _GNU_SOURCE
49#endif
50#include <sys/types.h>
51#include <sys/socket.h>
52#include <arpa/inet.h>
53#include <sys/select.h>
54#include <sys/time.h>
55#include <sys/types.h>
56#include <unistd.h>
57#include <stdio.h>
58#include <string.h>
59#include <errno.h>
60#include <stdlib.h>
61#include <stdint.h>
62#include <time.h>
63#include <netinet/ip.h>
64#include <netinet/ip_icmp.h>
65#include <netinet/in.h>
66
67/**
68 * Should we print some debug output?
69 */
70#define VERBOSE 0
71
72/**
73 * Must match IP given in the client.
74 */
75#define DUMMY_IP "192.0.2.86"
76
77/**
78 * Port for UDP
79 */
80#define NAT_TRAV_PORT 22225
81
82/**
83 * How often do we send our ICMP messages to receive replies?
84 */
85#define ICMP_SEND_FREQUENCY_MS 500
86
87/**
88 * IPv4 header.
89 */
90struct ip_header
91{
92
93 /**
94 * Version (4 bits) + Internet header length (4 bits)
95 */
96 uint8_t vers_ihl;
97
98 /**
99 * Type of service
100 */
101 uint8_t tos;
102
103 /**
104 * Total length
105 */
106 uint16_t pkt_len;
107
108 /**
109 * Identification
110 */
111 uint16_t id;
112
113 /**
114 * Flags (3 bits) + Fragment offset (13 bits)
115 */
116 uint16_t flags_frag_offset;
117
118 /**
119 * Time to live
120 */
121 uint8_t ttl;
122
123 /**
124 * Protocol
125 */
126 uint8_t proto;
127
128 /**
129 * Header checksum
130 */
131 uint16_t checksum;
132
133 /**
134 * Source address
135 */
136 uint32_t src_ip;
137
138 /**
139 * Destination address
140 */
141 uint32_t dst_ip;
142};
143
144/**
145 * Format of ICMP packet.
146 */
147struct icmp_ttl_exceeded_header
148{
149 uint8_t type;
150
151 uint8_t code;
152
153 uint16_t checksum;
154
155 uint32_t unused;
156
157 /* followed by original payload */
158};
159
160struct icmp_echo_header
161{
162 uint8_t type;
163
164 uint8_t code;
165
166 uint16_t checksum;
167
168 uint32_t reserved;
169};
170
171
172/**
173 * Beginning of UDP packet.
174 */
175struct udp_header
176{
177 uint16_t src_port;
178
179 uint16_t dst_port;
180
181 uint16_t length;
182
183 uint16_t crc;
184};
185
186/**
187 * Socket we use to receive "fake" ICMP replies.
188 */
189static int icmpsock;
190
191/**
192 * Socket we use to send our ICMP requests.
193 */
194static int rawsock;
195
196/**
197 * Socket we use to send our UDP requests.
198 */
199static int udpsock;
200
201/**
202 * Target "dummy" address.
203 */
204static struct in_addr dummy;
205
206
207/**
208 * CRC-16 for IP/ICMP headers.
209 *
210 * @param data what to calculate the CRC over
211 * @param bytes number of bytes in data (must be multiple of 2)
212 * @return the CRC 16.
213 */
214static uint16_t
215calc_checksum(const uint16_t *data,
216 unsigned int bytes)
217{
218 uint32_t sum;
219 unsigned int i;
220
221 sum = 0;
222 for (i=0;i<bytes/2;i++)
223 sum += data[i];
224 sum = (sum & 0xffff) + (sum >> 16);
225 sum = htons(0xffff - sum);
226 return sum;
227}
228
229
230/**
231 * Send an ICMP message to the dummy IP.
232 *
233 * @param my_ip source address (our ip address)
234 */
235static void
236send_icmp_echo (const struct in_addr *my_ip)
237{
238 char packet[sizeof (struct ip_header) + sizeof (struct icmp_echo_header)];
239 struct icmp_echo_header icmp_echo;
240 struct ip_header ip_pkt;
241 struct sockaddr_in dst;
242 size_t off;
243 int err;
244
245 off = 0;
246 ip_pkt.vers_ihl = 0x45;
247 ip_pkt.tos = 0;
248 ip_pkt.pkt_len = htons (sizeof (packet));
249 ip_pkt.id = htons (256);
250 ip_pkt.flags_frag_offset = 0;
251 ip_pkt.ttl = IPDEFTTL;
252 ip_pkt.proto = IPPROTO_ICMP;
253 ip_pkt.checksum = 0;
254 ip_pkt.src_ip = my_ip->s_addr;
255 ip_pkt.dst_ip = dummy.s_addr;
256 ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt,
257 sizeof (struct ip_header)));
258 memcpy (&packet[off],
259 &ip_pkt,
260 sizeof (struct ip_header));
261 off += sizeof (struct ip_header);
262
263 icmp_echo.type = ICMP_ECHO;
264 icmp_echo.code = 0;
265 icmp_echo.checksum = 0;
266 icmp_echo.reserved = 0;
267 icmp_echo.checksum = htons(calc_checksum((uint16_t*)&icmp_echo,
268 sizeof (struct icmp_echo_header)));
269 memcpy (&packet[off],
270 &icmp_echo,
271 sizeof (struct icmp_echo_header));
272 off += sizeof (struct icmp_echo_header);
273
274 memset (&dst, 0, sizeof (dst));
275 dst.sin_family = AF_INET;
276#if HAVE_SOCKADDR_IN_SIN_LEN
277 dst.sin_len = sizeof (struct sockaddr_in);
278#endif
279 dst.sin_addr = dummy;
280 err = sendto(rawsock,
281 packet, off, 0,
282 (struct sockaddr*)&dst,
283 sizeof(dst));
284 if (err < 0)
285 {
286#if VERBOSE
287 fprintf(stderr,
288 "sendto failed: %s\n", strerror(errno));
289#endif
290 }
291 else if (sizeof (packet) != err)
292 {
293 fprintf(stderr,
294 "Error: partial send of ICMP message\n");
295 }
296}
297
298
299/**
300 * Send a UDP message to the dummy IP.
301 */
302static void
303send_udp ()
304{
305 struct sockaddr_in dst;
306 ssize_t err;
307
308 memset (&dst, 0, sizeof (dst));
309 dst.sin_family = AF_INET;
310#if HAVE_SOCKADDR_IN_SIN_LEN
311 dst.sin_len = sizeof (struct sockaddr_in);
312#endif
313 dst.sin_addr = dummy;
314 dst.sin_port = htons (NAT_TRAV_PORT);
315 err = sendto(udpsock,
316 NULL, 0, 0,
317 (struct sockaddr*)&dst,
318 sizeof(dst));
319 if (err < 0)
320 {
321#if VERBOSE
322 fprintf(stderr,
323 "sendto failed: %s\n", strerror(errno));
324#endif
325 }
326 else if (0 != err)
327 {
328 fprintf(stderr,
329 "Error: partial send of ICMP message\n");
330 }
331}
332
333
334/**
335 * We've received an ICMP response. Process it.
336 */
337static void
338process_icmp_response ()
339{
340 char buf[65536];
341 ssize_t have;
342 struct in_addr source_ip;
343 struct ip_header ip_pkt;
344 struct icmp_ttl_exceeded_header icmp_ttl;
345 struct icmp_echo_header icmp_echo;
346 struct udp_header udp_pkt;
347 size_t off;
348 uint16_t port;
349
350 have = read (icmpsock, buf, sizeof (buf));
351 if (-1 == have)
352 {
353 fprintf (stderr,
354 "Error reading raw socket: %s\n",
355 strerror (errno));
356 return;
357 }
358#if VERBOSE
359 fprintf (stderr,
360 "Received message of %u bytes\n",
361 (unsigned int) have);
362#endif
363 if (have < (ssize_t) (sizeof (struct ip_header) + sizeof (struct icmp_ttl_exceeded_header) + sizeof (struct ip_header)))
364 {
365 /* malformed */
366 return;
367 }
368 off = 0;
369 memcpy (&ip_pkt,
370 &buf[off],
371 sizeof (struct ip_header));
372 off += sizeof (struct ip_header);
373 memcpy(&source_ip,
374 &ip_pkt.src_ip,
375 sizeof (source_ip));
376 memcpy (&icmp_ttl,
377 &buf[off],
378 sizeof (struct icmp_ttl_exceeded_header));
379 off += sizeof (struct icmp_ttl_exceeded_header);
380 if ( (ICMP_TIME_EXCEEDED != icmp_ttl.type) ||
381 (0 != icmp_ttl.code) )
382 {
383 /* different type than what we want */
384 return;
385 }
386 /* skip 2nd IP header */
387 memcpy (&ip_pkt,
388 &buf[off],
389 sizeof (struct ip_header));
390 off += sizeof (struct ip_header);
391
392 switch (ip_pkt.proto)
393 {
394 case IPPROTO_ICMP:
395 if (have != (sizeof (struct ip_header) * 2 +
396 sizeof (struct icmp_ttl_exceeded_header) +
397 sizeof (struct icmp_echo_header)) )
398 {
399 /* malformed */
400 return;
401 }
402 /* grab ICMP ECHO content */
403 memcpy (&icmp_echo,
404 &buf[off],
405 sizeof (struct icmp_echo_header));
406 port = (uint16_t) ntohl (icmp_echo.reserved);
407 break;
408 case IPPROTO_UDP:
409 if (have != (sizeof (struct ip_header) * 2 +
410 sizeof (struct icmp_ttl_exceeded_header) +
411 sizeof (struct udp_header)) )
412 {
413 /* malformed */
414 return;
415 }
416 /* grab UDP content */
417 memcpy (&udp_pkt,
418 &buf[off],
419 sizeof (struct udp_header));
420 port = ntohs (udp_pkt.length);
421 break;
422 default:
423 /* different type than what we want */
424 return;
425 }
426
427 if (port == 0)
428 fprintf (stdout,
429 "%s\n",
430 inet_ntop (AF_INET,
431 &source_ip,
432 buf,
433 sizeof (buf)));
434 else
435 fprintf (stdout,
436 "%s:%u\n",
437 inet_ntop (AF_INET,
438 &source_ip,
439 buf,
440 sizeof (buf)),
441 (unsigned int) port);
442 fflush (stdout);
443}
444
445
446/**
447 * Create an ICMP raw socket for reading.
448 *
449 * @return -1 on error
450 */
451static int
452make_icmp_socket ()
453{
454 int ret;
455
456 ret = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
457 if (-1 == ret)
458 {
459 fprintf (stderr,
460 "Error opening RAW socket: %s\n",
461 strerror (errno));
462 return -1;
463 }
464 if (ret >= FD_SETSIZE)
465 {
466 fprintf (stderr,
467 "Socket number too large (%d > %u)\n",
468 ret,
469 (unsigned int) FD_SETSIZE);
470 close (ret);
471 return -1;
472 }
473 return ret;
474}
475
476
477/**
478 * Create an ICMP raw socket for writing.
479 *
480 * @return -1 on error
481 */
482static int
483make_raw_socket ()
484{
485 const int one = 1;
486 int ret;
487
488 ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
489 if (-1 == ret)
490 {
491 fprintf (stderr,
492 "Error opening RAW socket: %s\n",
493 strerror (errno));
494 return -1;
495 }
496 if (-1 == setsockopt(ret,
497 SOL_SOCKET,
498 SO_BROADCAST,
499 (char *)&one, sizeof(one)))
500 {
501 fprintf(stderr,
502 "setsockopt failed: %s\n",
503 strerror (errno));
504 close (ret);
505 return -1;
506 }
507 if (-1 == setsockopt(ret,
508 IPPROTO_IP,
509 IP_HDRINCL,
510 (char *)&one, sizeof(one)))
511 {
512 fprintf(stderr,
513 "setsockopt failed: %s\n",
514 strerror (errno));
515 close (ret);
516 return -1;
517 }
518 return ret;
519}
520
521
522/**
523 * Create a UDP socket for writinging.
524 *
525 * @param my_ip source address (our ip address)
526 * @return -1 on error
527 */
528static int
529make_udp_socket (const struct in_addr *my_ip)
530{
531 int ret;
532 struct sockaddr_in addr;
533
534 ret = socket (AF_INET, SOCK_DGRAM, 0);
535 if (-1 == ret)
536 {
537 fprintf (stderr,
538 "Error opening UDP socket: %s\n",
539 strerror (errno));
540 return -1;
541 }
542 memset (&addr,
543 0,
544 sizeof (addr));
545 addr.sin_family = AF_INET;
546#if HAVE_SOCKADDR_IN_SIN_LEN
547 addr.sin_len = sizeof (struct sockaddr_in);
548#endif
549 addr.sin_addr = *my_ip;
550 addr.sin_port = htons (NAT_TRAV_PORT);
551
552 if (0 != bind (ret,
553 &addr,
554 sizeof(addr)))
555 {
556 fprintf (stderr,
557 "Error binding UDP socket to port %u: %s\n",
558 NAT_TRAV_PORT,
559 strerror (errno));
560 /* likely problematic, but not certain, try to continue */
561 }
562 return ret;
563}
564
565
566int
567main (int argc,
568 char *const *argv)
569{
570 struct in_addr external;
571 fd_set rs;
572 struct timeval tv;
573 uid_t uid;
574 unsigned int alt;
575
576 if (2 != argc)
577 {
578 fprintf (stderr,
579 "This program must be started with our (internal NAT) IP as the only argument.\n");
580 return 1;
581 }
582 if (1 != inet_pton (AF_INET, argv[1], &external))
583 {
584 fprintf (stderr,
585 "Error parsing IPv4 address: %s\n",
586 strerror (errno));
587 return 1;
588 }
589 if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
590 {
591 fprintf (stderr,
592 "Internal error converting dummy IP to binary.\n");
593 return 2;
594 }
595 if (-1 == (icmpsock = make_icmp_socket()))
596 {
597 return 3;
598 }
599 if (-1 == (rawsock = make_raw_socket()))
600 {
601 close (icmpsock);
602 return 3;
603 }
604 uid = getuid ();
605 if (0 != setresuid (uid, uid, uid))
606 {
607 fprintf (stderr,
608 "Failed to setresuid: %s\n",
609 strerror (errno));
610 /* not critical, continue anyway */
611 }
612 if (-1 == (udpsock = make_udp_socket(&external)))
613 {
614 close (icmpsock);
615 close (rawsock);
616 return 3;
617 }
618 alt = 0;
619 while (1)
620 {
621 FD_ZERO (&rs);
622 FD_SET (icmpsock, &rs);
623 tv.tv_sec = 0;
624 tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000;
625 if (-1 == select (icmpsock + 1, &rs, NULL, NULL, &tv))
626 {
627 if (errno == EINTR)
628 continue;
629 fprintf (stderr,
630 "select failed: %s\n",
631 strerror (errno));
632 break;
633 }
634 if (1 == getppid()) /* Check the parent process id, if 1 the parent has died, so we should die too */
635 break;
636 if (FD_ISSET (icmpsock, &rs))
637 process_icmp_response ();
638 if (0 == (++alt % 2))
639 send_icmp_echo (&external);
640 else
641 send_udp ();
642 }
643 /* select failed (internal error or OS out of resources) */
644 close (icmpsock);
645 close (rawsock);
646 close (udpsock);
647 return 4;
648}
649
650
651/* end of gnunet-nat-server.c */