aboutsummaryrefslogtreecommitdiff
path: root/src/nat
diff options
context:
space:
mode:
authorng0 <ng0@n0.is>2019-09-10 16:59:32 +0000
committerng0 <ng0@n0.is>2019-09-10 16:59:32 +0000
commit04b6df21cd281e8cd540139f8d9ae85defc1961c (patch)
tree6357199445df8d5c0c631bc8f10aef838b1f9f1e /src/nat
parent483b0139a218a5f8a8311bda3eb23bcd88f57688 (diff)
downloadgnunet-04b6df21cd281e8cd540139f8d9ae85defc1961c.tar.gz
gnunet-04b6df21cd281e8cd540139f8d9ae85defc1961c.zip
remove CYGWIN codeblocks, drop vendored Windows openvpn, drop win32 specific files.
configures and builds okay. testsuite wasn't checked, will be checked. diff including the plibc removal is now around 14370 lines of code less.
Diffstat (limited to 'src/nat')
-rw-r--r--src/nat/Makefile.am9
-rw-r--r--src/nat/gnunet-helper-nat-client-windows.c529
-rw-r--r--src/nat/gnunet-helper-nat-server-windows.c614
3 files changed, 1 insertions, 1151 deletions
diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am
index f0d5639a1..db104d194 100644
--- a/src/nat/Makefile.am
+++ b/src/nat/Makefile.am
@@ -1,13 +1,6 @@
1# This Makefile.am is in the public domain 1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include 2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3 3
4if MINGW
5 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
6 NATBIN = gnunet-helper-nat-server gnunet-helper-nat-client
7 NATSERVER = gnunet-helper-nat-server-windows.c
8 NATCLIENT = gnunet-helper-nat-client-windows.c
9endif
10
11libexecdir= $(pkglibdir)/libexec/ 4libexecdir= $(pkglibdir)/libexec/
12 5
13pkgcfgdir= $(pkgdatadir)/config.d/ 6pkgcfgdir= $(pkgdatadir)/config.d/
@@ -45,7 +38,7 @@ gnunet_helper_nat_server_SOURCES = \
45 $(NATSERVER) 38 $(NATSERVER)
46 39
47gnunet_helper_nat_client_SOURCES = \ 40gnunet_helper_nat_client_SOURCES = \
48 $(NATCLIENT) 41 $(NATCLIENT)
49 42
50 43
51gnunet_nat_SOURCES = \ 44gnunet_nat_SOURCES = \
diff --git a/src/nat/gnunet-helper-nat-client-windows.c b/src/nat/gnunet-helper-nat-client-windows.c
deleted file mode 100644
index 1be31ae42..000000000
--- a/src/nat/gnunet-helper-nat-client-windows.c
+++ /dev/null
@@ -1,529 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file src/nat/gnunet-helper-nat-client-windows.c
23 * @brief Tool to help bypass NATs using ICMP method; must run as
24 * administrator on W32
25 * This code is for 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/* Instead of including gnunet_common.h */
46#define GNUNET_memcpy(dst, src, n) do { if (0 != n) { (void)memcpy(dst, src, n); } } while (0)
47
48#define FD_SETSIZE 1024
49#include <winsock2.h>
50#include <ws2tcpip.h>
51#include <sys/time.h>
52#include <sys/types.h>
53#include <unistd.h>
54#include <stdio.h>
55#include <string.h>
56#include <errno.h>
57#include <stdlib.h>
58#include <stdint.h>
59#include <time.h>
60
61
62#define ICMP_ECHO 8
63#define IPDEFTTL 64
64#define ICMP_TIME_EXCEEDED 11
65
66/**
67 * Must match IP given in the server.
68 */
69#define DUMMY_IP "192.0.2.86"
70
71#define NAT_TRAV_PORT 22225
72
73/**
74 * IPv4 header.
75 */
76struct ip_header {
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/**
130 * Format of ICMP packet.
131 */
132struct icmp_ttl_exceeded_header {
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 uint8_t type;
146
147 uint8_t code;
148
149 uint16_t checksum;
150
151 uint32_t reserved;
152};
153
154/**
155 * Beginning of UDP packet.
156 */
157struct udp_header {
158 uint16_t src_port;
159
160 uint16_t dst_port;
161
162 uint16_t length;
163
164 uint16_t crc;
165};
166
167/**
168 * Will this binary be run in permissions testing mode?
169 */
170static boolean privilege_testing = FALSE;
171
172/**
173 * Socket we use to send our ICMP packets.
174 */
175static _win_socket rawsock;
176
177/**
178 * Target "dummy" address.
179 */
180static struct in_addr dummy;
181
182/**
183 * Port we are listening on (communicated to the server).
184 */
185static uint16_t port;
186
187
188
189/**
190 * Convert IPv4 address from text to binary form.
191 *
192 * @param af address family
193 * @param cp the address to print
194 * @param buf where to write the address result
195 * @return 1 on success
196 */
197static int
198inet_pton(int af, const char *cp, struct in_addr *buf)
199{
200 buf->s_addr = inet_addr(cp);
201 if (buf->s_addr == INADDR_NONE)
202 {
203 fprintf(stderr, "Error %d handling address %s", WSAGetLastError(), cp);
204 return 0;
205 }
206 return 1;
207}
208
209
210/**
211 * CRC-16 for IP/ICMP headers.
212 *
213 * @param data what to calculate the CRC over
214 * @param bytes number of bytes in data (must be multiple of 2)
215 * @return the CRC 16.
216 */
217static uint16_t
218calc_checksum(const uint16_t * data, unsigned int bytes)
219{
220 uint32_t sum;
221 unsigned int i;
222
223 sum = 0;
224 for (i = 0; i < bytes / 2; i++)
225 sum += data[i];
226 sum = (sum & 0xffff) + (sum >> 16);
227 sum = htons(0xffff - sum);
228 return sum;
229}
230
231
232/**
233 * Send an ICMP message to the target.
234 *
235 * @param my_ip source address
236 * @param other target address
237 */
238static void
239send_icmp_udp(const struct in_addr *my_ip, const struct in_addr *other)
240{
241 char packet[sizeof(struct ip_header) * 2 +
242 sizeof(struct icmp_ttl_exceeded_header) +
243 sizeof(struct udp_header)];
244 struct ip_header ip_pkt;
245 struct icmp_ttl_exceeded_header icmp_pkt;
246 struct udp_header udp_pkt;
247 struct sockaddr_in dst;
248 size_t off;
249 int err;
250
251 /* ip header: send to (known) ip address */
252 off = 0;
253 ip_pkt.vers_ihl = 0x45;
254 ip_pkt.tos = 0;
255 ip_pkt.pkt_len = htons(sizeof(packet));
256 ip_pkt.id = htons(256);
257 ip_pkt.flags_frag_offset = 0;
258 ip_pkt.ttl = 128;
259 ip_pkt.proto = IPPROTO_ICMP;
260 ip_pkt.checksum = 0;
261 ip_pkt.src_ip = my_ip->s_addr;
262 ip_pkt.dst_ip = other->s_addr;
263 ip_pkt.checksum =
264 htons(calc_checksum((uint16_t *)&ip_pkt, sizeof(struct ip_header)));
265 GNUNET_memcpy(&packet[off], &ip_pkt, sizeof(struct ip_header));
266 off += sizeof(struct ip_header);
267
268 icmp_pkt.type = ICMP_TIME_EXCEEDED;
269 icmp_pkt.code = 0;
270 icmp_pkt.checksum = 0;
271 icmp_pkt.unused = 0;
272 GNUNET_memcpy(&packet[off], &icmp_pkt, sizeof(struct icmp_ttl_exceeded_header));
273 off += sizeof(struct icmp_ttl_exceeded_header);
274
275 /* ip header of the presumably 'lost' udp packet */
276 ip_pkt.vers_ihl = 0x45;
277 ip_pkt.tos = 0;
278 ip_pkt.pkt_len =
279 htons(sizeof(struct ip_header) + sizeof(struct udp_header));
280 ip_pkt.id = htons(0);
281 ip_pkt.flags_frag_offset = 0;
282 ip_pkt.ttl = 128;
283 ip_pkt.proto = IPPROTO_UDP;
284 ip_pkt.checksum = 0;
285 ip_pkt.src_ip = other->s_addr;
286 ip_pkt.dst_ip = dummy.s_addr;
287 ip_pkt.checksum =
288 htons(calc_checksum((uint16_t *)&ip_pkt, sizeof(struct ip_header)));
289 GNUNET_memcpy(&packet[off], &ip_pkt, sizeof(struct ip_header));
290 off += sizeof(struct ip_header);
291
292 /* build UDP header */
293 udp_pkt.src_port = htons(NAT_TRAV_PORT);
294 udp_pkt.dst_port = htons(NAT_TRAV_PORT);
295 udp_pkt.length = htons(port);
296 udp_pkt.crc = 0;
297 GNUNET_memcpy(&packet[off], &udp_pkt, sizeof(struct udp_header));
298 off += sizeof(struct udp_header);
299
300 /* no go back to calculate ICMP packet checksum */
301 icmp_pkt.checksum =
302 htons(calc_checksum
303 ((uint16_t *)&packet[off],
304 sizeof(struct icmp_ttl_exceeded_header) +
305 sizeof(struct ip_header) + sizeof(struct udp_header)));
306 GNUNET_memcpy(&packet[sizeof(struct ip_header)], &icmp_pkt,
307 sizeof(struct icmp_ttl_exceeded_header));
308
309 memset(&dst, 0, sizeof(dst));
310 dst.sin_family = AF_INET;
311 dst.sin_addr = *other;
312 err =
313 sendto(rawsock, packet, sizeof(packet), 0, (struct sockaddr *)&dst,
314 sizeof(dst));
315 if (err < 0)
316 {
317 fprintf(stderr, "sendto failed: %s\n", strerror(errno));
318 }
319 else if (sizeof(packet) != (size_t)err)
320 {
321 fprintf(stderr, "Error: partial send of ICMP message\n");
322 }
323}
324
325
326/**
327 * Send an ICMP message to the target.
328 *
329 * @param my_ip source address
330 * @param other target address
331 */
332static void
333send_icmp(const struct in_addr *my_ip, const struct in_addr *other)
334{
335 struct ip_header ip_pkt;
336 struct icmp_ttl_exceeded_header icmp_ttl;
337 struct icmp_echo_header icmp_echo;
338 struct sockaddr_in dst;
339 char packet[sizeof(struct ip_header) * 2 +
340 sizeof(struct icmp_ttl_exceeded_header) +
341 sizeof(struct icmp_echo_header)];
342 size_t off;
343 int err;
344
345 /* ip header: send to (known) ip address */
346 off = 0;
347 ip_pkt.vers_ihl = 0x45;
348 ip_pkt.tos = 0;
349 ip_pkt.pkt_len = htons(sizeof(packet));
350 ip_pkt.id = htons(256);
351 ip_pkt.flags_frag_offset = 0;
352 ip_pkt.ttl = IPDEFTTL;
353 ip_pkt.proto = IPPROTO_ICMP;
354 ip_pkt.checksum = 0;
355 ip_pkt.src_ip = my_ip->s_addr;
356 ip_pkt.dst_ip = other->s_addr;
357 ip_pkt.checksum =
358 htons(calc_checksum((uint16_t *)&ip_pkt, sizeof(struct ip_header)));
359 GNUNET_memcpy(&packet[off], &ip_pkt, sizeof(struct ip_header));
360 off += sizeof(ip_pkt);
361
362 /* icmp reply: time exceeded */
363 icmp_ttl.type = ICMP_TIME_EXCEEDED;
364 icmp_ttl.code = 0;
365 icmp_ttl.checksum = 0;
366 icmp_ttl.unused = 0;
367 GNUNET_memcpy(&packet[off], &icmp_ttl, sizeof(struct icmp_ttl_exceeded_header));
368 off += sizeof(struct icmp_ttl_exceeded_header);
369
370 /* ip header of the presumably 'lost' udp packet */
371 ip_pkt.vers_ihl = 0x45;
372 ip_pkt.tos = 0;
373 ip_pkt.pkt_len =
374 htons(sizeof(struct ip_header) + sizeof(struct icmp_echo_header));
375 ip_pkt.id = htons(256);
376 ip_pkt.flags_frag_offset = 0;
377 ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
378 ip_pkt.proto = IPPROTO_ICMP;
379 ip_pkt.src_ip = other->s_addr;
380 ip_pkt.dst_ip = dummy.s_addr;
381 ip_pkt.checksum = 0;
382 ip_pkt.checksum =
383 htons(calc_checksum((uint16_t *)&ip_pkt, sizeof(struct ip_header)));
384 GNUNET_memcpy(&packet[off], &ip_pkt, sizeof(struct ip_header));
385 off += sizeof(struct ip_header);
386
387 icmp_echo.type = ICMP_ECHO;
388 icmp_echo.code = 0;
389 icmp_echo.reserved = htonl(port);
390 icmp_echo.checksum = 0;
391 icmp_echo.checksum =
392 htons(calc_checksum
393 ((uint16_t *)&icmp_echo, sizeof(struct icmp_echo_header)));
394 GNUNET_memcpy(&packet[off], &icmp_echo, sizeof(struct icmp_echo_header));
395
396 /* no go back to calculate ICMP packet checksum */
397 off = sizeof(struct ip_header);
398 icmp_ttl.checksum =
399 htons(calc_checksum
400 ((uint16_t *)&packet[off],
401 sizeof(struct icmp_ttl_exceeded_header) +
402 sizeof(struct ip_header) + sizeof(struct icmp_echo_header)));
403 GNUNET_memcpy(&packet[off], &icmp_ttl, sizeof(struct icmp_ttl_exceeded_header));
404
405 memset(&dst, 0, sizeof(dst));
406 dst.sin_family = AF_INET;
407 dst.sin_addr = *other;
408
409 err =
410 sendto(rawsock, packet, sizeof(packet), 0, (struct sockaddr *)&dst,
411 sizeof(dst));
412
413 if (err < 0)
414 {
415 fprintf(stderr, "sendto failed: %s\n", strerror(errno));
416 }
417 else if (sizeof(packet) != (size_t)err)
418 {
419 fprintf(stderr, "Error: partial send of ICMP message\n");
420 }
421}
422
423
424/**
425 * Create an ICMP raw socket.
426 *
427 * @return INVALID_SOCKET on error
428 */
429static _win_socket
430make_raw_socket()
431{
432 DWORD bOptVal = TRUE;
433 int bOptLen = sizeof(bOptVal);
434 _win_socket ret;
435
436 ret = _win_socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
437 if (INVALID_SOCKET == ret)
438 {
439 fprintf(stderr, "Error opening RAW socket: %s\n", strerror(errno));
440 return INVALID_SOCKET;
441 }
442 if (0 !=
443 _win_setsockopt(ret, SOL_SOCKET, SO_BROADCAST, (char *)&bOptVal, bOptLen))
444 {
445 fprintf(stderr, "Error setting SO_BROADCAST to ON: %s\n",
446 strerror(errno));
447 closesocket(rawsock);
448 return INVALID_SOCKET;
449 }
450
451 if (0 != _win_setsockopt(ret, IPPROTO_IP, IP_HDRINCL, (char *)&bOptVal, bOptLen))
452 {
453 fprintf(stderr, "Error setting IP_HDRINCL to ON: %s\n", strerror(errno));
454 closesocket(rawsock);
455 return INVALID_SOCKET;
456 }
457 return ret;
458}
459
460
461int
462main(int argc, char *const *argv)
463{
464 struct in_addr external;
465 struct in_addr target;
466 WSADATA wsaData;
467 unsigned int p;
468
469 if (argc > 1 && 0 != strcmp(argv[1], "-d"))
470 {
471 privilege_testing = TRUE;
472 fprintf(stderr,
473 "%s",
474 "DEBUG: Running binary in privilege testing mode.");
475 argv++;
476 argc--;
477 }
478
479 if (argc != 4)
480 {
481 fprintf(stderr,
482 "%s",
483 "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
484 return 1;
485 }
486 if ((1 != inet_pton(AF_INET, argv[1], &external)) ||
487 (1 != inet_pton(AF_INET, argv[2], &target)))
488 {
489 fprintf(stderr,
490 "Error parsing IPv4 address: %s\n",
491 strerror(errno));
492 return 1;
493 }
494 if ((1 != sscanf(argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
495 {
496 fprintf(stderr,
497 "Error parsing port value `%s'\n",
498 argv[3]);
499 return 1;
500 }
501 port = (uint16_t)p;
502
503 if (0 != WSAStartup(MAKEWORD(2, 1), &wsaData))
504 {
505 fprintf(stderr,
506 "%s",
507 "Failed to find Winsock 2.1 or better.\n");
508 return 2;
509 }
510 if (1 != inet_pton(AF_INET, DUMMY_IP, &dummy))
511 {
512 fprintf(stderr,
513 "%s",
514 "Internal error converting dummy IP to binary.\n");
515 return 2;
516 }
517 if (-1 == (rawsock = make_raw_socket()))
518 return 3;
519 if (!privilege_testing)
520 {
521 send_icmp(&external, &target);
522 send_icmp_udp(&external, &target);
523 }
524 closesocket(rawsock);
525 WSACleanup();
526 return 0;
527}
528
529/* end of gnunet-helper-nat-client-windows.c */
diff --git a/src/nat/gnunet-helper-nat-server-windows.c b/src/nat/gnunet-helper-nat-server-windows.c
deleted file mode 100644
index 688dde165..000000000
--- a/src/nat/gnunet-helper-nat-server-windows.c
+++ /dev/null
@@ -1,614 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file src/nat/gnunet-helper-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/* Instead of including gnunet_common.h */
45#define GNUNET_memcpy(dst, src, n) do { if (0 != n) { (void)memcpy(dst, src, n); } } while (0)
46
47#define FD_SETSIZE 1024
48#include <winsock2.h>
49#include <ws2tcpip.h>
50#include <sys/time.h>
51#include <sys/types.h>
52#include <unistd.h>
53#include <stdio.h>
54#include <string.h>
55#include <errno.h>
56#include <stdlib.h>
57#include <stdint.h>
58#include <time.h>
59
60/**
61 * Should we print some debug output?
62 */
63#define VERBOSE 0
64
65/**
66 * Must match IP given in the client.
67 */
68#define DUMMY_IP "192.0.2.86"
69
70/**
71 * Default Port
72 */
73#define NAT_TRAV_PORT 22225
74
75/**
76 * Must match packet ID used by gnunet-helper-nat-client.c
77 */
78#define PACKET_ID 256
79
80/**
81 * TTL to use for our outgoing messages.
82 */
83#define IPDEFTTL 64
84
85#define ICMP_ECHO 8
86
87#define ICMP_TIME_EXCEEDED 11
88
89/**
90 * How often do we send our ICMP messages to receive replies?
91 */
92#define ICMP_SEND_FREQUENCY_MS 500
93
94/**
95 * IPv4 header.
96 */
97struct ip_header {
98 /**
99 * Version (4 bits) + Internet header length (4 bits)
100 */
101 uint8_t vers_ihl;
102
103 /**
104 * Type of service
105 */
106 uint8_t tos;
107
108 /**
109 * Total length
110 */
111 uint16_t pkt_len;
112
113 /**
114 * Identification
115 */
116 uint16_t id;
117
118 /**
119 * Flags (3 bits) + Fragment offset (13 bits)
120 */
121 uint16_t flags_frag_offset;
122
123 /**
124 * Time to live
125 */
126 uint8_t ttl;
127
128 /**
129 * Protocol
130 */
131 uint8_t proto;
132
133 /**
134 * Header checksum
135 */
136 uint16_t checksum;
137
138 /**
139 * Source address
140 */
141 uint32_t src_ip;
142
143 /**
144 * Destination address
145 */
146 uint32_t dst_ip;
147};
148
149/**
150 * Format of ICMP packet.
151 */
152struct icmp_ttl_exceeded_header {
153 uint8_t type;
154
155 uint8_t code;
156
157 uint16_t checksum;
158
159 uint32_t unused;
160
161 /* followed by original payload */
162};
163
164struct icmp_echo_header {
165 uint8_t type;
166
167 uint8_t code;
168
169 uint16_t checksum;
170
171 uint32_t reserved;
172};
173
174/**
175 * Beginning of UDP packet.
176 */
177struct udp_header {
178 uint16_t src_port;
179
180 uint16_t dst_port;
181
182 uint16_t length;
183
184 uint16_t crc;
185};
186
187/**
188 * Will this binary be run in permissions testing mode?
189 */
190static boolean privilege_testing = FALSE;
191
192/**
193 * Socket we use to receive "fake" ICMP replies.
194 */
195static _win_socket icmpsock;
196
197/**
198 * Socket we use to send our ICMP requests.
199 */
200static _win_socket rawsock;
201
202/**
203 * Socket we use to send our UDP requests.
204 */
205static _win_socket udpsock;
206
207/**
208 * Target "dummy" address.
209 */
210static struct in_addr dummy;
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, unsigned int bytes)
222{
223 uint32_t sum;
224 unsigned int i;
225
226 sum = 0;
227 for (i = 0; i < bytes / 2; i++)
228 sum += data[i];
229 sum = (sum & 0xffff) + (sum >> 16);
230 sum = htons(0xffff - sum);
231 return sum;
232}
233
234
235/**
236 * Convert IPv4 address from text to binary form.
237 *
238 * @param af address family
239 * @param cp the address to print
240 * @param buf where to write the address result
241 * @return 1 on success
242 */
243static int
244inet_pton(int af, const char *cp, struct in_addr *buf)
245{
246 buf->s_addr = inet_addr(cp);
247 if (buf->s_addr == INADDR_NONE)
248 {
249 fprintf(stderr, "Error %d handling address %s", WSAGetLastError(), cp);
250 return 0;
251 }
252 return 1;
253}
254
255
256/**
257 * Send an ICMP message to the dummy IP.
258 *
259 * @param my_ip source address (our ip address)
260 */
261static void
262send_icmp_echo(const struct in_addr *my_ip)
263{
264 char packet[sizeof(struct ip_header) + sizeof(struct icmp_echo_header)];
265 struct icmp_echo_header icmp_echo;
266 struct ip_header ip_pkt;
267 struct sockaddr_in dst;
268 size_t off;
269 int err;
270
271 off = 0;
272 ip_pkt.vers_ihl = 0x45;
273 ip_pkt.tos = 0;
274 ip_pkt.pkt_len = htons(sizeof(packet));
275 ip_pkt.id = htons(PACKET_ID);
276 ip_pkt.flags_frag_offset = 0;
277 ip_pkt.ttl = IPDEFTTL;
278 ip_pkt.proto = IPPROTO_ICMP;
279 ip_pkt.checksum = 0;
280 ip_pkt.src_ip = my_ip->s_addr;
281 ip_pkt.dst_ip = dummy.s_addr;
282 ip_pkt.checksum =
283 htons(calc_checksum((uint16_t *)&ip_pkt, sizeof(struct ip_header)));
284 GNUNET_memcpy(&packet[off], &ip_pkt, sizeof(struct ip_header));
285 off += sizeof(struct ip_header);
286
287 icmp_echo.type = ICMP_ECHO;
288 icmp_echo.code = 0;
289 icmp_echo.reserved = 0;
290 icmp_echo.checksum = 0;
291 icmp_echo.checksum =
292 htons(calc_checksum
293 ((uint16_t *)&icmp_echo, sizeof(struct icmp_echo_header)));
294 GNUNET_memcpy(&packet[off], &icmp_echo, sizeof(struct icmp_echo_header));
295 off += sizeof(struct icmp_echo_header);
296
297 memset(&dst, 0, sizeof(dst));
298 dst.sin_family = AF_INET;
299 dst.sin_addr = dummy;
300 err =
301 sendto(rawsock, packet, off, 0, (struct sockaddr *)&dst, sizeof(dst));
302 if (err < 0)
303 {
304#if VERBOSE
305 fprintf(stderr, "sendto failed: %s\n", strerror(errno));
306#endif
307 }
308 else if (err != off)
309 {
310 fprintf(stderr, "Error: partial send of ICMP message\n");
311 }
312}
313
314
315/**
316 * Send a UDP message to the dummy IP.
317 */
318static void
319send_udp()
320{
321 struct sockaddr_in dst;
322 ssize_t err;
323
324 memset(&dst, 0, sizeof(dst));
325 dst.sin_family = AF_INET;
326 dst.sin_addr = dummy;
327 dst.sin_port = htons(NAT_TRAV_PORT);
328 err = sendto(udpsock, NULL, 0, 0, (struct sockaddr *)&dst, sizeof(dst));
329 if (err < 0)
330 {
331#if VERBOSE
332 fprintf(stderr, "sendto failed: %s\n", strerror(errno));
333#endif
334 }
335 else if (0 != err)
336 {
337 fprintf(stderr, "Error: partial send of ICMP message\n");
338 }
339}
340
341
342/**
343 * We've received an ICMP response. Process it.
344 */
345static void
346process_icmp_response()
347{
348 char buf[65536];
349 ssize_t have;
350 struct in_addr source_ip;
351 struct ip_header ip_pkt;
352 struct icmp_ttl_exceeded_header icmp_ttl;
353 struct icmp_echo_header icmp_echo;
354 struct udp_header udp_pkt;
355 size_t off;
356 uint16_t port;
357 DWORD ssize;
358
359 have = read(icmpsock, buf, sizeof(buf));
360 if (have == -1)
361 {
362 fprintf(stderr, "Error reading raw socket: %s\n", strerror(errno));
363 return;
364 }
365#if VERBOSE
366 fprintf(stderr, "Received message of %u bytes\n", (unsigned int)have);
367#endif
368 if (have <
369 (ssize_t)(sizeof(struct ip_header) +
370 sizeof(struct icmp_ttl_exceeded_header) +
371 sizeof(struct ip_header)))
372 {
373 /* malformed */
374 return;
375 }
376 off = 0;
377 GNUNET_memcpy(&ip_pkt, &buf[off], sizeof(struct ip_header));
378 off += sizeof(struct ip_header);
379 GNUNET_memcpy(&source_ip, &ip_pkt.src_ip, sizeof(source_ip));
380 GNUNET_memcpy(&icmp_ttl, &buf[off], sizeof(struct icmp_ttl_exceeded_header));
381 off += sizeof(struct icmp_ttl_exceeded_header);
382 if ((ICMP_TIME_EXCEEDED != icmp_ttl.type) || (0 != icmp_ttl.code))
383 {
384 /* different type than what we want */
385 return;
386 }
387 /* skip 2nd IP header */
388 GNUNET_memcpy(&ip_pkt, &buf[off], sizeof(struct ip_header));
389 off += sizeof(struct ip_header);
390
391 switch (ip_pkt.proto)
392 {
393 case IPPROTO_ICMP:
394 if (have !=
395 (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 GNUNET_memcpy(&icmp_echo, &buf[off], sizeof(struct icmp_echo_header));
404 port = (uint16_t)ntohl(icmp_echo.reserved);
405 break;
406
407 case IPPROTO_UDP:
408 if (have !=
409 (sizeof(struct ip_header) * 2 +
410 sizeof(struct icmp_ttl_exceeded_header) + sizeof(struct udp_header)))
411 {
412 /* malformed */
413 return;
414 }
415 /* grab UDP content */
416 GNUNET_memcpy(&udp_pkt, &buf[off], sizeof(struct udp_header));
417 port = ntohs(udp_pkt.length);
418 break;
419
420 default:
421 /* different type than what we want */
422 return;
423 }
424
425 ssize = sizeof(buf);
426 WSAAddressToString((LPSOCKADDR)&source_ip, sizeof(source_ip), NULL, buf,
427 &ssize);
428 if (port == 0)
429 fprintf(stdout, "%s\n", buf);
430 else
431 fprintf(stdout, "%s:%u\n", buf, (unsigned int)port);
432 fflush(stdout);
433}
434
435
436/**
437 * Create an ICMP raw socket for reading.
438 *
439 * @return INVALID_SOCKET on error
440 */
441static _win_socket
442make_icmp_socket()
443{
444 _win_socket ret;
445
446 ret = _win_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
447 if (INVALID_SOCKET == ret)
448 {
449 fprintf(stderr, "Error opening RAW socket: %s\n", strerror(errno));
450 return INVALID_SOCKET;
451 }
452 return ret;
453}
454
455
456/**
457 * Create an ICMP raw socket for writing.
458 *
459 * @return INVALID_SOCKET on error
460 */
461static _win_socket
462make_raw_socket()
463{
464 DWORD bOptVal = TRUE;
465 int bOptLen = sizeof(bOptVal);
466
467 rawsock = _win_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
468 if (INVALID_SOCKET == rawsock)
469 {
470 fprintf(stderr, "Error opening RAW socket: %s\n", strerror(errno));
471 return INVALID_SOCKET;
472 }
473
474 if (0 !=
475 _win_setsockopt(rawsock, SOL_SOCKET, SO_BROADCAST, (char *)&bOptVal,
476 bOptLen))
477 {
478 fprintf(stderr, "Error setting SO_BROADCAST to ON: %s\n",
479 strerror(errno));
480 closesocket(rawsock);
481 return INVALID_SOCKET;
482 }
483 if (0 !=
484 _win_setsockopt(rawsock, IPPROTO_IP, IP_HDRINCL, (char *)&bOptVal, bOptLen))
485 {
486 fprintf(stderr, "Error setting IP_HDRINCL to ON: %s\n", strerror(errno));
487 closesocket(rawsock);
488 return INVALID_SOCKET;
489 }
490 return rawsock;
491}
492
493
494/**
495 * Create a UDP socket for writing.
496 *
497 * @param my_ip source address (our ip address)
498 * @return INVALID_SOCKET on error
499 */
500static _win_socket
501make_udp_socket(const struct in_addr *my_ip)
502{
503 _win_socket ret;
504 struct sockaddr_in addr;
505
506 ret = _win_socket(AF_INET, SOCK_DGRAM, 0);
507 if (INVALID_SOCKET == ret)
508 {
509 fprintf(stderr, "Error opening UDP socket: %s\n", strerror(errno));
510 return INVALID_SOCKET;
511 }
512 memset(&addr, 0, sizeof(addr));
513 addr.sin_family = AF_INET;
514 addr.sin_addr = *my_ip;
515 addr.sin_port = htons(NAT_TRAV_PORT);
516 if (0 != bind(ret, (struct sockaddr *)&addr, sizeof(addr)))
517 {
518 fprintf(stderr, "Error binding UDP socket to port %u: %s\n", NAT_TRAV_PORT,
519 strerror(errno));
520 /* likely problematic, but not certain, try to continue */
521 }
522 return ret;
523}
524
525
526int
527main(int argc, char *const *argv)
528{
529 struct in_addr external;
530 fd_set rs;
531 struct timeval tv;
532 WSADATA wsaData;
533 unsigned int alt = 0;
534
535 if ((argc > 1) && (0 != strcmp(argv[1], "-d")))
536 {
537 privilege_testing = TRUE;
538 fprintf(stderr,
539 "%s",
540 "DEBUG: Running binary in privilege testing mode.");
541 argv++;
542 argc--;
543 }
544
545 if (2 != argc)
546 {
547 fprintf(stderr,
548 "This program must be started with our (internal NAT) IP as the only argument.\n");
549 return 1;
550 }
551 if (1 != inet_pton(AF_INET, argv[1], &external))
552 {
553 fprintf(stderr, "Error parsing IPv4 address: %s, error %s\n", argv[1],
554 strerror(errno));
555 return 1;
556 }
557 if (1 != inet_pton(AF_INET, DUMMY_IP, &dummy))
558 {
559 fprintf(stderr, "Internal error converting dummy IP to binary.\n");
560 return 2;
561 }
562 if (WSAStartup(MAKEWORD(2, 1), &wsaData) != 0)
563 {
564 fprintf(stderr, "Failed to find Winsock 2.1 or better.\n");
565 return 2;
566 }
567 if (INVALID_SOCKET == (icmpsock = make_icmp_socket()))
568 {
569 return 3;
570 }
571 if (INVALID_SOCKET == (make_raw_socket()))
572 {
573 closesocket(icmpsock);
574 return 3;
575 }
576 if (INVALID_SOCKET == (udpsock = make_udp_socket(&external)))
577 {
578 closesocket(icmpsock);
579 closesocket(rawsock);
580 return 3;
581 }
582
583 while (!privilege_testing)
584 {
585 FD_ZERO(&rs);
586 FD_SET(icmpsock, &rs);
587 tv.tv_sec = 0;
588 tv.tv_usec = ICMP_SEND_FREQUENCY_MS * 1000;
589 if (-1 == select(icmpsock + 1, &rs, NULL, NULL, &tv))
590 {
591 if (errno == EINTR)
592 continue;
593 fprintf(stderr, "select failed: %s\n", strerror(errno));
594 break;
595 }
596 if (FD_ISSET(icmpsock, &rs))
597 process_icmp_response();
598 if (0 == (++alt % 2))
599 send_icmp_echo(&external);
600 else
601 send_udp();
602 }
603 /* select failed (internal error or OS out of resources) */
604 closesocket(icmpsock);
605 closesocket(rawsock);
606 closesocket(udpsock);
607 WSACleanup();
608 if (privilege_testing)
609 return 0;
610 return 4;
611}
612
613
614/* end of gnunet-helper-nat-server-windows.c */