Import udp-broadcast-relay-0.3.tar.gz
[udp-broadcast-relay.git] / main.c
1 /*
2 ******************************************************************
3 udp-broadcast-relay
4         Relays UDP broadcasts to other networks, forging
5         the sender address.
6
7 Copyright (c) 2003 Joachim Breitner <mail@joachim-breitner.de>
8
9 Based upon:
10 udp_broadcast_fw ; Forwards UDP broadcast packets to all local 
11         interfaces as though they originated from sender
12         
13 Copyright (C) 2002  Nathan O'Sullivan
14
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License
17 as published by the Free Software Foundation; either version 2
18 of the License, or (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 GNU General Public License for more details.
24 ******************************************************************
25
26 Thanks:
27
28 Arny <cs6171@scitsc.wlv.ac.uk> 
29 - public domain UDP spoofing code
30 http://www.netfor2.com/ip.htm
31 - IP/UDP packet formatting info
32
33 */
34
35 #define MAXIFS  256
36 #define DPRINT  if (debug) printf
37 #define IPHEADER_LEN 20
38 #define UDPHEADER_LEN 8
39 #define HEADER_LEN (IPHEADER_LEN + UDPHEADER_LEN)
40 #define TTL_ID_OFFSET 64
41  
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <netinet/in_systm.h>
45 #include <netinet/in.h>
46 #include <netinet/ip.h>
47 #include <netinet/udp.h>
48 #include <errno.h>
49 #include <string.h>
50 #include <arpa/inet.h>
51 #include <stdio.h>
52 #include <linux/if.h>
53 #include <sys/ioctl.h>
54
55 main(int argc,char **argv)
56 {
57         /* Debugging, forking, other settings */
58         int debug, forking;
59         
60         u_int16_t port;
61         u_char id;
62         u_char ttl;
63
64         /* We use two sockets - one for receiving broadcast packets (type UDP), and
65            one for spoofing them (type RAW) */
66         int fd,rcv;
67
68         /* Structure holds info on local interfaces */
69         struct ifreq reqbuf;
70         int maxifs;
71                 
72         /* list of addresses and interface numbers on local machine */
73         // struct sockaddr_in addr[MAXIFS];
74         struct {
75                 struct sockaddr_in dstaddr;
76                 int ifindex;
77         } ifs[MAXIFS];
78         
79         /* Address broadcast packet was sent from */
80         struct sockaddr_in rcv_addr;
81         
82         /* Incoming message read via rcvsmsg */
83         struct msghdr rcv_msg;
84         struct iovec iov;
85         u_char pkt_infos[16384];
86
87         /* various variables */
88         int x=1, len;
89         
90         /* Where we forge our packets */
91         u_char gram[4096]=
92         {
93                 0x45,   0x00,   0x00,   0x26,
94                 0x12,   0x34,   0x00,   0x00,
95                 0xFF,   0x11,   0,      0,
96                 0,      0,      0,      0,
97                 0,      0,      0,      0,
98                 0,      0,      0,      0,
99                 0x00,   0x12,   0x00,   0x00,
100                 '1','2','3','4','5','6','7','8','9','0'
101         };
102
103         struct cmsghdr *cmsg;
104         int *ttlptr=NULL;
105         int rcv_ifindex;
106
107         iov.iov_base = gram+ HEADER_LEN; 
108         iov.iov_len = 4006 - HEADER_LEN - 1;
109         
110         rcv_msg.msg_name = &rcv_addr;
111         rcv_msg.msg_namelen = sizeof(rcv_addr);
112         rcv_msg.msg_iov = &iov;
113         rcv_msg.msg_iovlen = 1;
114         rcv_msg.msg_control = pkt_infos;
115         rcv_msg.msg_controllen = sizeof(pkt_infos);
116         
117         /* parsing the args */
118         if(argc < 5)
119         {
120                 fprintf(stderr,"usage: %s [-d] [-f] id udp-port dev1 dev2 ...\n",*argv);
121                 fprintf(stderr,"This program listens for broadcast packets on the specified udp-port\n"
122                         "and then forwards them to each other given interface.  Packets are sent\n"
123                         "such that they appear to have come from the original broadcaster. When using\n"
124                         "multiple instances for the same port on the same network, they must have the\n"
125                         "a different id.\n"
126                         "-d enables Debugging, -f forces forking to background\n");
127                 exit(1);
128         };
129         
130         if (debug = (strcmp(argv[1],"-d") == 0))
131         {
132                 argc--;
133                 argv++;
134                 DPRINT ("Debugging Mode enabled\n");
135         };
136         
137         if (forking = (strcmp(argv[1],"-f") == 0))
138         {
139                 argc--;
140                 argv++;
141                 DPRINT ("Forking Mode enabled (while debuggin? useless..)\n");
142         };
143
144         if ((id = atoi(argv[1])) == 0)
145         {
146                 fprintf (stderr,"ID argument not valid\n");
147                 exit(1);
148         }
149         argc--;
150         argv++;
151
152         if (id < 1 || id > 99)
153         {
154                 fprintf (stderr,"ID argument %i not between 1 and 99\n",id);
155                 exit(1);
156         }
157         ttl = id+TTL_ID_OFFSET;
158         gram[8] = ttl; 
159         /* The is is used to detect packets we just sent, and is stored in the "ttl" field,
160          * which is not used with broadcast packets. Beware when using this with
161          * non-broadcast-packets */
162         
163         if ((port = atoi(argv[1])) == 0)
164         {
165                 fprintf (stderr,"Port argument not valid\n");
166                 exit(1);
167         }
168         argc--;
169         argv++;
170         
171         DPRINT ("ID: %i (ttl: %i), Port %i\n",id,ttl,port);
172
173
174         /* We need to find out what IP's are bound to this host - set up a temporary socket to do so */
175         if((fd=socket(AF_INET,SOCK_RAW,IPPROTO_RAW)) < 0)
176         {
177                 perror("socket");
178                 fprintf(stderr,"You must be root to create a raw socket\n");
179                 exit(1);
180         };
181
182         /* For each interface on the command line */
183         for (maxifs=0;argc>1;argc--,argv++)
184         {
185                 int ioctl_request; 
186
187                 strncpy(reqbuf.ifr_name,argv[1],IFNAMSIZ);
188
189                 /* Request index for this interface */
190                 if (ioctl(fd,SIOCGIFINDEX, &reqbuf) < 0) {
191                         perror("ioctl(SIOCGIFINDEX)");
192                         exit(1);
193                 }
194                 
195                 /* Save the index for later use */      
196                 ifs[maxifs].ifindex = reqbuf.ifr_ifindex;
197                 
198                 /* Request flags for this interface */
199                 if (ioctl(fd,SIOCGIFFLAGS, &reqbuf) < 0) {
200                         perror("ioctl(SIOCGIFFLAGS)");
201                         exit(1);
202                 }
203
204                 /* if the interface is not up or a loopback, ignore it */
205                 if ((reqbuf.ifr_flags & IFF_UP) == 0 ||
206                     (reqbuf.ifr_flags & IFF_LOOPBACK) )
207                         continue;
208
209                 /* find the address type we need */
210                 if (reqbuf.ifr_flags & IFF_BROADCAST)
211                         ioctl_request = SIOCGIFBRDADDR;
212                 else 
213                         ioctl_request = SIOCGIFDSTADDR;
214                                 
215
216                 /* Request the broadcast/destination address for this interface */
217                 if (ioctl(fd,ioctl_request, &reqbuf) < 0) {
218                         perror("ioctl(SIOCGIFBRDADDR)");
219                         exit(1);
220                 }
221
222                 /* Save the address for later use */
223                 bcopy(  (struct sockaddr_in *)&reqbuf.ifr_addr,
224                         &ifs[maxifs].dstaddr,
225                         sizeof(struct sockaddr_in) );
226
227                 DPRINT("%s: %i / %s\n",
228                         reqbuf.ifr_name,
229                         ifs[maxifs].ifindex,
230                         inet_ntoa(ifs[maxifs].dstaddr.sin_addr) );
231
232                 /* ... and count it */
233                 maxifs++;
234         }
235         /* well, we want the max index, actually */
236         maxifs--;
237         DPRINT("found %i interfaces total\n",maxifs+1);
238         
239         /* Free our allocated buffer and close the socket */
240         close(fd);
241
242         /* Create our broadcast receiving socket */
243         if((rcv=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)) < 0)
244         {
245                 perror("socket");
246                 exit(1);
247         };
248
249         x = 1;
250         if(setsockopt(rcv, SOL_SOCKET, SO_BROADCAST, (char*) &x, sizeof(int))<0){
251                 perror("SO_BROADCAST on rcv");
252                 exit(1);
253         };
254         if(setsockopt(rcv, SOL_IP, IP_RECVTTL, (char*) &x, sizeof(int))<0){
255                 perror("IP_RECVTTL on rcv");
256                 exit(1);
257         };
258         if(setsockopt(rcv, SOL_IP, IP_PKTINFO, (char*) &x, sizeof(int))<0){
259                 perror("IP_PKTINFO on rcv");
260                 exit(1);
261         };
262
263         /* We bind it to broadcast addr on the given port */
264         rcv_addr.sin_family = AF_INET;
265         rcv_addr.sin_port = htons(port);
266         rcv_addr.sin_addr.s_addr = INADDR_ANY;
267
268         if ( bind (rcv, (struct sockaddr *)&rcv_addr, sizeof(struct sockaddr_in) ) < 0 )
269         {
270                 perror("bind");
271                 fprintf(stderr,"A program is already bound to the broadcast address for the given port\n");
272                 exit(1);
273         }
274         
275         /* Set up a raw socket for sending our packets through */
276         if((fd=socket(AF_INET,SOCK_RAW,IPPROTO_RAW)) < 0)
277         {
278                 perror("socket");
279                 exit(1);
280         };
281
282         /* Set dest port to that was provided on command line */
283         *(u_short*)(gram+22)=(u_short)htons(port);
284         
285         x=1;
286         if (setsockopt(fd,SOL_SOCKET,SO_BROADCAST,(char*)&x,sizeof(x))<0)
287         {
288                 perror("setsockopt SO_BROADCAST");
289                 exit(1);
290         };
291
292         /* Enable IP header stuff on the raw socket */
293         #ifdef IP_HDRINCL
294         x=1;
295         if (setsockopt(fd,IPPROTO_IP,IP_HDRINCL,(char*)&x,sizeof(x))<0)
296         {
297                 perror("setsockopt IP_HDRINCL");
298                 exit(1);
299         };
300         #else
301         #error IP_HDRINCL support is required
302         #endif
303
304         /* Fork to background */
305   if (! debug) {
306     if (forking && fork())
307       exit(0);
308
309     fclose(stdin);
310     fclose(stdout);
311     fclose(stderr);
312   }
313
314   DPRINT("Done Initializing\n\n");
315
316         for (;;) /* endless loop */
317         {
318                 /* Receive a broadcast packet */
319                 len = recvmsg(rcv,&rcv_msg,0);
320                 if (len <= 0) continue; /* ignore broken packets */
321
322                 /* Find the ttl and the reveicing interface */
323                 ttlptr=NULL;
324                 if (rcv_msg.msg_controllen>0)
325                   for (cmsg=CMSG_FIRSTHDR(&rcv_msg);cmsg;cmsg=CMSG_NXTHDR(&rcv_msg,cmsg)) {
326                     if (cmsg->cmsg_type==IP_TTL) {
327                       ttlptr = (int *)CMSG_DATA(cmsg);
328                     }
329                     if (cmsg->cmsg_type==IP_PKTINFO) {
330                       rcv_ifindex=((struct in_pktinfo *)CMSG_DATA(cmsg))->ipi_ifindex;
331                     }
332                   }
333
334                 if (ttlptr == NULL) {
335                         perror("TTL not found on incoming packet\n");
336                         exit(1);
337                 }
338                 if (*ttlptr == ttl) {
339                         DPRINT ("Got local packge (TTL %i) on interface %i\n",*ttlptr,rcv_ifindex);
340                         continue;
341                 }
342                 
343
344                 gram[HEADER_LEN + len] =0;
345                 DPRINT("Got remote package:\n");
346                 // DPRINT("Content:\t%s\n",gram+HEADER_LEN);
347                 DPRINT("TTL:\t\t%i\n",*ttlptr);
348                 DPRINT("Interface:\t%i\n",rcv_ifindex);
349                 DPRINT("From:\t\t%s:%d\n",inet_ntoa(rcv_addr.sin_addr),rcv_addr.sin_port);
350         
351                 /* copy sender's details into our datagram as the source addr */        
352                 bcopy(&(rcv_addr.sin_addr.s_addr),(gram+12),4);
353                 *(u_short*)(gram+20)=(u_short)rcv_addr.sin_port;
354
355                 /* set the length of the packet */
356                 *(u_short*)(gram+24)=htons(8 + len);
357                 *(u_short*)(gram+2)=htons(28+len);
358
359                 /* Iterate through our interfaces and send packet to each one */
360                 for (x=0;x<=maxifs;x++)
361                 {
362                         if (ifs[x].ifindex == rcv_ifindex) continue; /* no bounces, please */
363
364                         /* Set destination addr ip - port is set already*/
365                         bcopy(&(ifs[x].dstaddr.sin_addr.s_addr),(gram+16),4);   
366
367                         DPRINT ("Sent to %s:%d on interface %i\n",
368                                 inet_ntoa(ifs[x].dstaddr.sin_addr), /* dst ip */
369                                 ntohs(*(u_short*)(gram+22)), /* dst port */
370                                 ifs[x].ifindex); /* interface number */
371                                 
372                         /* Send the packet */
373                         if (sendto(fd,
374                                         &gram,
375                                         28+len,0,
376                                         (struct sockaddr*)&ifs[x].dstaddr,sizeof(struct sockaddr)
377                                 ) < 0)
378                                 perror("sendto");
379                 }
380                 DPRINT ("\n");
381         }
382 }