Пример #1
0
static int
SendSSDPbyebye(int s, const struct sockaddr * dest, socklen_t destlen,
               const char * dest_str,
               const char * nt, const char * suffix,
               const char * usn1, const char * usn2, const char * usn3)
{
	int n, l;
	char bufr[SSDP_PACKET_MAX_LEN];

	l = snprintf(bufr, sizeof(bufr),
	             "NOTIFY * HTTP/1.1\r\n"
	             "HOST: %s:%d\r\n"
	             "NT: %s%s\r\n"
	             "USN: %s%s%s%s\r\n"
	             "NTS: ssdp:byebye\r\n"
	             "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
	             "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
	             "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
	             "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
	             "\r\n",
	             dest_str, SSDP_PORT,		/* HOST : */
	             nt, suffix,				/* NT: */
	             usn1, usn2, usn3, suffix,	/* USN: */
	             upnp_bootid, upnp_bootid, upnp_configid);
	if(l<0)
	{
		syslog(LOG_ERR, "%s: snprintf error", "SendSSDPbyebye()");
		return -1;
	}
	else if((unsigned int)l >= sizeof(bufr))
	{
		syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
		       "SendSSDPbyebye()", (unsigned)l, (unsigned)sizeof(bufr));
		l = sizeof(bufr) - 1;
	}
	n = sendto_or_schedule(s, bufr, l, 0, dest, destlen);
	if(n < 0)
	{
		syslog(LOG_ERR, "sendto(udp_shutdown=%d): %m", s);
		return -1;
	}
	else if(n != l)
	{
		syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l);
		return -1;
	}
	return 0;
}
Пример #2
0
static void
SendSSDPNotify(int s, const struct sockaddr * dest, socklen_t dest_len,
               const char * dest_str,
               const char * host, unsigned short http_port,
#ifdef ENABLE_HTTPS
               unsigned short https_port,
#endif
               const char * nt, const char * suffix,
               const char * usn1, const char * usn2, const char * usn3,
               unsigned int lifetime)
{
	char bufr[SSDP_PACKET_MAX_LEN];
	int n, l;

	l = snprintf(bufr, sizeof(bufr),
		"NOTIFY * HTTP/1.1\r\n"
		"HOST: %s:%d\r\n"
		"CACHE-CONTROL: max-age=%u\r\n"
		"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
#ifdef ENABLE_HTTPS
		"SECURELOCATION.UPNP.ORG: https://%s:%u" ROOTDESC_PATH "\r\n"
#endif
		"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
		"NT: %s%s\r\n"
		"USN: %s%s%s%s\r\n"
		"NTS: ssdp:alive\r\n"
		"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
		"01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
		"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
		"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
		"\r\n",
		dest_str, SSDP_PORT,			/* HOST: */
		lifetime,						/* CACHE-CONTROL: */
		host, (unsigned int)http_port,	/* LOCATION: */
#ifdef ENABLE_HTTPS
		host, (unsigned int)https_port,	/* SECURE-LOCATION: */
#endif
		nt, suffix,						/* NT: */
		usn1, usn2, usn3, suffix,		/* USN: */
		upnp_bootid,					/* 01-NLS: */
		upnp_bootid,					/* BOOTID.UPNP.ORG: */
		upnp_configid );				/* CONFIGID.UPNP.ORG: */
	if(l<0)
	{
		syslog(LOG_ERR, "%s: snprintf error", "SendSSDPNotify()");
		return;
	}
	else if((unsigned int)l >= sizeof(bufr))
	{
		syslog(LOG_WARNING, "%s: truncated output (%u>=%u)",
		       "SendSSDPNotify()", (unsigned)l, (unsigned)sizeof(bufr));
		l = sizeof(bufr) - 1;
	}
	n = sendto_or_schedule(s, bufr, l, 0, dest, dest_len);
	if(n < 0)
	{
		syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
		       host ? host : "NULL");
	}
	else if(n != l)
	{
		syslog(LOG_NOTICE, "sendto() sent %d out of %d bytes", n, l);
	}
	/* Due to the unreliable nature of UDP, devices SHOULD send the entire
	 * set of discovery messages more than once with some delay between
	 * sets e.g. a few hundred milliseconds. To avoid network congestion
	 * discovery messages SHOULD NOT be sent more than three times. */
	n = sendto_schedule(s, bufr, l, 0, dest, dest_len, 250);
	if(n < 0)
	{
		syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
		       host ? host : "NULL");
	}
}
Пример #3
0
/* SendNATPMPPublicAddressChangeNotification()
 * should be called when the public IP address changed */
