stuff
[oweals/gnunet.git] / src / transport / gnunet-nat-client.c
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 #define _GNU_SOURCE
28 #include <sys/types.h> 
29 #include <sys/socket.h>
30 #include <arpa/inet.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <netinet/ip.h>
39 #include <netinet/ip_icmp.h>
40 #include <netinet/in.h> 
41
42 /**
43  * Must match IP given in the server.
44  */
45 #define DUMMY_IP "1.2.3.4"
46
47 struct ip_packet 
48 {
49   uint8_t vers_ihl;
50   uint8_t tos;
51   uint16_t pkt_len;
52   uint16_t id;
53   uint16_t flags_frag_offset;
54   uint8_t ttl;
55   uint8_t proto;
56   uint16_t checksum;
57   uint32_t src_ip;
58   uint32_t dst_ip;
59 };
60
61 struct icmp_packet 
62 {
63   uint8_t type;
64   uint8_t code;
65   uint16_t checksum;
66   uint32_t reserved;
67 };
68  
69 static int rawsock;
70
71 static struct in_addr dummy;
72  
73 static struct in_addr target;
74
75
76 static uint16_t 
77 calc_checksum(const uint16_t *data, 
78               unsigned int bytes)
79 {
80   uint32_t sum;
81   unsigned int i;
82
83   sum = 0;
84   for (i=0;i<bytes/2;i++) 
85     sum += data[i];        
86   sum = (sum & 0xffff) + (sum >> 16);
87   sum = htons(0xffff - sum);
88   return sum;
89 }
90
91
92 static void
93 make_echo (const struct in_addr *src_ip,
94            struct icmp_packet *echo)
95 {
96   memset(echo, 0, sizeof(struct icmp_packet));
97   echo->type = ICMP_ECHO;
98   echo->code = 0;
99   echo->reserved = 0;
100   echo->checksum = 0;
101   echo->checksum = htons(calc_checksum((uint16_t*)echo, 
102                                        sizeof (struct icmp_packet)));
103 }
104
105
106 /**
107  * Send an ICMP message to the target.
108  *
109  * @param my_ip source address
110  * @param other target address
111  */
112 static void
113 send_icmp (const struct in_addr *my_ip,
114            const struct in_addr *other)
115 {
116   struct ip_packet ip_pkt;
117   struct icmp_packet *icmp_pkt;
118   struct icmp_packet icmp_echo;
119   struct sockaddr_in dst;
120   char packet[sizeof (struct ip_packet)*2 + sizeof (struct icmp_packet)*2];
121   size_t off;
122   int err;
123
124   /* ip header: send to (known) ip address */
125   off = 0;
126   memset(&ip_pkt, 0, sizeof(ip_pkt));
127   ip_pkt.vers_ihl = 0x45;
128   ip_pkt.tos = 0;
129   ip_pkt.pkt_len = sizeof (packet); /* huh? */
130   ip_pkt.id = 1; 
131   ip_pkt.flags_frag_offset = 0;
132   ip_pkt.ttl = IPDEFTTL;
133   ip_pkt.proto = IPPROTO_ICMP;
134   ip_pkt.checksum = 0; 
135   ip_pkt.src_ip = my_ip->s_addr;
136   ip_pkt.dst_ip = other->s_addr;
137   ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (struct ip_packet)));
138   memcpy (packet, &ip_pkt, sizeof (struct ip_packet));
139   off += sizeof (ip_pkt);
140   /* icmp reply: time exceeded */
141   icmp_pkt = (struct icmp_packet*) &packet[off];
142   memset(icmp_pkt, 0, sizeof(struct icmp_packet));
143   icmp_pkt->type = ICMP_TIME_EXCEEDED;
144   icmp_pkt->code = 0; 
145   icmp_pkt->reserved = 0;
146   icmp_pkt->checksum = 0;
147   off += sizeof (struct icmp_packet);
148
149   /* ip header of the presumably 'lost' udp packet */
150   ip_pkt.vers_ihl = 0x45;
151   ip_pkt.tos = 0;
152   ip_pkt.pkt_len = (sizeof (struct ip_packet) + sizeof (struct icmp_packet));
153   ip_pkt.id = 1; 
154   ip_pkt.flags_frag_offset = 0;
155   ip_pkt.ttl = 1; /* real TTL would be 1 on a time exceeded packet */
156   ip_pkt.proto = IPPROTO_ICMP;
157   ip_pkt.src_ip = other->s_addr;
158   ip_pkt.dst_ip = dummy.s_addr;
159   ip_pkt.checksum = 0;
160   ip_pkt.checksum = htons(calc_checksum((uint16_t*)&ip_pkt, sizeof (struct ip_packet)));  
161   memcpy (&packet[off], &ip_pkt, sizeof (struct ip_packet));
162   off += sizeof (struct ip_packet);
163   make_echo (other, &icmp_echo);
164   memcpy (&packet[off], &icmp_echo, sizeof(struct icmp_packet));
165   off += sizeof (struct icmp_packet);
166   icmp_pkt->checksum = htons(calc_checksum((uint16_t*)icmp_pkt, 
167                                            sizeof (struct icmp_packet)*2 + sizeof(struct ip_packet)));
168
169   memset (&dst, 0, sizeof (dst));
170   dst.sin_family = AF_INET;
171   dst.sin_addr = *other;
172   err = sendto(rawsock, 
173                packet, 
174                off, 0, 
175                (struct sockaddr*)&dst, 
176                sizeof(dst)); /* or sizeof 'struct sockaddr'? */
177   if (err < 0) 
178     {
179       fprintf(stderr,
180               "sendto failed: %s\n", strerror(errno));
181     }
182   else if (err != off) 
183     {
184       fprintf(stderr,
185               "Error: partial send of ICMP message\n");
186     }
187 }
188
189
190 static int
191 make_raw_socket ()
192 {
193   const int one = 1;
194   int ret;
195
196   ret = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
197   if (-1 == ret)
198     {
199       fprintf (stderr,
200                "Error opening RAW socket: %s\n",
201                strerror (errno));
202       return -1;
203     }  
204   if (setsockopt(ret, SOL_SOCKET, SO_BROADCAST,
205                  (char *)&one, sizeof(one)) == -1)
206     fprintf(stderr,
207             "setsockopt failed: %s\n",
208             strerror (errno));
209   if (setsockopt(ret, IPPROTO_IP, IP_HDRINCL,
210                  (char *)&one, sizeof(one)) == -1)
211     fprintf(stderr,
212             "setsockopt failed: %s\n",
213             strerror (errno));
214   return ret;
215 }
216
217
218 int
219 main (int argc, char *const *argv)
220 {
221   struct in_addr external;
222   uid_t uid;
223
224   if (-1 == (rawsock = make_raw_socket()))
225     return 1;     
226   uid = getuid ();
227   if (0 != setresuid (uid, uid, uid))
228     fprintf (stderr,
229              "Failed to setresuid: %s\n",
230              strerror (errno));
231   if (argc != 3)
232     {
233       fprintf (stderr,
234                "This program must be started with our IP and the targets external IP as arguments.\n");
235       return 1;
236     }
237   if ( (1 != inet_pton (AF_INET, argv[1], &external)) ||
238        (1 != inet_pton (AF_INET, argv[2], &target)) )
239     {
240       fprintf (stderr,
241                "Error parsing IPv4 address: %s\n",
242                strerror (errno));
243       return 1;
244     }
245   inet_pton (AF_INET, DUMMY_IP, &dummy);
246   send_icmp (&external,
247              &target);
248   close (rawsock);
249   return 0;
250 }
251
252 /* end of gnunet-nat-client.c */