GNUnet  0.10.x
gnunet-helper-nat-client-windows.c
Go to the documentation of this file.
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 
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 
69 #define DUMMY_IP "192.0.2.86"
70 
71 #define NAT_TRAV_PORT 22225
72 
76 struct ip_header {
80  uint8_t vers_ihl;
81 
85  uint8_t tos;
86 
90  uint16_t pkt_len;
91 
95  uint16_t id;
96 
101 
105  uint8_t ttl;
106 
110  uint8_t proto;
111 
115  uint16_t checksum;
116 
120  uint32_t src_ip;
121 
125  uint32_t dst_ip;
126 };
127 
128 
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 
145  uint8_t type;
146 
147  uint8_t code;
148 
149  uint16_t checksum;
150 
151  uint32_t reserved;
152 };
153 
157 struct 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 
170 static boolean privilege_testing = FALSE;
171 
175 static _win_socket rawsock;
176 
180 static struct in_addr dummy;
181 
185 static uint16_t port;
186 
187 
188 
197 static int
198 inet_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 
217 static uint16_t
218 calc_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 
238 static void
239 send_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 
332 static void
333 send_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 
429 static _win_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 
461 int
462 main(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 */
static struct in_addr dummy
Target "dummy" address.
uint32_t src_ip
Source address.
uint16_t checksum
Header checksum.
uint16_t id
Identification.
uint32_t dst_ip
Destination address.
uint8_t vers_ihl
Version (4 bits) + Internet header length (4 bits)
uint8_t tos
Type of service.
#define DUMMY_IP
Must match IP given in the server.
static int ret
Final status code.
Definition: gnunet-arm.c:89
static void send_icmp(const struct in_addr *my_ip, const struct in_addr *other)
Send an ICMP message to the target.
uint16_t pkt_len
Total length.
#define GNUNET_memcpy(dst, src, n)
static struct GNUNET_OS_Process * p
Helper process we started.
Definition: gnunet-qr.c:59
uint16_t flags_frag_offset
Flags (3 bits) + Fragment offset (13 bits)
#define INVALID_SOCKET
Definition: network.c:39
static char buf[2048]
static uint16_t calc_checksum(const uint16_t *data, unsigned int bytes)
CRC-16 for IP/ICMP headers.
uint8_t ttl
Time to live.
static boolean privilege_testing
Will this binary be run in permissions testing mode?
static uint16_t port
Port we are listening on (communicated to the server).
static _win_socket rawsock
Socket we use to send our ICMP packets.
static void send_icmp_udp(const struct in_addr *my_ip, const struct in_addr *other)
Send an ICMP message to the target.
static int inet_pton(int af, const char *cp, struct in_addr *buf)
Convert IPv4 address from text to binary form.
static _win_socket make_raw_socket()
Create an ICMP raw socket.
Beginning of UDP packet.
#define ICMP_TIME_EXCEEDED
uint32_t data
The data value.
int main(int argc, char *const *argv)