void SendNATPMPPublicAddressChangeNotification(int * sockets, int n_sockets)
{
	struct sockaddr_in sockname;
	unsigned char notif[12];
	int j, n;

	notif[0] = 0;	/* vers */
	notif[1] = 128;	/* OP code */
	notif[2] = 0;	/* result code */
	notif[3] = 0;	/* result code */
	/* seconds since "start of epoch" :
	 * time elapsed since the port mapping table was initialized on
	 * startup or reset for any other reason */
	WRITENU32(notif+4, time(NULL) - startup_time);
#ifndef MULTIPLE_EXTERNAL_IP
	FillPublicAddressResponse(notif, 0);
	if(notif[3])
	{
		syslog(LOG_WARNING, "%s: cannot get public IP address, stopping",
		       "SendNATPMPPublicAddressChangeNotification");
		return;
	}
#endif
	memset(&sockname, 0, sizeof(struct sockaddr_in));
    sockname.sin_family = AF_INET;
    sockname.sin_addr.s_addr = inet_addr(NATPMP_NOTIF_ADDR);

	for(j=0; j<n_sockets; j++)
	{
		if(sockets[j] < 0)
			continue;
#ifdef MULTIPLE_EXTERNAL_IP
		{
			struct lan_addr_s * lan_addr = lan_addrs.lh_first;
			int i;
			for(i=0; i<j; i++)
				lan_addr = lan_addr->list.le_next;
			FillPublicAddressResponse(notif, lan_addr->addr.s_addr);
		}
#endif
		/* Port to use in 2006 version of the NAT-PMP specification */
    	sockname.sin_port = htons(NATPMP_PORT);
		n = sendto_or_schedule(sockets[j], notif, 12, 0,
		           (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
		if(n < 0)
		{
			syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m",
			       "SendNATPMPPublicAddressChangeNotification", sockets[j]);
			return;
		}
		/* Port to use in 2008 version of the NAT-PMP specification */
    	sockname.sin_port = htons(NATPMP_NOTIF_PORT);
		n = sendto_or_schedule(sockets[j], notif, 12, 0,
		           (struct sockaddr *)&sockname, sizeof(struct sockaddr_in));
		if(n < 0)
		{
			syslog(LOG_ERR, "%s: sendto(s_udp=%d): %m",
			       "SendNATPMPPublicAddressChangeNotification", sockets[j]);
			return;
		}
	}
}
Пример #4
0
/** read the request from the socket, process it and then send the
 * response back.
 */
void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len,
		struct sockaddr_in *senderaddr)
{
	unsigned char *req=msg_buff;	/* request udp packet */
	unsigned char resp[32];	/* response udp packet */
	int resplen;
	int n = len;
	char senderaddrstr[16];

	if(!inet_ntop(AF_INET, &senderaddr->sin_addr,
			senderaddrstr, sizeof(senderaddrstr))) {
		syslog(LOG_ERR, "inet_ntop(natpmp): %m");
	}

	syslog(LOG_INFO, "NAT-PMP request received from %s:%hu %dbytes",
	       senderaddrstr, ntohs(senderaddr->sin_port), n);

