1 /* $Id: upnpredirect.c,v 1.97 2020/06/06 17:55:24 nanard Exp $ */
2 /* vim: tabstop=4 shiftwidth=4 noexpandtab
3 * MiniUPnP project
4 * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
5 * (c) 2006-2020 Thomas Bernard
6 * This software is subject to the conditions detailed
7 * in the LICENCE file provided within the distribution */
8
9 #include <stdlib.h>
10 #include <string.h>
11 #include <syslog.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <net/if.h>
16 #include <arpa/inet.h>
17
18 #include <stdio.h>
19 #include <ctype.h>
20 #include <unistd.h>
21
22 #include "macros.h"
23 #include "config.h"
24 #include "upnpredirect.h"
25 #include "upnpglobalvars.h"
26 #include "upnpevents.h"
27 #include "portinuse.h"
28 #include "upnputils.h"
29 #if defined(USE_NETFILTER)
30 #include "netfilter/iptcrdr.h"
31 #endif
32 #if defined(USE_PF)
33 #include "pf/obsdrdr.h"
34 #endif
35 #if defined(USE_IPF)
36 #include "ipf/ipfrdr.h"
37 #endif
38 #if defined(USE_IPFW)
39 #include "ipfw/ipfwrdr.h"
40 #endif
41 #ifdef USE_MINIUPNPDCTL
42 #include <stdio.h>
43 #include <unistd.h>
44 #endif
45 #ifdef ENABLE_LEASEFILE
46 #include <sys/stat.h>
47 #endif
48
49 /* from <inttypes.h> */
50 #ifndef PRIu64
51 #define PRIu64 "llu"
52 #endif
53
54 /* proto_atoi()
55 * convert the string "UDP" or "TCP" to IPPROTO_UDP and IPPROTO_UDP */
56 static int
proto_atoi(const char * protocol)57 proto_atoi(const char * protocol)
58 {
59 int proto = IPPROTO_TCP;
60 if(strcasecmp(protocol, "UDP") == 0)
61 proto = IPPROTO_UDP;
62 #ifdef IPPROTO_UDPLITE
63 else if(strcasecmp(protocol, "UDPLITE") == 0)
64 proto = IPPROTO_UDPLITE;
65 #endif /* IPPROTO_UDPLITE */
66 return proto;
67 }
68
69 /* proto_itoa()
70 * convert IPPROTO_UDP, IPPROTO_UDP, etc. to "UDP", "TCP" */
71 static const char *
proto_itoa(int proto)72 proto_itoa(int proto)
73 {
74 const char * protocol;
75 switch(proto) {
76 case IPPROTO_UDP:
77 protocol = "UDP";
78 break;
79 case IPPROTO_TCP:
80 protocol = "TCP";
81 break;
82 #ifdef IPPROTO_UDPLITE
83 case IPPROTO_UDPLITE:
84 protocol = "UDPLITE";
85 break;
86 #endif /* IPPROTO_UDPLITE */
87 default:
88 protocol = "*UNKNOWN*";
89 }
90 return protocol;
91 }
92
93 #ifdef ENABLE_LEASEFILE
94 static int
lease_file_add(unsigned short eport,const char * iaddr,unsigned short iport,int proto,const char * desc,unsigned int timestamp)95 lease_file_add(unsigned short eport,
96 const char * iaddr,
97 unsigned short iport,
98 int proto,
99 const char * desc,
100 unsigned int timestamp)
101 {
102 FILE * fd;
103
104 if (lease_file == NULL) return 0;
105
106 fd = fopen( lease_file, "a");
107 if (fd==NULL) {
108 syslog(LOG_ERR, "could not open lease file: %s", lease_file);
109 return -1;
110 }
111
112 /* convert our time to unix time
113 * if LEASEFILE_USE_REMAINING_TIME is defined, only the remaining time is stored */
114 if (timestamp != 0) {
115 timestamp -= upnp_time();
116 #ifndef LEASEFILE_USE_REMAINING_TIME
117 timestamp += time(NULL);
118 #endif
119 }
120
121 fprintf(fd, "%s:%hu:%s:%hu:%u:%s\n",
122 proto_itoa(proto), eport, iaddr, iport,
123 timestamp, desc);
124 fclose(fd);
125
126 return 0;
127 }
128
129 static int
lease_file_remove(unsigned short eport,int proto)130 lease_file_remove(unsigned short eport, int proto)
131 {
132 FILE* fd, *fdt;
133 int tmp;
134 char buf[512];
135 char str[32];
136 char tmpfilename[128];
137 int str_size, buf_size;
138
139
140 if (lease_file == NULL) return 0;
141
142 if (strlen( lease_file) + 7 > sizeof(tmpfilename)) {
143 syslog(LOG_ERR, "Lease filename is too long");
144 return -1;
145 }
146
147 snprintf( tmpfilename, sizeof(tmpfilename), "%sXXXXXX", lease_file);
148
149 fd = fopen( lease_file, "r");
150 if (fd==NULL) {
151 return 0;
152 }
153
154 snprintf( str, sizeof(str), "%s:%u", proto_itoa(proto), eport);
155 str_size = strlen(str);
156
157 tmp = mkstemp(tmpfilename);
158 if (tmp==-1) {
159 fclose(fd);
160 syslog(LOG_ERR, "could not open temporary lease file");
161 return -1;
162 }
163 fchmod(tmp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
164 fdt = fdopen(tmp, "a");
165
166 buf[sizeof(buf)-1] = 0;
167 while( fgets(buf, sizeof(buf)-1, fd) != NULL) {
168 buf_size = strlen(buf);
169
170 if (buf_size < str_size || strncmp(str, buf, str_size)!=0) {
171 fwrite(buf, buf_size, 1, fdt);
172 }
173 }
174 fclose(fdt);
175 fclose(fd);
176
177 if (rename(tmpfilename, lease_file) < 0) {
178 syslog(LOG_ERR, "could not rename temporary lease file to %s", lease_file);
179 remove(tmpfilename);
180 }
181
182 return 0;
183
184 }
185
186 /* reload_from_lease_file()
187 * read lease_file and add the rules contained
188 */
reload_from_lease_file()189 int reload_from_lease_file()
190 {
191 FILE * fd;
192 char * p;
193 unsigned short eport, iport;
194 char * proto;
195 char * iaddr;
196 char * desc;
197 char * rhost;
198 unsigned int leaseduration;
199 unsigned int timestamp;
200 time_t current_time;
201 #ifndef LEASEFILE_USE_REMAINING_TIME
202 time_t current_unix_time;
203 #endif
204 char line[128];
205 int r;
206
207 if(!lease_file) return -1;
208 fd = fopen( lease_file, "r");
209 if (fd==NULL) {
210 syslog(LOG_ERR, "could not open lease file: %s", lease_file);
211 return -1;
212 }
213 if(unlink(lease_file) < 0) {
214 syslog(LOG_WARNING, "could not unlink file %s : %m", lease_file);
215 }
216
217 current_time = upnp_time();
218 #ifndef LEASEFILE_USE_REMAINING_TIME
219 current_unix_time = time(NULL);
220 #endif
221 while(fgets(line, sizeof(line), fd)) {
222 syslog(LOG_DEBUG, "parsing lease file line '%s'", line);
223 proto = line;
224 p = strchr(line, ':');
225 if(!p) {
226 syslog(LOG_ERR, "unrecognized data in lease file");
227 continue;
228 }
229 *(p++) = '\0';
230 iaddr = strchr(p, ':');
231 if(!iaddr) {
232 syslog(LOG_ERR, "unrecognized data in lease file");
233 continue;
234 }
235 *(iaddr++) = '\0';
236 eport = (unsigned short)atoi(p);
237 p = strchr(iaddr, ':');
238 if(!p) {
239 syslog(LOG_ERR, "unrecognized data in lease file");
240 continue;
241 }
242 *(p++) = '\0';
243 iport = (unsigned short)atoi(p);
244 p = strchr(p, ':');
245 if(!p) {
246 syslog(LOG_ERR, "unrecognized data in lease file");
247 continue;
248 }
249 *(p++) = '\0';
250 desc = strchr(p, ':');
251 if(!desc) {
252 syslog(LOG_ERR, "unrecognized data in lease file");
253 continue;
254 }
255 *(desc++) = '\0';
256 /*timestamp = (unsigned int)atoi(p);*/
257 timestamp = (unsigned int)strtoul(p, NULL, 10);
258 /* trim description */
259 while(isspace(*desc))
260 desc++;
261 p = desc;
262 while(*(p+1))
263 p++;
264 while(isspace(*p) && (p > desc))
265 *(p--) = '\0';
266
267 if(timestamp > 0) {
268 #ifdef LEASEFILE_USE_REMAINING_TIME
269 leaseduration = timestamp;
270 timestamp += current_time; /* convert to our time */
271 #else
272 if(timestamp <= (unsigned int)current_unix_time) {
273 syslog(LOG_NOTICE, "already expired lease in lease file");
274 continue;
275 } else {
276 leaseduration = timestamp - current_unix_time;
277 timestamp = leaseduration + current_time; /* convert to our time */
278 }
279 #endif
280 } else {
281 leaseduration = 0; /* default value */
282 }
283 rhost = NULL;
284 r = upnp_redirect(rhost, eport, iaddr, iport, proto, desc, leaseduration);
285 if(r == -1) {
286 syslog(LOG_ERR, "Failed to redirect %hu -> %s:%hu protocol %s",
287 eport, iaddr, iport, proto);
288 } else if(r == -2) {
289 /* Add the redirection again to the lease file */
290 lease_file_add(eport, iaddr, iport, proto_atoi(proto),
291 desc, timestamp);
292 }
293 }
294 fclose(fd);
295
296 return 0;
297 }
298
299 #ifdef LEASEFILE_USE_REMAINING_TIME
lease_file_rewrite(void)300 void lease_file_rewrite(void)
301 {
302 int index;
303 unsigned short eport, iport;
304 int proto;
305 char iaddr[32];
306 char desc[64];
307 char rhost[40];
308 unsigned int timestamp;
309
310 if (lease_file == NULL) return;
311 remove(lease_file);
312 for(index = 0; ; index++) {
313 if(get_redirect_rule_by_index(index, 0/*ifname*/, &eport, iaddr, sizeof(iaddr),
314 &iport, &proto, desc, sizeof(desc),
315 rhost, sizeof(rhost), ×tamp,
316 0, 0) < 0)
317 break;
318 if(lease_file_add(eport, iaddr, iport, proto, desc, timestamp) < 0)
319 break;
320 }
321 }
322 #endif
323 #endif
324
325 /* upnp_redirect()
326 * calls OS/fw dependent implementation of the redirection.
327 * protocol should be the string "TCP" or "UDP"
328 * returns: 0 on success
329 * -1 failed to redirect
330 * -2 already redirected
331 * -3 permission check failed
332 * -4 already redirected (other mechanism)
333 */
334 int
upnp_redirect(const char * rhost,unsigned short eport,const char * iaddr,unsigned short iport,const char * protocol,const char * desc,unsigned int leaseduration)335 upnp_redirect(const char * rhost, unsigned short eport,
336 const char * iaddr, unsigned short iport,
337 const char * protocol, const char * desc,
338 unsigned int leaseduration)
339 {
340 int proto, r;
341 char iaddr_old[32];
342 char rhost_old[32];
343 unsigned short iport_old;
344 struct in_addr address;
345 unsigned int timestamp;
346
347 proto = proto_atoi(protocol);
348 if(inet_aton(iaddr, &address) <= 0) {
349 syslog(LOG_ERR, "inet_aton(%s) FAILED", iaddr);
350 return -1;
351 }
352
353 if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm,
354 eport, address, iport)) {
355 syslog(LOG_INFO, "redirection permission check failed for "
356 "%hu->%s:%hu %s", eport, iaddr, iport, protocol);
357 return -3;
358 }
359
360 if (desc == NULL)
361 desc = ""; /* assume empty description */
362
363 /* IGDv1 (WANIPConnection:1 Service Template Version 1.01 / Nov 12, 2001)
364 * - 2.2.20.PortMappingDescription :
365 * Overwriting Previous / Existing Port Mappings:
366 * If the RemoteHost, ExternalPort, PortMappingProtocol and InternalClient
367 * are exactly the same as an existing mapping, the existing mapping values
368 * for InternalPort, PortMappingDescription, PortMappingEnabled and
369 * PortMappingLeaseDuration are overwritten.
370 * Rejecting a New Port Mapping:
371 * In cases where the RemoteHost, ExternalPort and PortMappingProtocol
372 * are the same as an existing mapping, but the InternalClient is
373 * different, the action is rejected with an appropriate error.
374 * Add or Reject New Port Mapping behavior based on vendor implementation:
375 * In cases where the ExternalPort, PortMappingProtocol and InternalClient
376 * are the same, but RemoteHost is different, the vendor can choose to
377 * support both mappings simultaneously, or reject the second mapping
378 * with an appropriate error.
379 *
380 * - 2.4.16.AddPortMapping
381 * This action creates a new port mapping or overwrites an existing
382 * mapping with the same internal client. If the ExternalPort and
383 * PortMappingProtocol pair is already mapped to another internal client,
384 * an error is returned.
385 *
386 * IGDv2 (WANIPConnection:2 Service Standardized DCP (SDCP) Sep 10, 2010)
387 * Protocol ExternalPort RemoteHost InternalClient Result
388 * = = ≠ ≠ Failure
389 * = = ≠ = Failure or success
390 * (vendor specific)
391 * = = = ≠ Failure
392 * = = = = Success (overwrite)
393 */
394 rhost_old[0] = '\0';
395 r = get_redirect_rule(ext_if_name, eport, proto,
396 iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0,
397 rhost_old, sizeof(rhost_old),
398 ×tamp, 0, 0);
399 if(r == 0) {
400 if(strcmp(iaddr, iaddr_old)==0 &&
401 ((rhost == NULL && rhost_old[0]=='\0') ||
402 (rhost && (strcmp(rhost, "*") == 0) && rhost_old[0]=='\0') ||
403 (rhost && (strcmp(rhost, rhost_old) == 0)))) {
404 syslog(LOG_INFO, "updating existing port mapping %hu %s (rhost '%s') => %s:%hu",
405 eport, protocol, rhost_old, iaddr_old, iport_old);
406 timestamp = (leaseduration > 0) ? upnp_time() + leaseduration : 0;
407 if(iport != iport_old) {
408 r = update_portmapping(ext_if_name, eport, proto, iport, desc, timestamp);
409 } else {
410 r = update_portmapping_desc_timestamp(ext_if_name, eport, proto, desc, timestamp);
411 }
412 #ifdef ENABLE_LEASEFILE
413 if(r == 0) {
414 lease_file_remove(eport, proto);
415 lease_file_add(eport, iaddr, iport, proto, desc, timestamp);
416 }
417 #endif /* ENABLE_LEASEFILE */
418 return r;
419 } else {
420 syslog(LOG_INFO, "port %hu %s (rhost '%s') already redirected to %s:%hu",
421 eport, protocol, rhost_old, iaddr_old, iport_old);
422 return -2;
423 }
424 #ifdef CHECK_PORTINUSE
425 } else if (port_in_use(ext_if_name, eport, proto, iaddr, iport) > 0) {
426 syslog(LOG_INFO, "port %hu protocol %s already in use",
427 eport, protocol);
428 return -4;
429 #endif /* CHECK_PORTINUSE */
430 } else {
431 timestamp = (leaseduration > 0) ? upnp_time() + leaseduration : 0;
432 syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
433 eport, iaddr, iport, protocol, desc);
434 return upnp_redirect_internal(rhost, eport, iaddr, iport, proto,
435 desc, timestamp);
436 }
437 }
438
439 int
upnp_redirect_internal(const char * rhost,unsigned short eport,const char * iaddr,unsigned short iport,int proto,const char * desc,unsigned int timestamp)440 upnp_redirect_internal(const char * rhost, unsigned short eport,
441 const char * iaddr, unsigned short iport,
442 int proto, const char * desc,
443 unsigned int timestamp)
444 {
445 /*syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
446 eport, iaddr, iport, protocol, desc); */
447 if(disable_port_forwarding)
448 return -1;
449 if(add_redirect_rule2(ext_if_name, rhost, eport, iaddr, iport, proto,
450 desc, timestamp) < 0) {
451 return -1;
452 }
453
454 #ifdef ENABLE_LEASEFILE
455 lease_file_add( eport, iaddr, iport, proto, desc, timestamp);
456 #endif
457 /* syslog(LOG_INFO, "creating pass rule to %s:%hu protocol %s for: %s",
458 iaddr, iport, protocol, desc);*/
459 if(add_filter_rule2(ext_if_name, rhost, iaddr, eport, iport, proto, desc) < 0) {
460 /* clean up the redirect rule */
461 #if !defined(__linux__)
462 delete_redirect_rule(ext_if_name, eport, proto);
463 #endif
464 return -1;
465 }
466 if(timestamp > 0) {
467 if(!nextruletoclean_timestamp || (timestamp < nextruletoclean_timestamp))
468 nextruletoclean_timestamp = timestamp;
469 }
470 #ifdef ENABLE_EVENTS
471 /* the number of port mappings changed, we must
472 * inform the subscribers */
473 upnp_event_var_change_notify(EWanIPC);
474 #endif
475 return 0;
476 }
477
478
479
480 /* Firewall independent code which call the FW dependent code. */
481 int
upnp_get_redirection_infos(unsigned short eport,const char * protocol,unsigned short * iport,char * iaddr,int iaddrlen,char * desc,int desclen,char * rhost,int rhostlen,unsigned int * leaseduration)482 upnp_get_redirection_infos(unsigned short eport, const char * protocol,
483 unsigned short * iport,
484 char * iaddr, int iaddrlen,
485 char * desc, int desclen,
486 char * rhost, int rhostlen,
487 unsigned int * leaseduration)
488 {
489 int r;
490 unsigned int timestamp;
491 time_t current_time;
492
493 if(desc && (desclen > 0))
494 desc[0] = '\0';
495 if(rhost && (rhostlen > 0))
496 rhost[0] = '\0';
497 r = get_redirect_rule(ext_if_name, eport, proto_atoi(protocol),
498 iaddr, iaddrlen, iport, desc, desclen,
499 rhost, rhostlen, ×tamp,
500 0, 0);
501 if(r == 0 &&
502 timestamp > 0 &&
503 timestamp > (unsigned int)(current_time = upnp_time())) {
504 *leaseduration = timestamp - current_time;
505 } else {
506 *leaseduration = 0;
507 }
508 return r;
509 }
510
511 int
upnp_get_redirection_infos_by_index(int index,unsigned short * eport,char * protocol,unsigned short * iport,char * iaddr,int iaddrlen,char * desc,int desclen,char * rhost,int rhostlen,unsigned int * leaseduration)512 upnp_get_redirection_infos_by_index(int index,
513 unsigned short * eport, char * protocol,
514 unsigned short * iport,
515 char * iaddr, int iaddrlen,
516 char * desc, int desclen,
517 char * rhost, int rhostlen,
518 unsigned int * leaseduration)
519 {
520 /*char ifname[IFNAMSIZ];*/
521 int proto = 0;
522 unsigned int timestamp;
523 time_t current_time;
524
525 if(desc && (desclen > 0))
526 desc[0] = '\0';
527 if(rhost && (rhostlen > 0))
528 rhost[0] = '\0';
529 if(get_redirect_rule_by_index(index, 0/*ifname*/, eport, iaddr, iaddrlen,
530 iport, &proto, desc, desclen,
531 rhost, rhostlen, ×tamp,
532 0, 0) < 0)
533 return -1;
534 else
535 {
536 current_time = upnp_time();
537 *leaseduration = (timestamp > (unsigned int)current_time)
538 ? (timestamp - current_time)
539 : 0;
540 if(proto == IPPROTO_TCP)
541 memcpy(protocol, "TCP", 4);
542 #ifdef IPPROTO_UDPLITE
543 else if(proto == IPPROTO_UDPLITE)
544 memcpy(protocol, "UDPLITE", 8);
545 #endif /* IPPROTO_UDPLITE */
546 else
547 memcpy(protocol, "UDP", 4);
548 return 0;
549 }
550 }
551
552 /* called from natpmp.c too */
553 int
_upnp_delete_redir(unsigned short eport,int proto)554 _upnp_delete_redir(unsigned short eport, int proto)
555 {
556 int r;
557 #if defined(__linux__)
558 r = delete_redirect_and_filter_rules(eport, proto);
559 #elif defined(USE_PF)
560 r = delete_redirect_and_filter_rules(ext_if_name, eport, proto);
561 #else
562 r = delete_redirect_rule(ext_if_name, eport, proto);
563 delete_filter_rule(ext_if_name, eport, proto);
564 #endif
565 #ifdef ENABLE_LEASEFILE
566 lease_file_remove( eport, proto);
567 #endif
568
569 #ifdef ENABLE_EVENTS
570 upnp_event_var_change_notify(EWanIPC);
571 #endif
572 return r;
573 }
574
575 int
upnp_delete_redirection(unsigned short eport,const char * protocol)576 upnp_delete_redirection(unsigned short eport, const char * protocol)
577 {
578 syslog(LOG_INFO, "removing redirect rule port %hu %s", eport, protocol);
579 return _upnp_delete_redir(eport, proto_atoi(protocol));
580 }
581
582 /* upnp_get_portmapping_number_of_entries() */
583 int
upnp_get_portmapping_number_of_entries()584 upnp_get_portmapping_number_of_entries()
585 {
586 #if defined(USE_PF) || defined(USE_NFTABLES)
587 return get_redirect_rule_count(ext_if_name);
588 #else
589 int n = 0, r = 0;
590 unsigned short eport, iport;
591 char protocol[8], iaddr[32], desc[64], rhost[32];
592 unsigned int leaseduration;
593 do {
594 protocol[0] = '\0'; iaddr[0] = '\0'; desc[0] = '\0';
595 r = upnp_get_redirection_infos_by_index(n, &eport, protocol, &iport,
596 iaddr, sizeof(iaddr),
597 desc, sizeof(desc),
598 rhost, sizeof(rhost),
599 &leaseduration);
600 n++;
601 } while(r==0);
602 return (n-1);
603 #endif
604 }
605
606 /* functions used to remove unused rules
607 * As a side effect, delete expired rules (based on LeaseDuration) */
608 struct rule_state *
get_upnp_rules_state_list(int max_rules_number_target)609 get_upnp_rules_state_list(int max_rules_number_target)
610 {
611 /*char ifname[IFNAMSIZ];*/
612 int proto;
613 unsigned short iport;
614 unsigned int timestamp;
615 struct rule_state * tmp;
616 struct rule_state * list = 0;
617 struct rule_state * * p;
618 int i = 0;
619 time_t current_time;
620
621 /*ifname[0] = '\0';*/
622 tmp = malloc(sizeof(struct rule_state));
623 if(!tmp)
624 return 0;
625 current_time = upnp_time();
626 nextruletoclean_timestamp = 0;
627 while(get_redirect_rule_by_index(i, /*ifname*/0, &tmp->eport, 0, 0,
628 &iport, &proto, 0, 0, 0,0, ×tamp,
629 &tmp->packets, &tmp->bytes) >= 0)
630 {
631 tmp->to_remove = 0;
632 if(timestamp > 0) {
633 /* need to remove this port mapping ? */
634 if(timestamp <= (unsigned int)current_time)
635 tmp->to_remove = 1;
636 else if((nextruletoclean_timestamp <= (unsigned int)current_time)
637 || (timestamp < nextruletoclean_timestamp))
638 nextruletoclean_timestamp = timestamp;
639 }
640 tmp->proto = (short)proto;
641 /* add tmp to list */
642 tmp->next = list;
643 list = tmp;
644 /* prepare next iteration */
645 i++;
646 tmp = malloc(sizeof(struct rule_state));
647 if(!tmp)
648 break;
649 }
650 #ifdef PCP_PEER
651 i=0;
652 while(get_peer_rule_by_index(i, /*ifname*/0, &tmp->eport, 0, 0,
653 &iport, &proto, 0, 0, 0,0,0, ×tamp,
654 &tmp->packets, &tmp->bytes) >= 0)
655 {
656 tmp->to_remove = 0;
657 if(timestamp > 0) {
658 /* need to remove this port mapping ? */
659 if(timestamp <= (unsigned int)current_time)
660 tmp->to_remove = 1;
661 else if((nextruletoclean_timestamp <= (unsigned int)current_time)
662 || (timestamp < nextruletoclean_timestamp))
663 nextruletoclean_timestamp = timestamp;
664 }
665 tmp->proto = (short)proto;
666 /* add tmp to list */
667 tmp->next = list;
668 list = tmp;
669 /* prepare next iteration */
670 i++;
671 tmp = malloc(sizeof(struct rule_state));
672 if(!tmp)
673 break;
674 }
675 #endif
676 free(tmp);
677 /* remove the redirections that need to be removed */
678 for(p = &list, tmp = list; tmp; tmp = *p)
679 {
680 if(tmp->to_remove)
681 {
682 syslog(LOG_NOTICE, "remove port mapping %hu %s because it has expired",
683 tmp->eport, proto_itoa(tmp->proto));
684 _upnp_delete_redir(tmp->eport, tmp->proto);
685 *p = tmp->next;
686 free(tmp);
687 i--;
688 } else {
689 p = &(tmp->next);
690 }
691 }
692 /* return empty list if not enough redirections */
693 if(i<=max_rules_number_target)
694 while(list)
695 {
696 tmp = list;
697 list = tmp->next;
698 free(tmp);
699 }
700 /* return list */
701 return list;
702 }
703
704 void
remove_unused_rules(struct rule_state * list)705 remove_unused_rules(struct rule_state * list)
706 {
707 char ifname[IFNAMSIZ];
708 unsigned short iport;
709 struct rule_state * tmp;
710 u_int64_t packets;
711 u_int64_t bytes;
712 unsigned int timestamp;
713 int n = 0;
714
715 while(list)
716 {
717 /* remove the rule if no traffic has used it */
718 if(get_redirect_rule(ifname, list->eport, list->proto,
719 0, 0, &iport, 0, 0, 0, 0, ×tamp,
720 &packets, &bytes) >= 0)
721 {
722 if(packets == list->packets && bytes == list->bytes)
723 {
724 syslog(LOG_DEBUG, "removing unused mapping %hu %s : still "
725 "%" PRIu64 "packets %" PRIu64 "bytes",
726 list->eport, proto_itoa(list->proto),
727 packets, bytes);
728 _upnp_delete_redir(list->eport, list->proto);
729 n++;
730 }
731 }
732 tmp = list;
733 list = tmp->next;
734 free(tmp);
735 }
736 if(n>0)
737 syslog(LOG_NOTICE, "removed %d unused rules", n);
738 }
739
740 /* upnp_get_portmappings_in_range()
741 * return a list of all "external" ports for which a port
742 * mapping exists */
743 unsigned short *
upnp_get_portmappings_in_range(unsigned short startport,unsigned short endport,const char * protocol,unsigned int * number)744 upnp_get_portmappings_in_range(unsigned short startport,
745 unsigned short endport,
746 const char * protocol,
747 unsigned int * number)
748 {
749 int proto;
750 proto = proto_atoi(protocol);
751 if(!number)
752 return NULL;
753 return get_portmappings_in_range(startport, endport, proto, number);
754 }
755
756 /* stuff for miniupnpdctl */
757 #ifdef USE_MINIUPNPDCTL
758 void
write_ruleset_details(int s)759 write_ruleset_details(int s)
760 {
761 int proto = 0;
762 unsigned short eport, iport;
763 char desc[64];
764 char iaddr[32];
765 char rhost[32];
766 unsigned int timestamp;
767 u_int64_t packets;
768 u_int64_t bytes;
769 int i = 0;
770 char buffer[256];
771 int n;
772
773 write(s, "Ruleset :\n", 10);
774 while(get_redirect_rule_by_index(i, 0/*ifname*/, &eport, iaddr, sizeof(iaddr),
775 &iport, &proto, desc, sizeof(desc),
776 rhost, sizeof(rhost),
777 ×tamp,
778 &packets, &bytes) >= 0)
779 {
780 n = snprintf(buffer, sizeof(buffer),
781 "%2d %s %s:%hu->%s:%hu "
782 "'%s' %u %" PRIu64 " %" PRIu64 "\n",
783 /*"'%s' %llu %llu\n",*/
784 i, proto_itoa(proto), rhost,
785 eport, iaddr, iport, desc, timestamp, packets, bytes);
786 write(s, buffer, n);
787 i++;
788 }
789 }
790 #endif
791