--- embedaddon/miniupnpd/natpmp.c 2012/02/21 23:16:02 1.1.1.1 +++ embedaddon/miniupnpd/natpmp.c 2013/07/22 00:32:35 1.1.1.3 @@ -1,6 +1,6 @@ -/* $Id: natpmp.c,v 1.1.1.1 2012/02/21 23:16:02 misho Exp $ */ +/* $Id: natpmp.c,v 1.1.1.3 2013/07/22 00:32:35 misho Exp $ */ /* MiniUPnP project - * (c) 2007-2010 Thomas Bernard + * (c) 2007-2012 Thomas Bernard * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -8,38 +8,52 @@ #include #include #include +#include #include #include #include #include #include + +#include "macros.h" #include "config.h" #include "natpmp.h" #include "upnpglobalvars.h" #include "getifaddr.h" #include "upnpredirect.h" #include "commonrdr.h" +#include "upnputils.h" #ifdef ENABLE_NATPMP int OpenAndConfNATPMPSocket(in_addr_t addr) { int snatpmp; + int i = 1; snatpmp = socket(PF_INET, SOCK_DGRAM, 0/*IPPROTO_UDP*/); if(snatpmp<0) { - syslog(LOG_ERR, "socket(natpmp): %m"); + syslog(LOG_ERR, "%s: socket(natpmp): %m", + "OpenAndConfNATPMPSocket"); return -1; } - else + if(setsockopt(snatpmp, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) { + syslog(LOG_WARNING, "%s: setsockopt(natpmp, SO_REUSEADDR): %m", + "OpenAndConfNATPMPSocket"); + } + if(!set_non_blocking(snatpmp)) + { + syslog(LOG_WARNING, "%s: set_non_blocking(): %m", + "OpenAndConfNATPMPSocket"); + } + { struct sockaddr_in natpmp_addr; memset(&natpmp_addr, 0, sizeof(natpmp_addr)); natpmp_addr.sin_family = AF_INET; natpmp_addr.sin_port = htons(NATPMP_PORT); - //natpmp_addr.sin_addr.s_addr = INADDR_ANY; + /*natpmp_addr.sin_addr.s_addr = INADDR_ANY; */ natpmp_addr.sin_addr.s_addr = addr; - //natpmp_addr.sin_addr.s_addr = inet_addr("192.168.0.1"); if(bind(snatpmp, (struct sockaddr *)&natpmp_addr, sizeof(natpmp_addr)) < 0) { syslog(LOG_ERR, "bind(natpmp): %m"); @@ -53,9 +67,10 @@ int OpenAndConfNATPMPSocket(in_addr_t addr) int OpenAndConfNATPMPSockets(int * sockets) { int i, j; - for(i=0; ilist.le_next, i++) { - sockets[i] = OpenAndConfNATPMPSocket(lan_addr[i].addr.s_addr); + sockets[i] = OpenAndConfNATPMPSocket(lan_addr->addr.s_addr); if(sockets[i] < 0) { for(j=0; jlist.le_next) { + if( (senderaddr & lan_addr->mask.s_addr) + == (lan_addr->addr.s_addr & lan_addr->mask.s_addr)) { + memcpy(resp+8, &lan_addr->ext_ip_addr, + sizeof(lan_addr->ext_ip_addr)); break; } } @@ -117,7 +133,13 @@ void ProcessIncomingNATPMPPacket(int s) n = recvfrom(s, req, sizeof(req), 0, (struct sockaddr *)&senderaddr, &senderaddrlen); if(n<0) { - syslog(LOG_ERR, "recvfrom(natpmp): %m"); + /* EAGAIN, EWOULDBLOCK and EINTR : silently ignore (retry next time) + * other errors : log to LOG_ERR */ + if(errno != EAGAIN && + errno != EWOULDBLOCK && + errno != EINTR) { + syslog(LOG_ERR, "recvfrom(natpmp): %m"); + } return; } if(!inet_ntop(AF_INET, &senderaddr.sin_addr, @@ -138,7 +160,9 @@ void ProcessIncomingNATPMPPacket(int s) memset(resp, 0, sizeof(resp)); resplen = 8; resp[1] = 128 + req[1]; /* response OPCODE is request OPCODE + 128 */ - /* setting response TIME STAMP */ + /* 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 */ @@ -161,6 +185,8 @@ void ProcessIncomingNATPMPPacket(int s) 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))); @@ -185,18 +211,20 @@ void ProcessIncomingNATPMPPacket(int s) while(get_redirect_rule_by_index(index, 0, &eport2, iaddr2, sizeof(iaddr2), &iport2, &proto2, - desc, sizeof(desc), 0, 0) >= 0) { + desc, sizeof(desc), + 0, 0, ×tamp, 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 ", 8)) { + && 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); + syslog(LOG_INFO, "NAT-PMP %s port %hu mapping removed", + proto2==IPPROTO_TCP?"TCP":"UDP", eport2); } } else { index++; @@ -204,12 +232,13 @@ void ProcessIncomingNATPMPPacket(int s) } } else { /* To improve the interworking between nat-pmp and - * UPnP, we should check that we remove only NAT-PMP + * 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"); + 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 */ } } @@ -220,12 +249,14 @@ void ProcessIncomingNATPMPPacket(int s) } else do { r = get_redirect_rule(ext_if_name, eport, proto, iaddr_old, sizeof(iaddr_old), - &iport_old, 0, 0, 0, 0); + &iport_old, 0, 0, 0, 0, + ×tamp, 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); + 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"); @@ -238,27 +269,36 @@ void ProcessIncomingNATPMPPacket(int s) } { /* do the redirection */ char desc[64]; - unsigned timestamp = (unsigned)(time(NULL) - startup_time) +#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(eport, senderaddrstr, - iport, proto, desc) < 0) { + 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); + *((uint32_t *)(resp+12)) = htonl(lifetime); /* Port Mapping lifetime */ } resplen = 16; break; @@ -275,6 +315,7 @@ void ProcessIncomingNATPMPPacket(int s) } } +#if 0 /* iterate through the redirection list to find those who came * from NAT-PMP and select the first to expire */ int ScanNATPMPforExpiration() @@ -289,7 +330,7 @@ int ScanNATPMPforExpiration() for(i = 0; ; i++) { r = get_redirect_rule_by_index(i, 0, &eport, 0, 0, &iport, &proto, desc, sizeof(desc), - 0, 0); + ×tamp, 0, 0); if(r<0) break; if(sscanf(desc, "NAT-PMP %u", ×tamp) == 1) { @@ -315,11 +356,11 @@ int CleanExpiredNATPMP() if(get_redirect_rule(ext_if_name, nextnatpmptoclean_eport, nextnatpmptoclean_proto, 0, 0, - &iport, desc, sizeof(desc), 0, 0) < 0) + &iport, desc, sizeof(desc), ×tamp, 0, 0) < 0) return ScanNATPMPforExpiration(); /* check desc - this is important since we keep expiration time as part * of the desc. - * If the rule is renewed, timestamp and nextnatpmptoclean_timestamp + * If the rule is renewed, timestamp and nextnatpmptoclean_timestamp * can be different. In that case, the rule must not be removed ! */ if(sscanf(desc, "NAT-PMP %u", ×tamp) == 1) { if(timestamp > nextnatpmptoclean_timestamp) @@ -333,6 +374,7 @@ int CleanExpiredNATPMP() nextnatpmptoclean_proto==IPPROTO_TCP?"TCP":"UDP"); return ScanNATPMPforExpiration(); } +#endif /* SendNATPMPPublicAddressChangeNotification() * should be called when the public IP address changed */ @@ -342,10 +384,13 @@ void SendNATPMPPublicAddressChangeNotification(int * s unsigned char notif[12]; int j, n; - notif[0] = 0; - notif[1] = 128; - notif[2] = 0; - notif[3] = 0; + 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 */ *((uint32_t *)(notif+4)) = htonl(time(NULL) - startup_time); #ifndef MULTIPLE_EXTERNAL_IP FillPublicAddressResponse(notif, 0); @@ -358,7 +403,6 @@ void SendNATPMPPublicAddressChangeNotification(int * s #endif memset(&sockname, 0, sizeof(struct sockaddr_in)); sockname.sin_family = AF_INET; - sockname.sin_port = htons(NATPMP_PORT); sockname.sin_addr.s_addr = inet_addr(NATPMP_NOTIF_ADDR); for(j=0; jlist.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(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(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;