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), &timestamp,
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 	                      &timestamp, 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, &timestamp,
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, &timestamp,
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, &timestamp,
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, &timestamp,
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, &timestamp,
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 	                                 &timestamp,
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