	if(n<2 || ((((req[1]-1)&~1)==0) && n<12)) {
		syslog(LOG_WARNING, "discarding NAT-PMP request (too short) %dBytes",
		       n);
		return;
	}
	if(req[1] & 128) {
		/* discarding NAT-PMP responses silently */
		return;
	}
	memset(resp, 0, sizeof(resp));
	resplen = 8;
	resp[1] = 128 + req[1];	/* response OPCODE is request OPCODE + 128 */
	/* setting response TIME STAMP :
	 * time elapsed since its port mapping table was initialized on
	 * startup or reset for any other reason */
	WRITENU32(resp+4, time(NULL) - startup_time);
	if(req[0] > 0) {
		/* invalid version */
		syslog(LOG_WARNING, "unsupported NAT-PMP version : %u",
		       (unsigned)req[0]);
		resp[3] = 1;	/* unsupported version */
	} else switch(req[1]) {
	case 0:	/* Public address request */
		syslog(LOG_INFO, "NAT-PMP public address request");
		FillPublicAddressResponse(resp, senderaddr->sin_addr.s_addr);
		resplen = 12;
		break;
	case 1:	/* UDP port mapping request */
	case 2:	/* TCP port mapping request */
		{
			unsigned short iport;	/* private port */
			unsigned short eport;	/* public port */
			uint32_t lifetime; 		/* lifetime=0 => remove port mapping */
			int r;
			int proto;
			char iaddr_old[16];
			unsigned short iport_old;
			unsigned int timestamp;

			iport = READNU16(req+4);
			eport = READNU16(req+6);
			lifetime = READNU32(req+8);
			proto = (req[1]==1)?IPPROTO_UDP:IPPROTO_TCP;
			syslog(LOG_INFO, "NAT-PMP port mapping request : "
			                 "%hu->%s:%hu %s lifetime=%us",
			                 eport, senderaddrstr, iport,
			                 (req[1]==1)?"udp":"tcp", lifetime);
			/* TODO: accept port mapping if iport ok but eport not ok
			 * (and set eport correctly) */
			if(lifetime == 0) {
				/* remove the mapping */
				/* RFC6886 :
				 * A client MAY also send an explicit packet to request deletion of a
				 * mapping that is no longer needed. A client requests explicit
				 * deletion of a mapping by sending a message to the NAT gateway
				 * requesting the mapping, with the Requested Lifetime in Seconds set to
				 * zero. The Suggested External Port MUST be set to zero by the client
				 * on sending, and MUST be ignored by the gateway on reception. */
				int index = 0;
				unsigned short eport2, iport2;
				char iaddr2[16];
				int proto2;
				char desc[64];
				eport = 0; /* to indicate correct removing of port mapping */
				while(get_redirect_rule_by_index(index, 0,
				          &eport2, iaddr2, sizeof(iaddr2),
						  &iport2, &proto2,
						  desc, sizeof(desc),
				          0, 0, &timestamp, 0, 0) >= 0) {
					syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'",
					       index, proto2, eport2, iaddr2, iport2, desc);
					if(0 == strcmp(iaddr2, senderaddrstr)
					  && 0 == memcmp(desc, "NAT-PMP", 7)) {
						/* (iport == 0) => remove all the mappings for this client */
						if((iport == 0) || ((iport == iport2) && (proto == proto2))) {
							r = _upnp_delete_redir(eport2, proto2);
							if(r < 0) {
								syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s",
								       eport2, (proto2==IPPROTO_TCP)?"TCP":"UDP");
								resp[3] = 2;	/* Not Authorized/Refused */
								break;
							} else {
								syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed",
								       proto2==IPPROTO_TCP?"TCP":"UDP", eport2);
								index--;
							}
						}
					}
					index++;
				}
			} else if(iport==0) {
				resp[3] = 2;	/* Not Authorized/Refused */
			} else { /* iport > 0 && lifetime > 0 */
				unsigned short eport_first = 0;
				int any_eport_allowed = 0;
				char desc[64];
				if(eport==0)	/* if no suggested external port, use same a internal port */
					eport = iport;
				while(resp[3] == 0) {
					if(eport_first == 0) { /* first time in loop */
						eport_first = eport;
					} else if(eport == eport_first) { /* no eport available */
						if(any_eport_allowed == 0) { /* all eports rejected by permissions */
							syslog(LOG_ERR, "No allowed eport for NAT-PMP %hu %s->%s:%hu",
							       eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport);
							resp[3] = 2;	/* Not Authorized/Refused */
						} else { /* at least one eport allowed (but none available) */
							syslog(LOG_ERR, "Failed to find available eport for NAT-PMP %hu %s->%s:%hu",
							       eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport);
							resp[3] = 4;	/* Out of resources */
						}
						break;
					}
					if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr->sin_addr, iport)) {
						eport++;
						if(eport == 0) eport++; /* skip port zero */
						continue;
					}
					any_eport_allowed = 1;	/* at lease one eport is allowed */
#ifdef CHECK_PORTINUSE
					if (port_in_use(ext_if_name, eport, proto, senderaddrstr, iport) > 0) {
						syslog(LOG_INFO, "port %hu protocol %s already in use",
						       eport, (proto==IPPROTO_TCP)?"tcp":"udp");
						eport++;
						if(eport == 0) eport++; /* skip port zero */
						continue;
					}
