OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / user / miniupnpd / upnpredirect.c
1 /* $Id: upnpredirect.c,v 1.1 2008-09-15 12:28:52 winfred Exp $ */
2 /* MiniUPnP project
3  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4  * (c) 2006-2007 Thomas Bernard 
5  * This software is subject to the conditions detailed
6  * in the LICENCE file provided within the distribution */
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <syslog.h>
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <netinet/in.h>
14 #include <net/if.h>
15 #include <arpa/inet.h>
16
17 #include <stdio.h>
18 #include <unistd.h>
19
20 #include "config.h"
21 #include "upnpredirect.h"
22 #include "upnpglobalvars.h"
23 #include "upnpevents.h"
24 #if defined(USE_NETFILTER)
25 #include "netfilter/iptcrdr.h"
26 #endif
27 #if defined(USE_PF)
28 #include "pf/obsdrdr.h"
29 #endif
30 #if defined(USE_IPF)
31 #include "ipf/ipfrdr.h"
32 #endif
33 #ifdef USE_MINIUPNPDCTL
34 #include <stdio.h>
35 #include <unistd.h>
36 #endif
37 #ifdef ENABLE_LEASEFILE
38 #include <sys/stat.h>
39 #endif
40
41 #ifdef ENABLE_LEASEFILE
42 static int lease_file_add( unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc)
43 {
44 FILE* fd;
45
46         if (lease_file == NULL) return 0;
47
48         fd = fopen( lease_file, "a");
49         if (fd==NULL) {
50                 syslog(LOG_ERR, "could not open lease file: %s", lease_file);
51                 return -1;
52         }
53
54         /* FIXME: what if desc contains ':'? */
55         fprintf( fd, "%s:%u:%s:%u:%s\n", ((proto==IPPROTO_TCP)?"TCP":"UDP"), eport, iaddr, iport, desc);
56         fclose(fd);
57         
58         return 0;
59 }
60
61 static int lease_file_remove( unsigned short eport, int proto)
62 {
63         FILE* fd, *fdt;
64         int tmp;
65         char buf[512];
66         char str[32];
67         char tmpfilename[128];
68         int str_size, buf_size;
69
70
71         if (lease_file == NULL) return 0;
72
73         if (strlen( lease_file) + 7 > sizeof(tmpfilename)) {
74                 syslog(LOG_ERR, "Lease filename is too long");
75                 return -1;
76         }
77
78         strncpy( tmpfilename, lease_file, sizeof(tmpfilename) );
79         strncat( tmpfilename, "XXXXXX", sizeof(tmpfilename) - strlen(tmpfilename));
80
81         fd = fopen( lease_file, "r");
82         if (fd==NULL) {
83                 return 0;
84         }
85
86         snprintf( str, sizeof(str), "%s:%u", ((proto==IPPROTO_TCP)?"TCP":"UDP"), eport);
87         str_size = strlen(str);
88
89         tmp = mkstemp(tmpfilename);
90         if (tmp==-1) {
91                 fclose(fd);
92                 syslog(LOG_ERR, "could not open temporary lease file");
93                 return -1;
94         }
95         fchmod(tmp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
96         fdt = fdopen(tmp, "a");
97
98         buf[sizeof(buf)-1] = 0;
99         while( fgets( buf, sizeof(buf)-1, fd) != NULL) {
100                 buf_size = strlen(buf);
101
102                 if (buf_size < str_size || strncmp( str, buf, str_size)!=0) {
103                         fwrite(buf, buf_size, 1, fdt);
104                 }
105         }
106         fclose(fdt);
107         fclose(fd);
108         
109         if (rename( tmpfilename, lease_file) < 0) {
110                 syslog(LOG_ERR, "could not rename temporary lease file to %s", lease_file);
111                 remove( tmpfilename);
112         }
113         
114         return 0;
115         
116 }
117 #endif
118
119 /* proto_atoi() 
120  * convert the string "UDP" or "TCP" to IPPROTO_UDP and IPPROTO_UDP */
121 static int
122 proto_atoi(const char * protocol)
123 {
124         int proto = IPPROTO_TCP;
125         if(strcmp(protocol, "UDP") == 0)
126                 proto = IPPROTO_UDP;
127         return proto;
128 }
129
130 /* upnp_redirect() 
131  * calls OS/fw dependant implementation of the redirection.
132  * protocol should be the string "TCP" or "UDP"
133  * returns: 0 on success
134  *          -1 failed to redirect
135  *          -2 already redirected
136  *          -3 permission check failed
137  */
138 int
139 upnp_redirect(unsigned short eport, 
140               const char * iaddr, unsigned short iport,
141               const char * protocol, const char * desc)
142 {
143         int proto, r;
144         char iaddr_old[32];
145         unsigned short iport_old;
146         struct in_addr address;
147         proto = proto_atoi(protocol);
148         if(inet_aton(iaddr, &address) < 0)
149         {
150                 syslog(LOG_ERR, "inet_aton(%s) : %m", iaddr);
151                 return -1;
152         }
153
154         if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm,
155                                                 eport, address, iport))
156         {
157                 syslog(LOG_INFO, "redirection permission check failed for "
158                                  "%hu->%s:%hu %s", eport, iaddr, iport, protocol);
159                 return -3;
160         }
161         r = get_redirect_rule(ext_if_name, eport, proto,
162                               iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, 0, 0);
163         if(r == 0)
164         {
165                 /* if existing redirect rule matches redirect request return success
166                  * xbox 360 does not keep track of the port it redirects and will
167                  * redirect another port when receiving ConflictInMappingEntry */
168                 if(strcmp(iaddr,iaddr_old)==0 && iport==iport_old)
169                 {
170                         syslog(LOG_INFO, "ignoring redirect request as it matches existing redirect");
171                 }
172                 else
173                 {
174
175                         syslog(LOG_INFO, "port %hu protocol %s already redirected to %s:%hu",
176                                 eport, protocol, iaddr_old, iport_old);
177                         return -2;
178                 }
179         }
180         else
181         {
182                 syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
183                         eport, iaddr, iport, protocol, desc);                   
184                 return upnp_redirect_internal(eport, iaddr, iport, proto, desc);
185 #if 0
186                 if(add_redirect_rule2(ext_if_name, eport, iaddr, iport, proto, desc) < 0)
187                 {
188                         return -1;
189                 }
190
191                 syslog(LOG_INFO, "creating pass rule to %s:%hu protocol %s for: %s",
192                         iaddr, iport, protocol, desc);
193                 if(add_filter_rule2(ext_if_name, iaddr, eport, iport, proto, desc) < 0)
194                 {
195                         /* clean up the redirect rule */
196 #if !defined(__linux__)
197                         delete_redirect_rule(ext_if_name, eport, proto);
198 #endif
199                         return -1;
200                 }
201 #endif
202         }
203
204         return 0;
205 }
206
207 int
208 upnp_redirect_internal(unsigned short eport,
209                        const char * iaddr, unsigned short iport,
210                        int proto, const char * desc)
211 {
212         /*syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
213                 eport, iaddr, iport, protocol, desc);                   */
214         if(add_redirect_rule2(ext_if_name, eport, iaddr, iport, proto, desc) < 0)
215         {
216                 return -1;
217         }
218
219 #ifdef ENABLE_LEASEFILE
220         lease_file_add( eport, iaddr, iport, proto, desc);
221 #endif
222 /*      syslog(LOG_INFO, "creating pass rule to %s:%hu protocol %s for: %s",
223                 iaddr, iport, protocol, desc);*/
224         if(add_filter_rule2(ext_if_name, iaddr, eport, iport, proto, desc) < 0)
225         {
226                 /* clean up the redirect rule */
227 #if !defined(__linux__)
228                 delete_redirect_rule(ext_if_name, eport, proto);
229 #endif
230                 return -1;
231         }
232 #ifdef ENABLE_EVENTS
233         upnp_event_var_change_notify(EWanIPC);
234 #endif
235         return 0;
236 }
237
238
239
240 int
241 upnp_get_redirection_infos(unsigned short eport, const char * protocol,
242                            unsigned short * iport,
243                            char * iaddr, int iaddrlen,
244                            char * desc, int desclen)
245 {
246         if(desc && (desclen > 0))
247                 desc[0] = '\0';
248         return get_redirect_rule(ext_if_name, eport, proto_atoi(protocol),
249                                  iaddr, iaddrlen, iport, desc, desclen, 0, 0);
250 }
251
252 int
253 upnp_get_redirection_infos_by_index(int index,
254                                     unsigned short * eport, char * protocol,
255                                     unsigned short * iport, 
256                                     char * iaddr, int iaddrlen,
257                                     char * desc, int desclen)
258 {
259         /*char ifname[IFNAMSIZ];*/
260         int proto = 0;
261
262         if(desc && (desclen > 0))
263                 desc[0] = '\0';
264         if(get_redirect_rule_by_index(index, 0/*ifname*/, eport, iaddr, iaddrlen,
265                                       iport, &proto, desc, desclen, 0, 0) < 0)
266                 return -1;
267         else
268         {
269                 if(proto == IPPROTO_TCP)
270                         memcpy(protocol, "TCP", 4);
271                 else
272                         memcpy(protocol, "UDP", 4);
273                 return 0;
274         }
275 }
276
277 int
278 _upnp_delete_redir(unsigned short eport, int proto)
279 {
280         int r;
281 #if defined(__linux__)
282         r = delete_redirect_and_filter_rules(eport, proto);
283 #else
284         r = delete_redirect_rule(ext_if_name, eport, proto);
285         delete_filter_rule(ext_if_name, eport, proto);
286 #endif
287 #ifdef ENABLE_LEASEFILE
288         lease_file_remove( eport, proto);
289 #endif
290
291 #ifdef ENABLE_EVENTS
292         upnp_event_var_change_notify(EWanIPC);
293 #endif
294         return r;
295 }
296
297 int
298 upnp_delete_redirection(unsigned short eport, const char * protocol)
299 {
300         syslog(LOG_INFO, "removing redirect rule port %hu %s", eport, protocol);
301         return _upnp_delete_redir(eport, proto_atoi(protocol));
302 }
303
304 /* upnp_get_portmapping_number_of_entries()
305  * TODO: improve this code */
306 int
307 upnp_get_portmapping_number_of_entries()
308 {
309         int n = 0, r = 0;
310         unsigned short eport, iport;
311         char protocol[4], iaddr[32], desc[64];
312         do {
313                 protocol[0] = '\0'; iaddr[0] = '\0'; desc[0] = '\0';
314                 r = upnp_get_redirection_infos_by_index(n, &eport, protocol, &iport,
315                                                         iaddr, sizeof(iaddr),
316                                                         desc, sizeof(desc) );
317                 n++;
318         } while(r==0);
319         return (n-1);
320 }
321
322 /* functions used to remove unused rules */
323 struct rule_state *
324 get_upnp_rules_state_list(int max_rules_number_target)
325 {
326         char ifname[IFNAMSIZ];
327         int proto;
328         unsigned short iport;
329         struct rule_state * tmp;
330         struct rule_state * list = 0;
331         int i = 0;
332         tmp = malloc(sizeof(struct rule_state));
333         if(!tmp)
334                 return 0;
335         while(get_redirect_rule_by_index(i, ifname, &tmp->eport, 0, 0,
336                                       &iport, &proto, 0, 0,
337                                                                   &tmp->packets, &tmp->bytes) >= 0)
338         {
339                 tmp->proto = (short)proto;
340                 /* add tmp to list */
341                 tmp->next = list;
342                 list = tmp;
343                 /* prepare next iteration */
344                 i++;
345                 tmp = malloc(sizeof(struct rule_state));
346                 if(!tmp)
347                         break;
348         }
349         free(tmp);
350         /* return empty list if not enough redirections */
351         if(i<=max_rules_number_target)
352                 while(list)
353                 {
354                         tmp = list;
355                         list = tmp->next;
356                         free(tmp);
357                 }
358         /* return list */
359         return list;
360 }
361
362 void
363 remove_unused_rules(struct rule_state * list)
364 {
365         char ifname[IFNAMSIZ];
366         unsigned short iport;
367         struct rule_state * tmp;
368         u_int64_t packets;
369         u_int64_t bytes;
370         int n = 0;
371         while(list)
372         {
373                 /* remove the rule if no traffic has used it */
374                 if(get_redirect_rule(ifname, list->eport, list->proto,
375                                  0, 0, &iport, 0, 0, &packets, &bytes) >= 0)
376                 {
377                         if(packets == list->packets && bytes == list->bytes)
378                         {
379                                 _upnp_delete_redir(list->eport, list->proto);
380                                 n++;
381                         }
382                 }
383                 tmp = list;
384                 list = tmp->next;
385                 free(tmp);
386         }
387         if(n>0)
388                 syslog(LOG_NOTICE, "removed %d unused rules", n);
389 }
390
391
392 /* stuff for miniupnpdctl */
393 #ifdef USE_MINIUPNPDCTL
394 void
395 write_ruleset_details(int s)
396 {
397         char ifname[IFNAMSIZ];
398         int proto = 0;
399         unsigned short eport, iport;
400         char desc[64];
401         char iaddr[32];
402         u_int64_t packets;
403         u_int64_t bytes;
404         int i = 0;
405         char buffer[256];
406         int n;
407         while(get_redirect_rule_by_index(i, ifname, &eport, iaddr, sizeof(iaddr),
408                                          &iport, &proto, desc, sizeof(desc),
409                                          &packets, &bytes) >= 0)
410         {
411                 n = snprintf(buffer, sizeof(buffer), "%2d %s %s %hu->%s:%hu "
412                                                      "'%s' %llu %llu\n",
413                              i, ifname, proto==IPPROTO_TCP?"TCP":"UDP",
414                              eport, iaddr, iport, desc, packets, bytes);
415                 write(s, buffer, n);
416                 i++;
417         }
418 }
419 #endif
420