1/* $Id: upnppinhole.c,v 1.7 2014/12/09 09:13:53 nanard Exp $ */
2/* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2012 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 <ctype.h>
19#include <unistd.h>
20
21#include "macros.h"
22#include "config.h"
23#include "upnpredirect.h"
24#include "upnpglobalvars.h"
25#include "upnpevents.h"
26#include "upnppinhole.h"
27#ifdef __APPLE__
28/* XXX - Apple version of PF API seems to differ from what
29 * pf/pfpinhole.c expects so don't use that at least.. */
30#ifdef USE_PF
31#undef USE_PF
32#endif /* USE_PF */
33#endif /* __APPLE__ */
34#if defined(USE_NETFILTER)
35#include "netfilter/iptpinhole.h"
36#endif
37#if defined(USE_PF)
38#include "pf/pfpinhole.h"
39#endif
40#if defined(USE_IPF)
41#endif
42#if defined(USE_IPFW)
43#endif
44
45#ifdef ENABLE_UPNPPINHOLE
46
47#if 0
48int
49upnp_check_outbound_pinhole(int proto, int * timeout)
50{
51	int s, tmptimeout, tmptime_out;
52	switch(proto)
53	{
54		case IPPROTO_UDP:
55			s = retrieve_timeout("udp_timeout", timeout);
56			return s;
57			break;
58		case IPPROTO_UDPLITE:
59			s = retrieve_timeout("udp_timeout_stream", timeout);
60			return s;
61			break;
62		case IPPROTO_TCP:
63			s = retrieve_timeout("tcp_timeout_established", timeout);
64			return s;
65			break;
66		case 65535:
67			s = retrieve_timeout("udp_timeout", timeout);
68			s = retrieve_timeout("udp_timeout_stream", &tmptimeout);
69			s = retrieve_timeout("tcp_timeout_established", &tmptime_out);
70			if(tmptimeout<tmptime_out)
71			{
72				if(tmptimeout<*timeout)
73					*timeout = tmptimeout;
74			}
75			else
76			{
77				if(tmptime_out<*timeout)
78					*timeout = tmptimeout;
79			}
80			return s;
81			break;
82		default:
83			return -5;
84			break;
85	}
86	return 0;
87}
88#endif
89
90/* upnp_add_inboundpinhole()
91 * returns:  1 on success
92 *          -1 Pinhole space exhausted
93 *          -4 invalid arguments
94 *         -42 not implemented
95 * TODO : return uid on success (positive) or error value (negative)
96 */
97int
98upnp_add_inboundpinhole(const char * raddr,
99                        unsigned short rport,
100                        const char * iaddr,
101                        unsigned short iport,
102                        int proto,
103                        char * desc,
104                        unsigned int leasetime,
105                        int * uid)
106{
107	int r;
108	time_t current;
109	unsigned int timestamp;
110	struct in6_addr address;
111
112	r = inet_pton(AF_INET6, iaddr, &address);
113	if(r <= 0) {
114		syslog(LOG_ERR, "inet_pton(%d, %s, %p) FAILED",
115		       AF_INET6, iaddr, &address);
116		return -4;
117	}
118	current = time(NULL);
119	timestamp = current + leasetime;
120	r = 0;
121
122#if 0
123	if(r == 1 && strcmp(iaddr, iaddr_old)==0 && iport==iport_old)
124	{
125		syslog(LOG_INFO, "Pinhole for inbound traffic from [%s]:%hu to [%s]:%hu with protocol %s already done. Updating it.", raddr, rport, iaddr_old, iport_old, protocol);
126		t = upnp_update_inboundpinhole(idfound, leaseTime);
127		*uid = atoi(idfound);
128		return t;
129	}
130	else
131#endif
132#if defined(USE_PF) || defined(USE_NETFILTER)
133	*uid = add_pinhole (0/*ext_if_name*/, raddr, rport,
134	                    iaddr, iport, proto, desc, timestamp);
135	return *uid >= 0 ? 1 : -1;
136#else
137	return -42;	/* not implemented */
138#endif
139}
140
141#if 0
142int
143upnp_add_inboundpinhole_internal(const char * raddr, unsigned short rport,
144                       const char * iaddr, unsigned short iport,
145                       const char * proto, int * uid)
146{
147	int c = 9999;
148	char cmd[256], cmd_raw[256], cuid[42];
149#if 0
150	static const char cmdval_full_udptcp[] = "ip6tables -I %s %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j ACCEPT";
151	static const char cmdval_udptcp[] = "ip6tables -I %s %d -p %s -i %s --sport %hu -d %s --dport %hu -j ACCEPT";
152	static const char cmdval_full_udplite[] = "ip6tables -I %s %d -p %s -i %s -s %s -d %s -j ACCEPT";
153	static const char cmdval_udplite[] = "ip6tables -I %s %d -p %s -i %s -d %s -j ACCEPT";
154	// raw table command
155	static const char cmdval_full_udptcp_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s --sport %hu -d %s --dport %hu -j TRACE";
156	static const char cmdval_udptcp_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s --sport %hu -d %s --dport %hu -j TRACE";
157	static const char cmdval_full_udplite_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -s %s -d %s -j TRACE";
158	static const char cmdval_udplite_raw[] = "ip6tables -t raw -I PREROUTING %d -p %s -i %s -d %s -j TRACE";
159#endif
160	/*printf("%s\n", raddr);*/
161	if(raddr!=NULL)
162	{
163#ifdef IPPROTO_UDPLITE
164		if(atoi(proto) == IPPROTO_UDPLITE)
165		{
166	/*		snprintf(cmd, sizeof(cmd), cmdval_full_udplite, miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, iaddr);
167			snprintf(cmd_raw, sizeof(cmd_raw), cmdval_full_udplite_raw, line_number, proto, ext_if_name, raddr, iaddr);*/
168		}
169		else
170#endif
171		{
172	/*		snprintf(cmd, sizeof(cmd), cmdval_full_udptcp, miniupnpd_forward_chain, line_number, proto, ext_if_name, raddr, rport, iaddr, iport);
173			snprintf(cmd_raw, sizeof(cmd_raw), cmdval_full_udptcp_raw, line_number, proto, ext_if_name, raddr, rport, iaddr, iport);*/
174		}
175	}
176	else
177	{
178#ifdef IPPROTO_UDPLITE
179		if(atoi(proto) == IPPROTO_UDPLITE)
180		{
181			/*snprintf(cmd, sizeof(cmd), cmdval_udplite, miniupnpd_forward_chain, line_number, proto, ext_if_name, iaddr);
182			snprintf(cmd_raw, sizeof(cmd_raw), cmdval_udplite_raw, line_number, proto, ext_if_name, iaddr);*/
183		}
184		else
185#endif
186		{
187			/*snprintf(cmd, sizeof(cmd), cmdval_udptcp, miniupnpd_forward_chain, line_number, proto, ext_if_name, rport, iaddr, iport);
188			snprintf(cmd_raw, sizeof(cmd_raw), cmdval_udptcp_raw, line_number, proto, ext_if_name, rport, iaddr, iport);
189*/
190		}
191	}
192#ifdef DEBUG
193	syslog(LOG_INFO, "Adding following ip6tables rule:");
194	syslog(LOG_INFO, "  -> %s", cmd);
195	syslog(LOG_INFO, "  -> %s", cmd_raw);
196#endif
197	/* TODO Add a better checking error.*/
198	if(system(cmd) < 0 || system(cmd_raw) < 0)
199	{
200		return 0;
201	}
202	srand(time(NULL));
203	snprintf(cuid, sizeof(cuid), "%.4d", rand()%c);
204	*uid = atoi(cuid);
205	printf("\t_add_ uid: %s\n", cuid);
206	return 1;
207}
208#endif
209
210/* upnp_get_pinhole_info()
211 * return values :
212 *   0   OK
213 *  -1   Internal error
214 *  -2   NOT FOUND (no such entry)
215 *  ..
216 *  -42  Not implemented
217 */
218int
219upnp_get_pinhole_info(unsigned short uid,
220                      char * raddr, int raddrlen,
221                      unsigned short * rport,
222                      char * iaddr, int iaddrlen,
223                      unsigned short * iport,
224                      int * proto, char * desc, int desclen,
225                      unsigned int * leasetime,
226                      unsigned int * packets)
227{
228	/* Call Firewall specific code to get IPv6 pinhole infos */
229#if defined(USE_PF) || defined(USE_NETFILTER)
230	int r;
231	unsigned int timestamp;
232	u_int64_t packets_tmp;
233	/*u_int64_t bytes_tmp;*/
234
235	r = get_pinhole_info(uid, raddr, raddrlen, rport,
236	                     iaddr, iaddrlen, iport,
237	                     proto, desc, desclen,
238	                     leasetime ? &timestamp : NULL,
239	                     packets ? &packets_tmp : NULL,
240	                     NULL/*&bytes_tmp*/);
241	if(r >= 0) {
242		if(leasetime) {
243			time_t current_time;
244			current_time = time(NULL);
245			if(timestamp > (unsigned int)current_time)
246				*leasetime = timestamp - current_time;
247			else
248				*leasetime = 0;
249		}
250		if(packets)
251			*packets = (unsigned int)packets_tmp;
252	}
253	return r;
254#else
255	UNUSED(uid);
256	UNUSED(raddr); UNUSED(raddrlen); UNUSED(rport);
257	UNUSED(iaddr); UNUSED(iaddrlen); UNUSED(iport);
258	UNUSED(proto); UNUSED(desc); UNUSED(desclen);
259	UNUSED(leasetime); UNUSED(packets);
260	return -42;	/* not implemented */
261#endif
262}
263
264int
265upnp_get_pinhole_uid_by_index(int index)
266{
267#if defined (USE_NETFILTER)
268	return get_pinhole_uid_by_index(index);
269#else
270	UNUSED(index);
271	return -42;
272#endif /* defined (USE_NETFILTER) */
273}
274
275int
276upnp_update_inboundpinhole(unsigned short uid, unsigned int leasetime)
277{
278#if defined(USE_PF) || defined(USE_NETFILTER)
279	unsigned int timestamp;
280
281	timestamp = time(NULL) + leasetime;
282	return update_pinhole(uid, timestamp);
283#else
284	UNUSED(uid); UNUSED(leasetime);
285
286	return -42; /* not implemented */
287#endif
288}
289
290int
291upnp_delete_inboundpinhole(unsigned short uid)
292{
293#if defined(USE_PF) || defined(USE_NETFILTER)
294	return delete_pinhole(uid);
295#else
296	UNUSED(uid);
297
298	return -1;
299#endif
300}
301
302#if 0
303/*
304 * Result:
305 * 	 1: Found Result
306 * 	-4: No result
307 * 	-5: Result in another table
308 * 	-6: Result in another chain
309 * 	-7: Result in a chain not a rule
310*/
311int
312upnp_check_pinhole_working(const char * uid,
313                           char * eaddr,
314                           char * iaddr,
315                           unsigned short * eport,
316                           unsigned short * iport,
317                           char * protocol,
318                           int * rulenum_used)
319{
320	/* TODO : to be implemented */
321#if 0
322	FILE * fd;
323	time_t expire = time(NULL);
324	char buf[1024], filename[] = "/var/log/kern.log", expire_time[32]="";
325	int res = -4, str_len;
326
327	str_len = strftime(expire_time, sizeof(expire_time), "%b %d %H:%M:%S", localtime(&expire));
328
329	fd = fopen(filename, "r");
330	if (fd==NULL)
331	{
332		syslog(LOG_ERR, "Get_rule: could not open file: %s", filename);
333		return -1;
334	}
335
336	syslog(LOG_INFO, "Get_rule: Starting getting info in file %s for %s\n", filename, uid);
337	buf[sizeof(buf)-1] = 0;
338	while(fgets(buf, sizeof(buf)-1, fd) != NULL && res != 1)
339	{
340		//printf("line: %s\n", buf);
341		char * r, * t, * c, * p;
342		// looking for something like filter:FORWARD:rule: or filter:MINIUPNPD:rule:
343		r = strstr(buf, ":rule:");
344		p = strstr(buf, ":policy:");
345		t = strstr(buf, "TRACE:"); // table pointeur
346		t += 7;
347		c = t + 7; // chain pointeur
348		if(r)
349		{
350			printf("\t** Found %.*s\n", 24 ,t);
351			char * src, * dst, * sport, * dport, * proto, * line;
352			char time[15]="", src_addr[40], dst_addr[40], proto_tmp[8];
353			int proto_int;
354			strncpy(time, buf, sizeof(time));
355			/*if(compare_time(time, expire_time)<0)
356			{
357				printf("\t\tNot corresponding time\n");
358				continue;
359			}*/
360
361			line = r + 6;
362			printf("\trule line = %d\n", atoi(line));
363
364			src = strstr(buf, "SRC=");
365			src += 4;
366			snprintf(src_addr, sizeof(src_addr), "%.*s", 39, src);
367#if 0
368			del_char(src_addr);
369			add_char(src_addr);
370#endif
371
372			dst = strstr(buf, "DST=");
373			dst += 4;
374			snprintf(dst_addr, sizeof(dst_addr), "%.*s", 39, dst);
375#if 0
376			del_char(dst_addr);
377			add_char(dst_addr);
378#endif
379
380			proto = strstr(buf, "PROTO=");
381			proto += 6;
382			proto_int = atoi(protocol);
383			if(proto_int == IPPROTO_UDP)
384				strcpy(proto_tmp, "UDP");
385			else if(proto_int == IPPROTO_TCP)
386				strcpy(proto_tmp, "TCP");
387#ifdef IPPROTO_UDPLITE
388			else if(proto_int == IPPROTO_UDPLITE)
389				strcpy(proto_tmp, "UDPLITE");
390#endif
391			else
392				strcpy(proto_tmp, "UnsupportedProto");
393
394	//		printf("\tCompare eaddr: %s // protocol: %s\n\t     to  addr: %s // protocol: %.*s\n", eaddr, proto_tmp, src_addr, strlen(proto_tmp), proto);
395	//		printf("\tCompare iaddr: %s // protocol: %s\n\t     to  addr: %s // protocol: %.*s\n", iaddr, proto_tmp, dst_addr, strlen(proto_tmp), proto);
396			// TODO Check time
397			// Check that the paquet found in trace correspond to the one we are looking for
398			if( /*(strcmp(eaddr, src_addr) == 0) &&*/ (strcmp(iaddr, dst_addr) == 0) && (strncmp(proto_tmp, proto, strlen(proto_tmp))==0))
399			{
400				sport = strstr(buf, "SPT=");
401				sport += 4;
402				dport = strstr(buf, "DPT=");
403				dport += 4;
404				printf("\tCompare eport: %hu\n\t     to   port: %d\n", *eport, atoi(sport));
405				printf("\tCompare iport: %hu\n\t     to   port: %d\n", *iport, atoi(dport));
406				if(/*eport != atoi(sport) &&*/ *iport != atoi(dport))
407				{
408					printf("\t\tPort not corresponding\n");
409					continue;
410				}
411				printf("\ttable found: %.*s\n", 6, t);
412				printf("\tchain found: %.*s\n", 9, c);
413				// Check that the table correspond to the filter table
414				if(strncmp(t, "filter", 6)==0)
415				{
416					// Check that the table correspond to the MINIUPNP table
417					if(strncmp(c, "MINIUPNPD", 9)==0)
418					{
419						*rulenum_used = atoi(line);
420						res = 1;
421					}
422					else
423					{
424						res = -6;
425						continue;
426					}
427				}
428				else
429				{
430					res = -5;
431					continue;
432				}
433			}
434			else
435			{
436				printf("Packet information not corresponding\n");
437				continue;
438			}
439		}
440		if(!r && p)
441		{
442			printf("\t** Policy case\n");
443			char * src, * dst, * sport, * dport, * proto, * line;
444			char time[15], src_addr[40], dst_addr[40], proto_tmp[8];
445			int proto_int;
446			strncpy(time, buf, sizeof(time));
447			/*if(compare_time(time, expire_time)<0)
448			{
449				printf("\t\tNot corresponding time\n");
450				continue;
451			}*/
452
453			line = p + 8;
454			printf("\trule line = %d\n", atoi(line));
455
456			src = strstr(buf, "SRC=");
457			src += 4;
458			snprintf(src_addr, sizeof(src_addr), "%.*s", 39, src);
459#if 0
460			del_char(src_addr);
461			add_char(src_addr);
462#endif
463
464			dst = strstr(buf, "DST=");
465			dst += 4;
466			snprintf(dst_addr, sizeof(dst_addr), "%.*s", 39, dst);
467#if 0
468			del_char(dst_addr);
469			add_char(dst_addr);
470#endif
471
472			proto = strstr(buf, "PROTO=");
473			proto += 6;
474			proto_int = atoi(protocol);
475			if(proto_int == IPPROTO_UDP)
476				strcpy(proto_tmp, "UDP");
477			else if(proto_int == IPPROTO_TCP)
478				strcpy(proto_tmp, "TCP");
479#ifdef IPPROTO_UDPLITE
480			else if(proto_int == IPPROTO_UDPLITE)
481				strcpy(proto_tmp, "UDPLITE");
482#endif
483			else
484				strcpy(proto_tmp, "UnsupportedProto");
485
486	//		printf("\tCompare eaddr: %s // protocol: %s\n\t     to  addr: %s // protocol: %.*s\n", eaddr, proto_tmp, src_addr, strlen(proto_tmp), proto);
487	//		printf("\tCompare iaddr: %s // protocol: %s\n\t     to  addr: %s // protocol: %.*s\n", iaddr, proto_tmp, dst_addr, strlen(proto_tmp), proto);
488			// Check that the paquet found in trace correspond to the one we are looking for
489			if( (strcmp(eaddr, src_addr) == 0) && (strcmp(iaddr, dst_addr) == 0) && (strncmp(proto_tmp, proto, 5)==0))
490			{
491				sport = strstr(buf, "SPT=");
492				sport += 4;
493				dport = strstr(buf, "DPT=");
494				dport += 4;
495				printf("\tCompare eport: %hu\n\t     to   port: %d\n", *eport, atoi(sport));
496				printf("\tCompare iport: %hu\n\t     to   port: %d\n", *iport, atoi(dport));
497				if(*eport != atoi(sport) && *iport != atoi(dport))
498				{
499					printf("\t\tPort not corresponding\n");
500					continue;
501				}
502				else
503				{
504					printf("Find a corresponding policy trace in the chain: %.*s\n", 10, c);
505					res = -7;
506					continue;
507				}
508			}
509			else
510				continue;
511		}
512	}
513	fclose(fd);
514	return res;
515#else
516	return -42;	/* to be implemented */
517#endif
518}
519#endif
520
521int
522upnp_clean_expired_pinholes(unsigned int * next_timestamp)
523{
524#if defined(USE_PF) || defined(USE_NETFILTER)
525	return clean_pinhole_list(next_timestamp);
526#else
527	UNUSED(next_timestamp);
528
529	return 0;	/* nothing to do */
530#endif
531}
532
533#endif /* ENABLE_UPNPPINHOLE */
534