#endif
					r = get_redirect_rule(ext_if_name, eport, proto,
					                      iaddr_old, sizeof(iaddr_old),
					                      &iport_old, 0, 0, 0, 0,
					                      &timestamp, 0, 0);
					if(r==0) {
						if(strcmp(senderaddrstr, iaddr_old)==0
						    && iport==iport_old) {
							/* redirection already existing */
							syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing",
							       eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old);
							/* remove and then add again */
							if(_upnp_delete_redir(eport, proto) < 0) {
								syslog(LOG_ERR, "failed to remove port mapping");
								break;
							}
						} else {
							eport++;
							if(eport == 0) eport++; /* skip port zero */
							continue;
						}
					}
					/* do the redirection */
#if 0
					timestamp = (unsigned)(time(NULL) - startup_time)
					                      + lifetime;
					snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp);
#else
					timestamp = time(NULL) + lifetime;
					snprintf(desc, sizeof(desc), "NAT-PMP %hu %s",
					         eport, (proto==IPPROTO_TCP)?"tcp":"udp");
#endif
					/* TODO : check return code */
					if(upnp_redirect_internal(NULL, eport, senderaddrstr,
					                          iport, proto, desc,
					                          timestamp) < 0) {
						syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'",
						       eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc);
						resp[3] = 3;  /* Failure */
					}
					break;
				}
			}
			WRITENU16(resp+8, iport);	/* private port */
			WRITENU16(resp+10, eport);	/* public port */
			WRITENU32(resp+12, lifetime);	/* Port Mapping lifetime */
		}
		resplen = 16;
		break;
	default:
		resp[3] = 5;	/* Unsupported OPCODE */
	}
	n = sendto_or_schedule(s, resp, resplen, 0,
	           (struct sockaddr *)senderaddr, sizeof(*senderaddr));
	if(n<0) {
		syslog(LOG_ERR, "sendto(natpmp): %m");
	} else if(n<resplen) {
		syslog(LOG_ERR, "sendto(natpmp): sent only %d bytes out of %d",
		       n, resplen);
	}
}
Пример #5
0
int test(void)
{
	int s;
	ssize_t n;
	int i;
	struct sockaddr_in addr;
	struct sockaddr_in dest_addr;
	struct timeval next_send;
	if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
		syslog(LOG_ERR, "socket(): %m");
		return 1;
	}
	set_non_blocking(s);
	memset(&addr, 0, sizeof(struct sockaddr_in));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		syslog(LOG_ERR, "bind(): %m");
		close(s);
		return 1;
	}
	memset(&dest_addr, 0, sizeof(struct sockaddr_in));
	dest_addr.sin_family = AF_INET;
	dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);
	dest_addr.sin_port =  htons(DEST_PORT);
	n = sendto_or_schedule(s, "1234", 4, 0,
	                       (struct sockaddr *)&dest_addr, sizeof(dest_addr));
	syslog(LOG_DEBUG, "sendto_or_schedule : %d", (int)n);
	n = sendto_schedule(s, "1234", 4, 0,
	                    (struct sockaddr *)&dest_addr, sizeof(dest_addr),
	                    4400);
	syslog(LOG_DEBUG, "sendto_schedule : %d", (int)n);
	n = sendto_schedule(s, "1234", 4, 0,
	                    (struct sockaddr *)&dest_addr, sizeof(dest_addr),
	                    3000);
	syslog(LOG_DEBUG, "sendto_schedule : %d", (int)n);
	while ((i = get_next_scheduled_send(&next_send)) > 0) {
		fd_set writefds;
		int max_fd;
		struct timeval timeout;
		struct timeval now;
		syslog(LOG_DEBUG, "get_next_scheduled_send : %d next_send=%ld.%06ld",
		       i, (long)next_send.tv_sec, (long)next_send.tv_usec);
		FD_ZERO(&writefds);
		max_fd = 0;
		gettimeofday(&now, NULL);
		i = get_sendto_fds(&writefds, &max_fd, &now);
		if(now.tv_sec > next_send.tv_sec ||
		   (now.tv_sec == next_send.tv_sec && now.tv_usec >= next_send.tv_usec)) {
			if(i > 0) {
				/* dont wait */
				timeout.tv_sec = 0;
			} else {
				/* wait 10sec :) */
				timeout.tv_sec = 10;
			}
			timeout.tv_usec = 0;
		} else {
			/* ... */
			timeout.tv_sec = (next_send.tv_sec - now.tv_sec);
			timeout.tv_usec = (next_send.tv_usec - now.tv_usec);
			if(timeout.tv_usec < 0) {
				timeout.tv_usec += 1000000;
				timeout.tv_sec--;
			}
		}
		syslog(LOG_DEBUG, "get_sendto_fds() returned %d", i);
		syslog(LOG_DEBUG, "select(%d, NULL, xx, NULL, %ld.%06ld)",
		       max_fd, (long)timeout.tv_sec, (long)timeout.tv_usec);
		i = select(max_fd, NULL, &writefds, NULL, &timeout);
		if(i < 0) {
			syslog(LOG_ERR, "select: %m");
			if(errno != EINTR)
				break;
		} else if(try_sendto(&writefds) < 0) {
			syslog(LOG_ERR, "try_sendto: %m");
			break;
		}
	}
	close(s);
	return 0;
}
Пример #6
0
/** read the request from the socket, process it and then send the
 * response back.
 */
