aboutsummaryrefslogtreecommitdiff
path: root/src/nat/gnunet-helper-nat-client.c
diff options
context:
space:
mode:
authorMartin Schanzenbach <schanzen@gnunet.org>2023-10-18 19:35:11 +0200
committerMartin Schanzenbach <schanzen@gnunet.org>2023-10-18 19:35:11 +0200
commitddfee3f564bff9c5d5719af3132d7869b8783ec4 (patch)
treee6fd7801fe6808797f3418bf081ab68d5a5ec27b /src/nat/gnunet-helper-nat-client.c
parent852718c2473e41bc01ada0d53ad93c7da78e6ec8 (diff)
downloadgnunet-ddfee3f564bff9c5d5719af3132d7869b8783ec4.tar.gz
gnunet-ddfee3f564bff9c5d5719af3132d7869b8783ec4.zip
BUILD: more more components into new structure; ftbfs fix
Diffstat (limited to 'src/nat/gnunet-helper-nat-client.c')
-rw-r--r--src/nat/gnunet-helper-nat-client.c547
1 files changed, 0 insertions, 547 deletions
diff --git a/src/nat/gnunet-helper-nat-client.c b/src/nat/gnunet-helper-nat-client.c
deleted file mode 100644
index 0b86aa0d2..000000000
--- a/src/nat/gnunet-helper-nat-client.c
+++ /dev/null
@@ -1,547 +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.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 "platform.h"
47#include "gnunet_private_config.h"
48#else
49#define _GNU_SOURCE
50#endif
51#include <sys/types.h>
52#include <sys/socket.h>
53#include <arpa/inet.h>
54#include <sys/types.h>
55#include <unistd.h>
56#include <stdio.h>
57#include <string.h>
58#include <errno.h>
59#include <stdlib.h>
60#include <stdint.h>
61#include <netinet/ip.h>
62#include <netinet/ip_icmp.h>
63#include <netinet/in.h>
64
65/* The following constant is missing from FreeBSD 9.2 */
66#ifndef ICMP_TIME_EXCEEDED
67#define ICMP_TIME_EXCEEDED 11
68#endif
69
70/**
71 * Call memcpy() but check for @a n being 0 first. In the latter
72 * case, it is now safe to pass NULL for @a src or @a dst.
73 * Unlike traditional memcpy(), returns nothing.
74 *
75 * @param dst destination of the copy, may be NULL if @a n is zero
76 * @param src source of the copy, may be NULL if @a n is zero
77 * @param n number of bytes to copy
78 */
79#define GNUNET_memcpy(dst, src, n) do { if (0 != n) { (void) memcpy (dst, src, \
80 n); \
81 } } while (0)
82
83/**
84 * Must match IP given in the server.
85 */
86#define DUMMY_IP "192.0.2.86"
87
88#define NAT_TRAV_PORT 22225
89
90/**
91 * Must match packet ID used by gnunet-helper-nat-server.c
92 */
93#define PACKET_ID 256
94
95/**
96 * IPv4 header.
97 */
98struct ip_header
99{
100 /**
101 * Version (4 bits) + Internet header length (4 bits)
102 */
103 uint8_t vers_ihl;
104
105 /**
106 * Type of service
107 */
108 uint8_t tos;
109
110 /**
111 * Total length
112 */
113 uint16_t pkt_len;
114
115 /**
116 * Identification
117 */
118 uint16_t id;
119
120 /**
121 * Flags (3 bits) + Fragment offset (13 bits)
122 */
123 uint16_t flags_frag_offset;
124
125 /**
126 * Time to live
127 */
128 uint8_t ttl;
129
130 /**
131 * Protocol
132 */
133 uint8_t proto;
134
135 /**
136 * Header checksum
137 */
138 uint16_t checksum;
139
140 /**
141 * Source address
142 */
143 uint32_t src_ip;
144
145 /**
146 * Destination address
147 */
148 uint32_t dst_ip;
149};
150
151/**
152 * Format of ICMP packet.
153 */
154struct icmp_ttl_exceeded_header
155{
156 uint8_t type;
157
158 uint8_t code;
159
160 uint16_t checksum;
161
162 uint32_t unused;
163
164 /* followed by original payload */
165};
166
167struct icmp_echo_header
168{
169 uint8_t type;
170
171 uint8_t code;
172
173 uint16_t checksum;
174
175 uint32_t reserved;
176};
177
178/**
179 * Beginning of UDP packet.
180 */
181struct udp_header
182{
183 uint16_t src_port;
184
185 uint16_t dst_port;
186
187 uint16_t length;
188
189 uint16_t crc;
190};
191
192/**
193 * Socket we use to send our fake ICMP replies.
194 */
195static int rawsock;
196
197/**
198 * Target "dummy" address of the packet we pretend to respond to.
199 */
200static struct in_addr dummy;
201
202/**
203 * Our "source" port.
204 */
205static uint16_t port;
206
207
208/**
209 * CRC-16 for IP/ICMP headers.
210 *
211 * @param data what to calculate the CRC over
212 * @param bytes number of bytes in data (must be multiple of 2)
213 * @return the CRC 16.
214 */
215static uint16_t
216calc_checksum (const uint16_t *data, 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 target.
232 *
233 * @param my_ip source address
234 * @param other target address
235 */
236static void
237send_icmp_udp (const struct in_addr *my_ip, const struct in_addr *other)
238{
239 char packet[sizeof(struct ip_header) * 2
240 + sizeof(struct icmp_ttl_exceeded_header)
241 + sizeof(struct udp_header)];
242 struct ip_header ip_pkt;
243 struct icmp_ttl_exceeded_header icmp_pkt;
244 struct udp_header udp_pkt;
245 struct sockaddr_in dst;
246 size_t off;
247 int err;
248
249 /* ip header: send to (known) ip address */
250 off = 0;
251 ip_pkt.vers_ihl = 0x45;
252 ip_pkt.tos = 0;
253 /* should this be BSD only? */
254#if defined(BSD) && defined(__FreeBSD__) && defined(__FreeBSD_kernel__)
255 ip_pkt.pkt_len = sizeof(packet); /* Workaround PR kern/21737 */
256#else
257 ip_pkt.pkt_len = htons (sizeof(packet));
258#endif
259 ip_pkt.id = htons (PACKET_ID);
260 ip_pkt.flags_frag_offset = 0;
261 ip_pkt.ttl = 128;
262 ip_pkt.proto = IPPROTO_ICMP;
263 ip_pkt.checksum = 0;
264 ip_pkt.src_ip = my_ip->s_addr;
265 ip_pkt.dst_ip = other->s_addr;
266 ip_pkt.checksum =
267 htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
268 GNUNET_memcpy (&packet[off],
269 &ip_pkt,
270 sizeof(struct ip_header));
271 off += sizeof(struct ip_header);
272
273 icmp_pkt.type = ICMP_TIME_EXCEEDED;
274 icmp_pkt.code = 0;
275 icmp_pkt.checksum = 0;
276 icmp_pkt.unused = 0;
277 GNUNET_memcpy (&packet[off],
278 &icmp_pkt,
279 sizeof(struct icmp_ttl_exceeded_header));
280 off += sizeof(struct icmp_ttl_exceeded_header);
281
282 /* ip header of the presumably 'lost' udp packet */
283 ip_pkt.vers_ihl = 0x45;
284 ip_pkt.tos = 0;
285 ip_pkt.pkt_len =
286 htons (sizeof(struct ip_header) + sizeof(struct udp_header));
287 ip_pkt.id = htons (0);
288 ip_pkt.flags_frag_offset = 0;
289 ip_pkt.ttl = 128;
290 ip_pkt.proto = IPPROTO_UDP;
291 ip_pkt.checksum = 0;
292 ip_pkt.src_ip = other->s_addr;
293 ip_pkt.dst_ip = dummy.s_addr;
294 ip_pkt.checksum =
295 htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
296 GNUNET_memcpy (&packet[off],
297 &ip_pkt,
298 sizeof(struct ip_header));
299 off += sizeof(struct ip_header);
300
301 /* build UDP header */
302 udp_pkt.src_port = htons (NAT_TRAV_PORT);
303 udp_pkt.dst_port = htons (NAT_TRAV_PORT);
304 udp_pkt.length = htons (port);
305 udp_pkt.crc = 0;
306 GNUNET_memcpy (&packet[off],
307 &udp_pkt,
308 sizeof(struct udp_header));
309 off += sizeof(struct udp_header);
310
311 /* set ICMP checksum */
312 icmp_pkt.checksum =
313 htons (calc_checksum
314 ((uint16_t *) &packet[sizeof(struct ip_header)],
315 sizeof(struct icmp_ttl_exceeded_header)
316 + sizeof(struct ip_header) + sizeof(struct udp_header)));
317 GNUNET_memcpy (&packet[sizeof(struct ip_header)],
318 &icmp_pkt,
319 sizeof(struct icmp_ttl_exceeded_header));
320
321 memset (&dst, 0, sizeof(dst));
322 dst.sin_family = AF_INET;
323#if HAVE_SOCKADDR_IN_SIN_LEN
324 dst.sin_len = sizeof(struct sockaddr_in);
325#endif
326 dst.sin_addr = *other;
327 err =
328 sendto (rawsock, packet, sizeof(packet), 0, (struct sockaddr *) &dst,
329 sizeof(dst));
330 if (err < 0)
331 {
332 fprintf (stderr, "sendto failed: %s\n", strerror (errno));
333 }
334 else if (sizeof(packet) != (size_t) err)
335 {
336 fprintf (stderr, "Error: partial send of ICMP message with size %lu\n",
337 (unsigned long) off);
338 }
339}
340
341
342/**
343 * Send an ICMP message to the target.
344 *
345 * @param my_ip source address
346 * @param other target address
347 */
348static void
349send_icmp (const struct in_addr *my_ip, const struct in_addr *other)
350{
351 struct ip_header ip_pkt;
352 struct icmp_ttl_exceeded_header icmp_ttl;
353 struct icmp_echo_header icmp_echo;
354 struct sockaddr_in dst;
355 char packet[sizeof(struct ip_header) * 2
356 + sizeof(struct icmp_ttl_exceeded_header)
357 + sizeof(struct icmp_echo_header)];
358 size_t off;
359 int err;
360
361 /* ip header: send to (known) ip address */
362 off = 0;
363 ip_pkt.vers_ihl = 0x45;
364 ip_pkt.tos = 0;
365#if defined(BSD) && defined(__FreeBSD__) && defined(__FreeBSD_kernel__)
366 ip_pkt.pkt_len = sizeof(packet); /* Workaround PR kern/21737 */
367#else
368 ip_pkt.pkt_len = htons (sizeof(packet));
369#endif
370 ip_pkt.id = htons (PACKET_ID);
371 ip_pkt.flags_frag_offset = 0;
372 ip_pkt.ttl = IPDEFTTL;
373 ip_pkt.proto = IPPROTO_ICMP;
374 ip_pkt.checksum = 0;
375 ip_pkt.src_ip = my_ip->s_addr;
376 ip_pkt.dst_ip = other->s_addr;
377 ip_pkt.checksum =
378 htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
379 GNUNET_memcpy (&packet[off],
380 &ip_pkt,
381 sizeof(struct ip_header));
382 off = sizeof(ip_pkt);
383
384 /* icmp reply: time exceeded */
385 icmp_ttl.type = ICMP_TIME_EXCEEDED;
386 icmp_ttl.code = 0;
387 icmp_ttl.checksum = 0;
388 icmp_ttl.unused = 0;
389 GNUNET_memcpy (&packet[off],
390 &icmp_ttl,
391 sizeof(struct icmp_ttl_exceeded_header));
392 off += sizeof(struct icmp_ttl_exceeded_header);
393
394 /* ip header of the presumably 'lost' udp packet */
395 ip_pkt.vers_ihl = 0x45;
396 ip_pkt.tos = 0;
397 ip_pkt.pkt_len =
398 htons (sizeof(struct ip_header) + sizeof(struct icmp_echo_header));
399 ip_pkt.id = htons (PACKET_ID);
400 ip_pkt.flags_frag_offset = 0;
401 ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
402 ip_pkt.proto = IPPROTO_ICMP;
403 ip_pkt.src_ip = other->s_addr;
404 ip_pkt.dst_ip = dummy.s_addr;
405 ip_pkt.checksum = 0;
406 ip_pkt.checksum =
407 htons (calc_checksum ((uint16_t *) &ip_pkt, sizeof(struct ip_header)));
408 GNUNET_memcpy (&packet[off],
409 &ip_pkt,
410 sizeof(struct ip_header));
411 off += sizeof(struct ip_header);
412
413 icmp_echo.type = ICMP_ECHO;
414 icmp_echo.code = 0;
415 icmp_echo.reserved = htonl (port);
416 icmp_echo.checksum = 0;
417 icmp_echo.checksum =
418 htons (calc_checksum
419 ((uint16_t *) &icmp_echo, sizeof(struct icmp_echo_header)));
420 GNUNET_memcpy (&packet[off],
421 &icmp_echo,
422 sizeof(struct icmp_echo_header));
423
424 /* no go back to calculate ICMP packet checksum */
425 off = sizeof(struct ip_header);
426 icmp_ttl.checksum =
427 htons (calc_checksum
428 ((uint16_t *) &packet[off],
429 sizeof(struct icmp_ttl_exceeded_header)
430 + sizeof(struct ip_header) + sizeof(struct icmp_echo_header)));
431 GNUNET_memcpy (&packet[off],
432 &icmp_ttl,
433 sizeof(struct icmp_ttl_exceeded_header));
434
435 /* prepare for transmission */
436 memset (&dst, 0, sizeof(dst));
437 dst.sin_family = AF_INET;
438#if HAVE_SOCKADDR_IN_SIN_LEN
439 dst.sin_len = sizeof(struct sockaddr_in);
440#endif
441 dst.sin_addr = *other;
442 err =
443 sendto (rawsock, packet, sizeof(packet), 0, (struct sockaddr *) &dst,
444 sizeof(dst));
445 if (err < 0)
446 {
447 fprintf (stderr, "sendto failed: %s\n", strerror (errno));
448 }
449 else if (sizeof(packet) != (size_t) err)
450 {
451 fprintf (stderr, "Error: partial send of ICMP message\n");
452 }
453}
454
455
456int
457main (int argc, char *const *argv)
458{
459 const int one = 1;
460 struct in_addr external;
461 struct in_addr target;
462 uid_t uid;
463 unsigned int p;
464 int raw_eno;
465 int global_ret;
466
467 /* Create an ICMP raw socket for writing (only operation that requires root) */
468 rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
469 raw_eno = errno; /* for later error checking */
470
471 /* now drop root privileges */
472 uid = getuid ();
473#ifdef HAVE_SETRESUID
474 if (0 != setresuid (uid, uid, uid))
475 {
476 fprintf (stderr, "Failed to setresuid: %s\n", strerror (errno));
477 global_ret = 1;
478 goto cleanup;
479 }
480#else
481 if (0 != (setuid (uid) | seteuid (uid)))
482 {
483 fprintf (stderr, "Failed to setuid: %s\n", strerror (errno));
484 global_ret = 2;
485 goto cleanup;
486 }
487#endif
488 if (-1 == rawsock)
489 {
490 fprintf (stderr, "Error opening RAW socket: %s\n", strerror (raw_eno));
491 global_ret = 3;
492 goto cleanup;
493 }
494 if (0 !=
495 setsockopt (rawsock, SOL_SOCKET, SO_BROADCAST, (char *) &one,
496 sizeof(one)))
497 {
498 fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
499 global_ret = 4;
500 goto cleanup;
501 }
502 if (0 !=
503 setsockopt (rawsock, IPPROTO_IP, IP_HDRINCL, (char *) &one, sizeof(one)))
504 {
505 fprintf (stderr, "setsockopt failed: %s\n", strerror (errno));
506 global_ret = 5;
507 goto cleanup;
508 }
509
510 if (4 != argc)
511 {
512 fprintf (stderr,
513 "This program must be started with our IP, the targets external IP, and our port as arguments.\n");
514 global_ret = 6;
515 goto cleanup;
516 }
517 if ((1 != inet_pton (AF_INET, argv[1], &external)) ||
518 (1 != inet_pton (AF_INET, argv[2], &target)))
519 {
520 fprintf (stderr, "Error parsing IPv4 address: %s\n", strerror (errno));
521 global_ret = 7;
522 goto cleanup;
523 }
524 if ((1 != sscanf (argv[3], "%u", &p)) || (0 == p) || (0xFFFF < p))
525 {
526 fprintf (stderr, "Error parsing port value `%s'\n", argv[3]);
527 global_ret = 8;
528 goto cleanup;
529 }
530 port = (uint16_t) p;
531 if (1 != inet_pton (AF_INET, DUMMY_IP, &dummy))
532 {
533 fprintf (stderr, "Internal error converting dummy IP to binary.\n");
534 global_ret = 9;
535 goto cleanup;
536 }
537 send_icmp (&external, &target);
538 send_icmp_udp (&external, &target);
539 global_ret = 0;
540cleanup:
541 if (-1 != rawsock)
542 (void) close (rawsock);
543 return global_ret;
544}
545
546
547/* end of gnunet-helper-nat-client.c */