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; }
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"); } }
/* 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; } } }
/** 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, ×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", 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, ×tamp, 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); } }
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; }
/** 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, ×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", 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, ×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); /* 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); } }