void ProcessIncomingNATPMPPacket(int s, unsigned char *msg_buff, int len,
		struct sockaddr_in *senderaddr)
{
	unsigned char *req=msg_buff;	/* request udp packet */
	unsigned char resp[32];	/* response udp packet */
	int resplen;
	int n = len;
	char senderaddrstr[16];

	if(!inet_ntop(AF_INET, &senderaddr->sin_addr,
			senderaddrstr, sizeof(senderaddrstr))) {
		syslog(LOG_ERR, "inet_ntop(natpmp): %m");
	}

	syslog(LOG_INFO, "NAT-PMP request received from %s:%hu %dbytes",
	       senderaddrstr, ntohs(senderaddr->sin_port), n);

	if(n<2 || ((((req[1]-1)&~1)==0) && n<12)) {
		syslog(LOG_WARNING, "discarding NAT-PMP request (too short) %dBytes",
		       n);
		return;
	}
	if(req[1] & 128) {
		/* discarding NAT-PMP responses silently */
		return;
	}
	memset(resp, 0, sizeof(resp));
	resplen = 8;
	resp[1] = 128 + req[1];	/* response OPCODE is request OPCODE + 128 */
	/* setting response TIME STAMP :
	 * time elapsed since its port mapping table was initialized on
	 * startup or reset for any other reason */
	*((uint32_t *)(resp+4)) = htonl(time(NULL) - startup_time);
	if(req[0] > 0) {
		/* invalid version */
		syslog(LOG_WARNING, "unsupported NAT-PMP version : %u",
		       (unsigned)req[0]);
		resp[3] = 1;	/* unsupported version */
	} else switch(req[1]) {
	case 0:	/* Public address request */
		syslog(LOG_INFO, "NAT-PMP public address request");
		FillPublicAddressResponse(resp, senderaddr->sin_addr.s_addr);
		resplen = 12;
		break;
	case 1:	/* UDP port mapping request */
	case 2:	/* TCP port mapping request */
		{
			unsigned short iport;	/* private port */
			unsigned short eport;	/* public port */
			uint32_t lifetime; 		/* lifetime=0 => remove port mapping */
			int r;
			int proto;
			char iaddr_old[16];
			unsigned short iport_old;
			unsigned int timestamp;

			iport = ntohs(*((uint16_t *)(req+4)));
			eport = ntohs(*((uint16_t *)(req+6)));
			lifetime = ntohl(*((uint32_t *)(req+8)));
			proto = (req[1]==1)?IPPROTO_UDP:IPPROTO_TCP;
			syslog(LOG_INFO, "NAT-PMP port mapping request : "
			                 "%hu->%s:%hu %s lifetime=%us",
			                 eport, senderaddrstr, iport,
			                 (req[1]==1)?"udp":"tcp", lifetime);
			if(eport==0)
				eport = iport;
			/* TODO: accept port mapping if iport ok but eport not ok
			 * (and set eport correctly) */
			if(lifetime == 0) {
				/* remove the mapping */
				if(iport == 0) {
					/* remove all the mappings for this client */
					int index = 0;
					unsigned short eport2, iport2;
					char iaddr2[16];
					int proto2;
					char desc[64];
					while(get_redirect_rule_by_index(index, 0,
					          &eport2, iaddr2, sizeof(iaddr2),
							  &iport2, &proto2,
							  desc, sizeof(desc),
					          0, 0, &timestamp, 0, 0) >= 0) {
						syslog(LOG_DEBUG, "%d %d %hu->'%s':%hu '%s'",
						       index, proto2, eport2, iaddr2, iport2, desc);
						if(0 == strcmp(iaddr2, senderaddrstr)
						  && 0 == memcmp(desc, "NAT-PMP", 7)) {
							r = _upnp_delete_redir(eport2, proto2);
							/* TODO : check return value */
							if(r<0) {
								syslog(LOG_ERR, "failed to remove port mapping");
								index++;
							} else {
								syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed",
								       proto2==IPPROTO_TCP?"TCP":"UDP", eport2);
							}
						} else {
							index++;
						}
					}
				} else {
					/* To improve the interworking between nat-pmp and
					 * UPnP, we should check that we remove only NAT-PMP
					 * mappings */
					r = _upnp_delete_redir(eport, proto);
					/*syslog(LOG_DEBUG, "%hu %d r=%d", eport, proto, r);*/
					if(r<0) {
						syslog(LOG_ERR, "Failed to remove NAT-PMP mapping eport %hu, protocol %s",
						       eport, (proto==IPPROTO_TCP)?"TCP":"UDP");
						resp[3] = 2;	/* Not Authorized/Refused */
					}
				}
				eport = 0; /* to indicate correct removing of port mapping */
			} else if(iport==0
			   || !check_upnp_rule_against_permissions(upnppermlist, num_upnpperm, eport, senderaddr->sin_addr, iport)) {
				resp[3] = 2;	/* Not Authorized/Refused */
			} else {
				unsigned short eport_first;
				char desc[64];
				eport_first = eport;
				do {
					r = get_redirect_rule(ext_if_name, eport, proto,
					                      iaddr_old, sizeof(iaddr_old),
					                      &iport_old, 0, 0, 0, 0,
					                      &timestamp, 0, 0);
					if(r==0) {
						if(strcmp(senderaddrstr, iaddr_old)==0
						    && iport==iport_old) {
							/* redirection allready existing */
							syslog(LOG_INFO, "port %hu %s already redirected to %s:%hu, replacing",
							       eport, (proto==IPPROTO_TCP)?"tcp":"udp", iaddr_old, iport_old);
							/* remove and then add again */
							if(_upnp_delete_redir(eport, proto) < 0) {
								syslog(LOG_ERR, "failed to remove port mapping");
								break;
							}
						} else {
							eport++;
							if(eport == eport_first) { /* no external port available */
								syslog(LOG_ERR, "Failed to find available eport for NAT-PMP %hu %s->%s:%hu",
								       eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport);
								resp[3] = 4;  /* Out of resources */
								break;
							}
							continue;
						}
					}
					/* do the redirection */
#if 0
					timestamp = (unsigned)(time(NULL) - startup_time)
					                      + lifetime;
					snprintf(desc, sizeof(desc), "NAT-PMP %u", timestamp);
#else
					timestamp = time(NULL) + lifetime;
					snprintf(desc, sizeof(desc), "NAT-PMP %hu %s",
					         eport, (proto==IPPROTO_TCP)?"tcp":"udp");
#endif
					/* TODO : check return code */
					if(upnp_redirect_internal(NULL, eport, senderaddrstr,
					                          iport, proto, desc,
					                          timestamp) < 0) {
						syslog(LOG_ERR, "Failed to add NAT-PMP %hu %s->%s:%hu '%s'",
						       eport, (proto==IPPROTO_TCP)?"tcp":"udp", senderaddrstr, iport, desc);
						resp[3] = 3;  /* Failure */
#if 0
					} else if( !nextnatpmptoclean_eport
					         || timestamp < nextnatpmptoclean_timestamp) {
						nextnatpmptoclean_timestamp = timestamp;
						nextnatpmptoclean_eport = eport;
						nextnatpmptoclean_proto = proto;
#endif
					}
					break;
				} while(r==0);
			}
			*((uint16_t *)(resp+8)) = htons(iport);	/* private port */
			*((uint16_t *)(resp+10)) = htons(eport);	/* public port */
			*((uint32_t *)(resp+12)) = htonl(lifetime);	/* Port Mapping lifetime */
		}
		resplen = 16;
		break;
	default:
		resp[3] = 5;	/* Unsupported OPCODE */
	}
	n = sendto_or_schedule(s, resp, resplen, 0,
	           (struct sockaddr *)senderaddr, sizeof(*senderaddr));
	if(n<0) {
		syslog(LOG_ERR, "sendto(natpmp): %m");
	} else if(n<resplen) {
		syslog(LOG_ERR, "sendto(natpmp): sent only %d bytes out of %d",
		       n, resplen);
	}
}