commit
810cb665c2
|
@ -1,5 +1,8 @@
|
|||
$Id: Changelog.txt,v 1.441 2018/05/08 21:34:18 nanard Exp $
|
||||
|
||||
2018/07/06:
|
||||
STUN support
|
||||
|
||||
VERSION 2.1 : released on 2018/05/08
|
||||
|
||||
2018/05/02:
|
||||
|
|
|
@ -101,6 +101,7 @@ STDOBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \
|
|||
upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \
|
||||
options.o upnppermissions.o minissdp.o natpmp.o pcpserver.o \
|
||||
upnpevents.o upnputils.o getconnstatus.o \
|
||||
upnpstun.o \
|
||||
upnppinhole.o asyncsendto.o portinuse.o
|
||||
BSDOBJS = bsd/getifstats.o bsd/ifacewatcher.o bsd/getroute.o
|
||||
SUNOSOBJS = solaris/getifstats.o bsd/ifacewatcher.o bsd/getroute.o
|
||||
|
|
|
@ -48,6 +48,7 @@ BASEOBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \
|
|||
upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \
|
||||
options.o upnppermissions.o minissdp.o natpmp.o pcpserver.o \
|
||||
upnpevents.o upnputils.o getconnstatus.o \
|
||||
upnpstun.o \
|
||||
upnppinhole.o pcplearndscp.o asyncsendto.o
|
||||
|
||||
LNXOBJS = linux/getifstats.o linux/ifacewatcher.o linux/getroute.o
|
||||
|
@ -341,3 +342,4 @@ testasyncsendto.o: miniupnpdtypes.h config.h upnputils.h asyncsendto.h
|
|||
testportinuse.o: macros.h config.h portinuse.h
|
||||
miniupnpdctl.o: macros.h
|
||||
testssdppktgen.o: macros.h config.h miniupnpdpath.h upnphttp.h
|
||||
upnpstun.o: config.h upnpstun.h netfilter/iptcrdr.h
|
||||
|
|
|
@ -42,6 +42,7 @@ BASEOBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \
|
|||
upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \
|
||||
options.o upnppermissions.o minissdp.o natpmp.o pcpserver.o \
|
||||
upnpevents.o upnputils.o getconnstatus.o \
|
||||
upnpstun.o \
|
||||
upnppinhole.o pcplearndscp.o asyncsendto.o
|
||||
|
||||
LNXOBJS = linux/getifstats.o linux/ifacewatcher.o linux/getroute.o
|
||||
|
|
|
@ -31,6 +31,7 @@ STD_OBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \
|
|||
upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \
|
||||
options.o upnppermissions.o minissdp.o natpmp.o \
|
||||
upnpevents.o getconnstatus.o upnputils.o \
|
||||
upnpstun.o \
|
||||
upnppinhole.o asyncsendto.o portinuse.o pcpserver.o
|
||||
MAC_OBJS = mac/getifstats.o bsd/ifacewatcher.o bsd/getroute.o
|
||||
IPFW_OBJS = ipfw/ipfwrdr.o ipfw/ipfwaux.o
|
||||
|
|
|
@ -50,6 +50,7 @@ STDOBJS = miniupnpd.o upnphttp.o upnpdescgen.o upnpsoap.o \
|
|||
upnpredirect.o getifaddr.o daemonize.o upnpglobalvars.o \
|
||||
options.o upnppermissions.o minissdp.o natpmp.o pcpserver.o \
|
||||
upnpevents.o upnputils.o getconnstatus.o \
|
||||
upnpstun.o \
|
||||
upnppinhole.o asyncsendto.o portinuse.o
|
||||
BSDOBJS = bsd/getifstats.o bsd/ifacewatcher.o bsd/getroute.o
|
||||
SUNOSOBJS = solaris/getifstats.o bsd/ifacewatcher.o bsd/getroute.o
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* $Id: asyncsendto.c,v 1.8 2017/05/24 22:51:57 nanard Exp $ */
|
||||
/* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2006-2017 Thomas Bernard
|
||||
* (c) 2006-2018 Thomas Bernard
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
|
||||
|
@ -345,4 +345,3 @@ void finalize_sendto(void)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/* $Id: getifaddr.c,v 1.19 2013/12/13 14:28:40 nanard Exp $ */
|
||||
/* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2006-2014 Thomas Bernard
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
|
||||
* (c) 2006-2018 Thomas Bernard
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
|
||||
|
@ -259,3 +260,43 @@ find_ipv6_addr(const char * ifname,
|
|||
}
|
||||
#endif
|
||||
|
||||
/* List of IP address blocks which are private / reserved and therefore not suitable for public external IP addresses */
|
||||
/* If interface has IP address from one of this block, then it is either behind NAT or port forwarding is impossible */
|
||||
#define IP(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
|
||||
#define MSK(m) (32-(m))
|
||||
static const struct { uint32_t address; uint32_t rmask; } reserved[] = {
|
||||
{ IP( 0, 0, 0, 0), MSK( 8) }, /* RFC1122 "This host on this network" */
|
||||
{ IP( 10, 0, 0, 0), MSK( 8) }, /* RFC1918 Private-Use */
|
||||
{ IP(100, 64, 0, 0), MSK(10) }, /* RFC6598 Shared Address Space */
|
||||
{ IP(127, 0, 0, 0), MSK( 8) }, /* RFC1122 Loopback */
|
||||
{ IP(169, 254, 0, 0), MSK(16) }, /* RFC3927 Link-Local */
|
||||
{ IP(172, 16, 0, 0), MSK(12) }, /* RFC1918 Private-Use */
|
||||
{ IP(192, 0, 0, 0), MSK(24) }, /* RFC6890 IETF Protocol Assignments */
|
||||
{ IP(192, 0, 2, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-1) */
|
||||
{ IP(192, 31, 196, 0), MSK(24) }, /* RFC7535 AS112-v4 */
|
||||
{ IP(192, 52, 193, 0), MSK(24) }, /* RFC7450 AMT */
|
||||
{ IP(192, 88, 99, 0), MSK(24) }, /* RFC7526 6to4 Relay Anycast */
|
||||
{ IP(192, 168, 0, 0), MSK(16) }, /* RFC1918 Private-Use */
|
||||
{ IP(192, 175, 48, 0), MSK(16) }, /* RFC7534 Direct Delegation AS112 Service */
|
||||
{ IP(198, 18, 0, 0), MSK(15) }, /* RFC2544 Benchmarking */
|
||||
{ IP(198, 51, 100, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-2) */
|
||||
{ IP(203, 0, 113, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-3) */
|
||||
{ IP(224, 0, 0, 0), MSK( 4) }, /* RFC1112 Multicast */
|
||||
{ IP(240, 0, 0, 0), MSK( 4) }, /* RFC1112 Reserved for Future Use + RFC919 Limited Broadcast */
|
||||
};
|
||||
#undef IP
|
||||
#undef MSK
|
||||
|
||||
int
|
||||
addr_is_reserved(struct in_addr * addr)
|
||||
{
|
||||
uint32_t address = ntohl(addr->s_addr);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(reserved)/sizeof(reserved[0]); ++i) {
|
||||
if ((address >> reserved[i].rmask) == (reserved[i].address >> reserved[i].rmask))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -28,5 +28,9 @@ int
|
|||
find_ipv6_addr(const char * ifname,
|
||||
char * dst, int n);
|
||||
|
||||
/* check if address is in private / reserved block (e.g. local area network) */
|
||||
int
|
||||
addr_is_reserved(struct in_addr * addr);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
#include "minissdp.h"
|
||||
#include "upnpredirect.h"
|
||||
#include "upnppinhole.h"
|
||||
#include "upnpstun.h"
|
||||
#include "miniupnpdtypes.h"
|
||||
#include "daemonize.h"
|
||||
#include "upnpevents.h"
|
||||
|
@ -990,6 +991,12 @@ parselanaddr(struct lan_addr_s * lan_addr, const char * str)
|
|||
if(!inet_aton(lan_addr->ext_ip_str, &lan_addr->ext_ip_addr)) {
|
||||
/* error */
|
||||
fprintf(stderr, "Error parsing address : %s\n", lan_addr->ext_ip_str);
|
||||
return -1;
|
||||
}
|
||||
if(addr_is_reserved(&lan_addr->ext_ip_addr)) {
|
||||
/* error */
|
||||
fprintf(stderr, "Error: option ext_ip address contains reserved / private address : %s\n", lan_addr->ext_ip_str);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1022,6 +1029,45 @@ parselan_error:
|
|||
return -1;
|
||||
}
|
||||
|
||||
static char ext_addr_str[INET_ADDRSTRLEN];
|
||||
|
||||
int update_ext_ip_addr_from_stun(int init)
|
||||
{
|
||||
struct in_addr if_addr, ext_addr;
|
||||
int restrictive_nat;
|
||||
char if_addr_str[INET_ADDRSTRLEN];
|
||||
|
||||
syslog(LOG_INFO, "STUN: Performing with host=%s and port=%u ...", ext_stun_host, (unsigned)ext_stun_port);
|
||||
|
||||
if (getifaddr(ext_if_name, if_addr_str, INET_ADDRSTRLEN, &if_addr, NULL) < 0) {
|
||||
syslog(LOG_ERR, "STUN: Cannot get IP address for ext interface %s", ext_if_name);
|
||||
return 1;
|
||||
}
|
||||
if (perform_stun(ext_if_name, if_addr_str, ext_stun_host, ext_stun_port, &ext_addr, &restrictive_nat) != 0) {
|
||||
syslog(LOG_ERR, "STUN: Performing STUN failed: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (!inet_ntop(AF_INET, &ext_addr, ext_addr_str, sizeof(ext_addr_str))) {
|
||||
syslog(LOG_ERR, "STUN: Function inet_ntop for IP address returned by STUN failed: %s", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((init || disable_port_forwarding) && !restrictive_nat) {
|
||||
if (addr_is_reserved(&if_addr))
|
||||
syslog(LOG_INFO, "STUN: ext interface %s with IP address %s is now behind unrestricted NAT 1:1 with public IP address %s: Port forwarding is now enabled", ext_if_name, if_addr_str, ext_addr_str);
|
||||
else
|
||||
syslog(LOG_INFO, "STUN: ext interface %s has now public IP address %s: Port forwarding is now enabled", ext_if_name, if_addr_str);
|
||||
} else if ((init || !disable_port_forwarding) && restrictive_nat) {
|
||||
syslog(LOG_INFO, "STUN: ext interface %s with IP address %s is now behind restrictive NAT with public IP address %s: Port forwarding is now impossible", ext_if_name, if_addr_str, ext_addr_str);
|
||||
} else {
|
||||
syslog(LOG_INFO, "STUN: ... done");
|
||||
}
|
||||
|
||||
use_ext_ip_addr = ext_addr_str;
|
||||
disable_port_forwarding = restrictive_nat;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* fill uuidvalue_wan and uuidvalue_wcd based on uuidvalue_igd */
|
||||
void complete_uuidvalues(void)
|
||||
{
|
||||
|
@ -1070,6 +1116,7 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||
int pid;
|
||||
int debug_flag = 0;
|
||||
int openlog_option;
|
||||
struct in_addr addr;
|
||||
struct sigaction sa;
|
||||
/*const char * logfilename = 0;*/
|
||||
const char * presurl = 0;
|
||||
|
@ -1134,6 +1181,16 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||
case UPNPEXT_IP:
|
||||
use_ext_ip_addr = ary_options[i].value;
|
||||
break;
|
||||
case UPNPEXT_PERFORM_STUN:
|
||||
if(strcmp(ary_options[i].value, "yes") == 0)
|
||||
SETFLAG(PERFORMSTUNMASK);
|
||||
break;
|
||||
case UPNPEXT_STUN_HOST:
|
||||
ext_stun_host = ary_options[i].value;
|
||||
break;
|
||||
case UPNPEXT_STUN_PORT:
|
||||
ext_stun_port = atoi(ary_options[i].value);
|
||||
break;
|
||||
case UPNPLISTENING_IP:
|
||||
lan_addr = (struct lan_addr_s *) malloc(sizeof(struct lan_addr_s));
|
||||
if (lan_addr == NULL)
|
||||
|
@ -1330,6 +1387,10 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||
return 1;
|
||||
}
|
||||
#endif /* ENABLE_PCP */
|
||||
if (GETFLAG(PERFORMSTUNMASK) && !ext_stun_host) {
|
||||
fprintf(stderr, "You must specify ext_stun_host= when ext_perform_stun=yes\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif /* DISABLE_CONFIG_FILE */
|
||||
|
||||
|
@ -1354,9 +1415,14 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||
break;
|
||||
case 'o':
|
||||
if(i+1 < argc)
|
||||
use_ext_ip_addr = argv[++i];
|
||||
else
|
||||
if(i+1 < argc) {
|
||||
i++;
|
||||
if (0 == strncasecmp(argv[i], "STUN:", 5)) {
|
||||
SETFLAG(PERFORMSTUNMASK);
|
||||
ext_stun_host = argv[i] + 5;
|
||||
} else
|
||||
use_ext_ip_addr = argv[i];
|
||||
} else
|
||||
fprintf(stderr, "Option -%c takes one argument.\n", argv[i][1]);
|
||||
break;
|
||||
case 't':
|
||||
|
@ -1606,6 +1672,22 @@ init(int argc, char * * argv, struct runtime_vars * v)
|
|||
goto print_usage;
|
||||
}
|
||||
|
||||
if (use_ext_ip_addr && GETFLAG(PERFORMSTUNMASK)) {
|
||||
fprintf(stderr, "Error: options ext_ip= and ext_perform_stun=yes cannot be specified together\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (use_ext_ip_addr) {
|
||||
if (inet_pton(AF_INET, use_ext_ip_addr, &addr) != 1) {
|
||||
fprintf(stderr, "Error: option ext_ip contains invalid address %s\n", use_ext_ip_addr);
|
||||
return 1;
|
||||
}
|
||||
if (addr_is_reserved(&addr)) {
|
||||
fprintf(stderr, "Error: option ext_ip contains reserved / private address %s, not public routable\n", use_ext_ip_addr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(debug_flag)
|
||||
{
|
||||
pid = getpid();
|
||||
|
@ -1778,6 +1860,7 @@ print_usage:
|
|||
"\tDefault pid file is '%s'.\n"
|
||||
"\tDefault config file is '%s'.\n"
|
||||
"\tWith -d miniupnpd will run as a standard program.\n"
|
||||
"\t-o argument is either an IPv4 address or \"STUN:xx.xx.xx.xx\".\n"
|
||||
#if defined(USE_PF) || defined(USE_IPF)
|
||||
"\t-L sets packet log in pf and ipf on.\n"
|
||||
#endif
|
||||
|
@ -1923,6 +2006,26 @@ main(int argc, char * * argv)
|
|||
GETFLAG(ENABLEUPNPMASK) ? "UPnP-IGD " : "",
|
||||
ext_if_name, upnp_bootid);
|
||||
|
||||
if(GETFLAG(PERFORMSTUNMASK))
|
||||
{
|
||||
if (update_ext_ip_addr_from_stun(1) != 0) {
|
||||
syslog(LOG_ERR, "Performing STUN failed. EXITING");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if (!use_ext_ip_addr)
|
||||
{
|
||||
char if_addr[INET_ADDRSTRLEN];
|
||||
struct in_addr addr;
|
||||
if (getifaddr(ext_if_name, if_addr, INET_ADDRSTRLEN, &addr, NULL) < 0) {
|
||||
syslog(LOG_WARNING, "Cannot get IP address for ext interface %s. Network is down", ext_if_name);
|
||||
} else if (addr_is_reserved(&addr)) {
|
||||
syslog(LOG_INFO, "Reserved / private IP address %s on ext interface %s: Port forwarding is impossible", if_addr, ext_if_name);
|
||||
syslog(LOG_INFO, "You are probably behind NAT, enable option ext_perform_stun=yes to detect public IP address");
|
||||
disable_port_forwarding = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(GETFLAG(ENABLEUPNPMASK))
|
||||
{
|
||||
unsigned short listen_port;
|
||||
|
@ -2114,6 +2217,23 @@ main(int argc, char * * argv)
|
|||
if(should_send_public_address_change_notif)
|
||||
{
|
||||
syslog(LOG_INFO, "should send external iface address change notification(s)");
|
||||
if(GETFLAG(PERFORMSTUNMASK))
|
||||
update_ext_ip_addr_from_stun(0);
|
||||
if (!use_ext_ip_addr)
|
||||
{
|
||||
char if_addr[INET_ADDRSTRLEN];
|
||||
struct in_addr addr;
|
||||
if (getifaddr(ext_if_name, if_addr, INET_ADDRSTRLEN, &addr, NULL) == 0) {
|
||||
int reserved = addr_is_reserved(&addr);
|
||||
if (disable_port_forwarding && !reserved) {
|
||||
syslog(LOG_INFO, "Public IP address %s on ext interface %s: Port forwarding is enabled", if_addr, ext_if_name);
|
||||
} else if (!disable_port_forwarding && reserved) {
|
||||
syslog(LOG_INFO, "Reserved / private IP address %s on ext interface %s: Port forwarding is impossible", if_addr, ext_if_name);
|
||||
syslog(LOG_INFO, "You are probably behind NAT, enable option ext_perform_stun=yes to detect public IP address");
|
||||
}
|
||||
disable_port_forwarding = reserved;
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_NATPMP
|
||||
if(GETFLAG(ENABLENATPMPMASK))
|
||||
SendNATPMPPublicAddressChangeNotification(snatpmp, addr_count);
|
||||
|
|
|
@ -4,6 +4,25 @@
|
|||
# If the WAN interface has several IP addresses, you
|
||||
# can specify the one to use below
|
||||
#ext_ip=
|
||||
# WAN interface must have public IP address. Otherwise it is behind NAT
|
||||
# and port forwarding is impossible. In some cases WAN interface can be
|
||||
# behind unrestricted NAT 1:1 when all incoming traffic is NAT-ed and
|
||||
# routed to WAN interfaces without any filtering. In this cases miniupnpd
|
||||
# needs to know public IP address and it can be learnt by asking external
|
||||
# server via STUN protocol. Following option enable retrieving external
|
||||
# public IP address from STUN server and detection of NAT type. You need
|
||||
# to specify also external STUN server in stun_host option below.
|
||||
# This option is disabled by default.
|
||||
#ext_perform_stun=yes
|
||||
# Specify STUN server, either hostname or IP address
|
||||
# Some public STUN servers:
|
||||
# stun.stunprotocol.org
|
||||
# stun.sipgate.net
|
||||
# stun.xten.com
|
||||
# stun.l.google.com (on non standard port 19302)
|
||||
#ext_stun_host=stun.stunprotocol.org
|
||||
# Specify STUN UDP port, by default it is standard port 3478.
|
||||
#ext_stun_port=3478
|
||||
|
||||
# LAN network interfaces IPs / networks
|
||||
# There can be multiple listening IPs for SSDP traffic, in that case
|
||||
|
|
|
@ -94,6 +94,7 @@ error:
|
|||
static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr)
|
||||
{
|
||||
#ifndef MULTIPLE_EXTERNAL_IP
|
||||
struct in_addr addr;
|
||||
char tmp[16];
|
||||
UNUSED(senderaddr);
|
||||
|
||||
|
@ -103,10 +104,13 @@ static void FillPublicAddressResponse(unsigned char * resp, in_addr_t senderaddr
|
|||
if(!ext_if_name || ext_if_name[0]=='\0') {
|
||||
resp[3] = 3; /* Network Failure (e.g. NAT box itself
|
||||
* has not obtained a DHCP lease) */
|
||||
} else if(getifaddr(ext_if_name, tmp, INET_ADDRSTRLEN, NULL, NULL) < 0) {
|
||||
} else if(getifaddr(ext_if_name, tmp, INET_ADDRSTRLEN, &addr, NULL) < 0) {
|
||||
syslog(LOG_ERR, "Failed to get IP for interface %s", ext_if_name);
|
||||
resp[3] = 3; /* Network Failure (e.g. NAT box itself
|
||||
* has not obtained a DHCP lease) */
|
||||
} else if (addr_is_reserved(&addr)) {
|
||||
resp[3] = 3; /* Network Failure, box has not obtained
|
||||
public IP address */
|
||||
} else {
|
||||
inet_pton(AF_INET, tmp, resp+8); /* ok */
|
||||
}
|
||||
|
|
|
@ -621,6 +621,69 @@ delete_rule_and_commit(unsigned int index, IPTC_HANDLE h,
|
|||
return r;
|
||||
}
|
||||
|
||||
/* delete_filter_rule()
|
||||
*/
|
||||
int
|
||||
delete_filter_rule(const char * ifname, unsigned short port, int proto)
|
||||
{
|
||||
int r = -1;
|
||||
unsigned index = 0;
|
||||
unsigned i = 0;
|
||||
IPTC_HANDLE h;
|
||||
const struct ipt_entry * e;
|
||||
const struct ipt_entry_match *match;
|
||||
UNUSED(ifname);
|
||||
|
||||
if((h = iptc_init("filter")))
|
||||
{
|
||||
i = 0;
|
||||
/* we must find the right index for the filter rule */
|
||||
#ifdef IPTABLES_143
|
||||
for(e = iptc_first_rule(miniupnpd_forward_chain, h);
|
||||
e;
|
||||
e = iptc_next_rule(e, h), i++)
|
||||
#else
|
||||
for(e = iptc_first_rule(miniupnpd_forward_chain, &h);
|
||||
e;
|
||||
e = iptc_next_rule(e, &h), i++)
|
||||
#endif
|
||||
{
|
||||
if(proto==e->ip.proto)
|
||||
{
|
||||
match = (const struct ipt_entry_match *)&e->elems;
|
||||
/*syslog(LOG_DEBUG, "filter rule #%u: %s %s",
|
||||
i, match->u.user.name, inet_ntoa(e->ip.dst));*/
|
||||
if(0 == strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN))
|
||||
{
|
||||
const struct ipt_tcp * info;
|
||||
info = (const struct ipt_tcp *)match->data;
|
||||
if(port != info->dpts[0])
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
const struct ipt_udp * info;
|
||||
info = (const struct ipt_udp *)match->data;
|
||||
if(port != info->dpts[0])
|
||||
continue;
|
||||
}
|
||||
index = i;
|
||||
/*syslog(LOG_INFO, "Trying to delete filter rule at index %u", index);*/
|
||||
r = delete_rule_and_commit(index, h, miniupnpd_forward_chain, "delete_filter_rule");
|
||||
h = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(h)
|
||||
#ifdef IPTABLES_143
|
||||
iptc_free(h);
|
||||
#else
|
||||
iptc_free(&h);
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
/* delete_redirect_and_filter_rules()
|
||||
*/
|
||||
int
|
||||
|
|
|
@ -32,6 +32,9 @@ add_filter_rule2(const char * ifname,
|
|||
int
|
||||
delete_redirect_and_filter_rules(unsigned short eport, int proto);
|
||||
|
||||
int
|
||||
delete_filter_rule(const char * ifname, unsigned short port, int proto);
|
||||
|
||||
int
|
||||
add_peer_dscp_rule2(const char * ifname,
|
||||
const char * rhost, unsigned short rport,
|
||||
|
|
|
@ -30,6 +30,9 @@ static const struct {
|
|||
} optionids[] = {
|
||||
{ UPNPEXT_IFNAME, "ext_ifname" },
|
||||
{ UPNPEXT_IP, "ext_ip" },
|
||||
{ UPNPEXT_PERFORM_STUN, "ext_perform_stun" },
|
||||
{ UPNPEXT_STUN_HOST, "ext_stun_host" },
|
||||
{ UPNPEXT_STUN_PORT, "ext_stun_port" },
|
||||
{ UPNPLISTENING_IP, "listening_ip" },
|
||||
#ifdef ENABLE_IPV6
|
||||
{ UPNPIPV6_LISTENING_IP, "ipv6_listening_ip" },
|
||||
|
|
|
@ -17,6 +17,9 @@ enum upnpconfigoptions {
|
|||
UPNP_INVALID = 0,
|
||||
UPNPEXT_IFNAME = 1, /* ext_ifname */
|
||||
UPNPEXT_IP, /* ext_ip */
|
||||
UPNPEXT_PERFORM_STUN, /* ext_perform_stun */
|
||||
UPNPEXT_STUN_HOST, /* ext_stun_host */
|
||||
UPNPEXT_STUN_PORT, /* ext_stun_port */
|
||||
UPNPLISTENING_IP, /* listening_ip */
|
||||
#ifdef ENABLE_IPV6
|
||||
UPNPIPV6_LISTENING_IP, /* listening address for IPv6 */
|
||||
|
|
|
@ -790,7 +790,7 @@ syslog(LOG_DEBUG, "%2d port=%hu proto=%d addr=%8x",
|
|||
#endif
|
||||
if( (iport == ntohs(pr.rule.dst.port[0]))
|
||||
&& (pr.rule.proto == proto) &&
|
||||
(iaddr == pr.rule.dst.addr.v.a.addr.v4.s_addr)
|
||||
(iaddr == 0 || iaddr == pr.rule.dst.addr.v.a.addr.v4.s_addr)
|
||||
)
|
||||
{
|
||||
pr.action = PF_CHANGE_GET_TICKET;
|
||||
|
@ -814,6 +814,12 @@ error:
|
|||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
delete_filter_rule(const char * ifname, unsigned short port, int proto)
|
||||
{
|
||||
return priv_delete_filter_rule(ifname, port, proto, 0);
|
||||
}
|
||||
|
||||
int
|
||||
delete_redirect_and_filter_rules(const char * ifname, unsigned short eport,
|
||||
int proto)
|
||||
|
|
|
@ -58,6 +58,9 @@ int
|
|||
delete_redirect_and_filter_rules(const char * ifname, unsigned short eport,
|
||||
int proto);
|
||||
|
||||
int
|
||||
delete_filter_rule(const char * ifname, unsigned short port, int proto);
|
||||
|
||||
#ifdef TEST
|
||||
int
|
||||
clear_redirect_rules(void);
|
||||
|
|
|
@ -54,6 +54,12 @@ int getifaddr(const char * ifname, char * buf, int len, struct in_addr * addr, s
|
|||
return 0;
|
||||
}
|
||||
|
||||
int addr_is_reserved(struct in_addr * addr)
|
||||
{
|
||||
UNUSED(addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upnp_get_portmapping_number_of_entries(void)
|
||||
{
|
||||
return 42;
|
||||
|
|
|
@ -1281,8 +1281,9 @@ genEventVars(int * len, const struct serviceDesc * s)
|
|||
if(use_ext_ip_addr)
|
||||
str = strcat_str(str, len, &tmplen, use_ext_ip_addr);
|
||||
else {
|
||||
struct in_addr addr;
|
||||
char ext_ip_addr[INET_ADDRSTRLEN];
|
||||
if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN, NULL, NULL) < 0) {
|
||||
if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN, &addr, NULL) < 0 || addr_is_reserved(&addr)) {
|
||||
str = strcat_str(str, len, &tmplen, "0.0.0.0");
|
||||
} else {
|
||||
str = strcat_str(str, len, &tmplen, ext_ip_addr);
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
/* network interface for internet */
|
||||
const char * ext_if_name = 0;
|
||||
|
||||
/* stun host/port configuration */
|
||||
const char * ext_stun_host = 0;
|
||||
uint16_t ext_stun_port = 0;
|
||||
|
||||
/* file to store leases */
|
||||
#ifdef ENABLE_LEASEFILE
|
||||
const char* lease_file = 0;
|
||||
|
@ -25,6 +29,10 @@ const char* lease_file = 0;
|
|||
* when NULL, getifaddr() is used */
|
||||
const char * use_ext_ip_addr = 0;
|
||||
|
||||
/* disallow all port forwarding requests when
|
||||
* we are behind restrictive nat */
|
||||
int disable_port_forwarding = 0;
|
||||
|
||||
unsigned long downstream_bitrate = 0;
|
||||
unsigned long upstream_bitrate = 0;
|
||||
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
/* name of the network interface used to access internet */
|
||||
extern const char * ext_if_name;
|
||||
|
||||
/* stun host/port configuration */
|
||||
extern const char * ext_stun_host;
|
||||
extern uint16_t ext_stun_port;
|
||||
|
||||
/* file to store all leases */
|
||||
#ifdef ENABLE_LEASEFILE
|
||||
extern const char * lease_file;
|
||||
|
@ -26,6 +30,10 @@ extern const char * lease_file;
|
|||
* when NULL, getifaddr() is used */
|
||||
extern const char * use_ext_ip_addr;
|
||||
|
||||
/* disallow all port forwarding requests when
|
||||
* we are behind restrictive nat */
|
||||
extern int disable_port_forwarding;
|
||||
|
||||
/* parameters to return to upnp client when asked */
|
||||
extern unsigned long downstream_bitrate;
|
||||
extern unsigned long upstream_bitrate;
|
||||
|
@ -69,6 +77,8 @@ extern int runtime_flags;
|
|||
#define FORCEIGDDESCV1MASK 0x0800
|
||||
#endif
|
||||
|
||||
#define PERFORMSTUNMASK 0x1000
|
||||
|
||||
#define SETFLAG(mask) runtime_flags |= mask
|
||||
#define GETFLAG(mask) (runtime_flags & mask)
|
||||
#define CLEARFLAG(mask) runtime_flags &= ~mask
|
||||
|
|
|
@ -440,6 +440,8 @@ upnp_redirect_internal(const char * rhost, unsigned short eport,
|
|||
{
|
||||
/*syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
|
||||
eport, iaddr, iport, protocol, desc); */
|
||||
if(disable_port_forwarding)
|
||||
return -1;
|
||||
if(add_redirect_rule2(ext_if_name, rhost, eport, iaddr, iport, proto,
|
||||
desc, timestamp) < 0) {
|
||||
return -1;
|
||||
|
|
|
@ -332,17 +332,20 @@ GetExternalIPAddress(struct upnphttp * h, const char * action, const char * ns)
|
|||
* There is usually no NAT with IPv6 */
|
||||
|
||||
#ifndef MULTIPLE_EXTERNAL_IP
|
||||
struct in_addr addr;
|
||||
if(use_ext_ip_addr)
|
||||
{
|
||||
strncpy(ext_ip_addr, use_ext_ip_addr, INET_ADDRSTRLEN);
|
||||
ext_ip_addr[INET_ADDRSTRLEN - 1] = '\0';
|
||||
}
|
||||
else if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN, NULL, NULL) < 0)
|
||||
else if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN, &addr, NULL) < 0)
|
||||
{
|
||||
syslog(LOG_ERR, "Failed to get ip address for interface %s",
|
||||
ext_if_name);
|
||||
strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
|
||||
}
|
||||
if (addr_is_reserved(&addr))
|
||||
strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
|
||||
#else
|
||||
struct lan_addr_s * lan_addr;
|
||||
strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
|
||||
|
@ -356,6 +359,11 @@ GetExternalIPAddress(struct upnphttp * h, const char * action, const char * ns)
|
|||
}
|
||||
}
|
||||
#endif
|
||||
if (strcmp(ext_ip_addr, "0.0.0.0") == 0)
|
||||
{
|
||||
SoapError(h, 501, "Action Failed");
|
||||
return;
|
||||
}
|
||||
bodylen = snprintf(body, sizeof(body), resp,
|
||||
action, ns, /*SERVICE_TYPE_WANIPC,*/
|
||||
ext_ip_addr, action);
|
||||
|
|
|
@ -0,0 +1,467 @@
|
|||
/* $Id: $ */
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2018 Pali Rohár
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef TEST_LINUX_DEBUG_APP
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "upnpstun.h"
|
||||
|
||||
#if defined(USE_NETFILTER)
|
||||
#include "netfilter/iptcrdr.h"
|
||||
#endif
|
||||
#if defined(USE_PF)
|
||||
#include "pf/obsdrdr.h"
|
||||
#endif
|
||||
#if defined(USE_IPF)
|
||||
#include "ipf/ipfrdr.h"
|
||||
#endif
|
||||
#if defined(USE_IPFW)
|
||||
#include "ipfw/ipfwrdr.h"
|
||||
#endif
|
||||
|
||||
#ifdef TEST_LINUX_DEBUG_APP
|
||||
static int add_filter_rule2(const char *ifname, const char *rhost, const char *iaddr, unsigned short eport, unsigned short iport, int proto, const char *desc);
|
||||
static int delete_filter_rule(const char * ifname, unsigned short port, int proto);
|
||||
#endif
|
||||
|
||||
/* Generate random STUN Transaction Id */
|
||||
static void generate_transaction_id(unsigned char transaction_id[12])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 12; ++i)
|
||||
transaction_id[i] = random()%255;
|
||||
}
|
||||
|
||||
/* Create and fill STUN Binding Request */
|
||||
static void fill_request(unsigned char buffer[28], int change_ip, int change_port)
|
||||
{
|
||||
/* Type: Binding Request */
|
||||
buffer[0] = 0x00;
|
||||
buffer[1] = 0x01;
|
||||
|
||||
/* Length: One 8-byte attribute */
|
||||
buffer[2] = 0x00;
|
||||
buffer[3] = 0x08;
|
||||
|
||||
/* Magic Cookie: 0x2120A442 */
|
||||
buffer[4] = 0x21;
|
||||
buffer[5] = 0x12;
|
||||
buffer[6] = 0xA4;
|
||||
buffer[7] = 0x42;
|
||||
|
||||
/* Transaction Id */
|
||||
generate_transaction_id(buffer+8);
|
||||
|
||||
/* Attribute Type: Change Request */
|
||||
buffer[20] = 0x00;
|
||||
buffer[21] = 0x03;
|
||||
|
||||
/* Attribute Length: 4 bytes */
|
||||
buffer[22] = 0x00;
|
||||
buffer[23] = 0x04;
|
||||
|
||||
buffer[24] = 0x00;
|
||||
buffer[25] = 0x00;
|
||||
buffer[26] = 0x00;
|
||||
buffer[27] = 0x00;
|
||||
|
||||
/* Change IP */
|
||||
buffer[27] |= change_ip ? 0x4 : 0x00;
|
||||
|
||||
/* Change Port */
|
||||
buffer[27] |= change_port ? 0x2 : 0x00;
|
||||
}
|
||||
|
||||
/* Resolve STUN host+port and return sockaddr_in structure */
|
||||
/* When port is 0 then use default STUN port */
|
||||
static int resolve_stun_host(const char *stun_host, unsigned short stun_port, struct sockaddr_in *sock_addr)
|
||||
{
|
||||
int have_sock;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *result, *rp;
|
||||
char service[6];
|
||||
|
||||
if (stun_port == 0)
|
||||
stun_port = (unsigned short)3478;
|
||||
snprintf(service, sizeof(service), "%hu", stun_port);
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
|
||||
if (getaddrinfo(stun_host, service, &hints, &result) != 0) {
|
||||
errno = EHOSTUNREACH;
|
||||
return -1;
|
||||
}
|
||||
|
||||
have_sock = 0;
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
if (rp->ai_addrlen > sizeof(*sock_addr) || rp->ai_addr->sa_family != AF_INET)
|
||||
continue;
|
||||
memcpy(sock_addr, rp->ai_addr, rp->ai_addrlen);
|
||||
have_sock = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
if (!have_sock) {
|
||||
errno = EHOSTUNREACH;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a new UDP socket for STUN connection and return file descriptor and local UDP port */
|
||||
static int stun_socket(unsigned short *local_port)
|
||||
{
|
||||
int fd;
|
||||
socklen_t addr_len;
|
||||
struct sockaddr_in local_addr;
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
memset(&local_addr, 0, sizeof(local_addr));
|
||||
local_addr.sin_family = AF_INET;
|
||||
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
local_addr.sin_port = 0;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) != 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr_len = sizeof(local_addr);
|
||||
if (getsockname(fd, (struct sockaddr *)&local_addr, &addr_len) != 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*local_port = ntohs(local_addr.sin_port);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Receive STUN response message for specified Transaction Id and returns message and peer address */
|
||||
static size_t receive_stun_response(int fd, unsigned char *buffer, unsigned char transaction_id[12], size_t buffer_len, struct sockaddr_in *peer_addr)
|
||||
{
|
||||
ssize_t len;
|
||||
socklen_t peer_addr_len = sizeof(*peer_addr);
|
||||
|
||||
len = recvfrom(fd, buffer, buffer_len, 0, (struct sockaddr *)peer_addr, &peer_addr_len);
|
||||
if (len < 20 || peer_addr_len != sizeof(*peer_addr))
|
||||
return 0;
|
||||
|
||||
/* Check that buffer is STUN message with class Response and Binding method with transaction id */
|
||||
if ((buffer[0] & 0xFF) != 0x01 || (buffer[1] & 0xEF) != 0x01 || memcmp(buffer+8, transaction_id, 12) != 0)
|
||||
return 0;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Wait for STUN response messages and try to receive them */
|
||||
static int wait_for_stun_responses(int fds[4], unsigned char *transaction_ids[4], unsigned char *buffers[4], size_t buffers_lens[4], struct sockaddr_in peer_addrs[4], size_t lens[4])
|
||||
{
|
||||
fd_set fdset;
|
||||
struct timeval timeout;
|
||||
int max_fd;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
max_fd = fds[0];
|
||||
for (i = 1; i < 4; i++) {
|
||||
if (fds[i] > max_fd)
|
||||
max_fd = fds[i];
|
||||
}
|
||||
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
while (timeout.tv_sec > 0 || timeout.tv_usec > 0) {
|
||||
|
||||
FD_ZERO(&fdset);
|
||||
for (i = 0; i < 4; i++) {
|
||||
FD_SET(fds[i], &fdset);
|
||||
}
|
||||
|
||||
ret = select(max_fd+1, &fdset, NULL, NULL, &timeout);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
if (FD_ISSET(fds[i], &fdset))
|
||||
lens[i] = receive_stun_response(fds[i], buffers[i], transaction_ids[i], buffers_lens[i], &peer_addrs[i]);
|
||||
|
||||
if (lens[0] && lens[1] && lens[2] && lens[3])
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse Mapped Address (with port) from STUN response message */
|
||||
static int parse_stun_response(unsigned char *buffer, size_t len, struct sockaddr_in *mapped_addr)
|
||||
{
|
||||
unsigned char *ptr, *end;
|
||||
uint16_t attr_type;
|
||||
uint16_t attr_len;
|
||||
int have_address;
|
||||
|
||||
if (len < 20)
|
||||
return -1;
|
||||
|
||||
/* Check that buffer is STUN message with class Success Response and Binding method */
|
||||
if (buffer[0] != 0x01 || buffer[1] != 0x01)
|
||||
return -1;
|
||||
|
||||
/* Check that STUN message is not longer as buffer length */
|
||||
if (((size_t)buffer[2] << 8) + buffer[3] + 20 > len)
|
||||
return -1;
|
||||
|
||||
ptr = buffer + 20;
|
||||
end = buffer + len;
|
||||
have_address = 0;
|
||||
|
||||
while (ptr + 4 <= end) {
|
||||
|
||||
attr_type = ((uint16_t)ptr[0] << 8) + ptr[1];
|
||||
attr_len = ((uint16_t)ptr[2] << 8) + ptr[3];
|
||||
ptr += 4;
|
||||
|
||||
if (ptr + attr_len > end)
|
||||
break;
|
||||
|
||||
if (attr_type == 0x0001 || attr_type == 0x8020) {
|
||||
/* Mapped Address or XOR Mapped Address */
|
||||
if (attr_len == 8 && ptr[1] == 1) {
|
||||
/* IPv4 address */
|
||||
if (attr_type == 0x8020) {
|
||||
/* Restore XOR Mapped Address */
|
||||
ptr[2] ^= buffer[4];
|
||||
ptr[3] ^= buffer[5];
|
||||
ptr[4] ^= buffer[4];
|
||||
ptr[5] ^= buffer[5];
|
||||
ptr[6] ^= buffer[6];
|
||||
ptr[7] ^= buffer[7];
|
||||
}
|
||||
|
||||
mapped_addr->sin_family = AF_INET;
|
||||
mapped_addr->sin_port = htons(((uint16_t)ptr[2] << 8) + ptr[3]);
|
||||
mapped_addr->sin_addr.s_addr = htonl(((uint32_t)ptr[4] << 24) + (ptr[5] << 16) + (ptr[6] << 8) + ptr[7]);
|
||||
|
||||
/* Prefer XOR Mapped Address, some NATs change IP addresses in UDP packets */
|
||||
if (attr_type == 0x8020)
|
||||
return 0;
|
||||
|
||||
have_address = 1;
|
||||
}
|
||||
}
|
||||
|
||||
ptr += attr_len;
|
||||
}
|
||||
|
||||
return have_address ? 0 : -1;
|
||||
}
|
||||
|
||||
/* Perform main STUN operation, return external IP address and check if host is behind restrictive NAT */
|
||||
/* Restrictive NAT means any NAT which do some filtering and which is not static 1:1, basically NAT which is not usable for port forwarding */
|
||||
int perform_stun(const char *if_name, const char *if_addr, const char *stun_host, unsigned short stun_port, struct in_addr *ext_addr, int *restrictive_nat)
|
||||
{
|
||||
int fds[4];
|
||||
size_t responses_lens[4];
|
||||
unsigned char responses_bufs[4][1024];
|
||||
unsigned char *responses[4];
|
||||
size_t responses_sizes[4];
|
||||
unsigned char requests[4][28];
|
||||
unsigned char *transaction_ids[4];
|
||||
int have_mapped_addrs[4];
|
||||
struct sockaddr_in remote_addr, peer_addrs[4], mapped_addrs[4];
|
||||
unsigned short local_ports[4];
|
||||
int have_ext_addr;
|
||||
int i, j;
|
||||
|
||||
if (resolve_stun_host(stun_host, stun_port, &remote_addr) != 0)
|
||||
return -1;
|
||||
|
||||
/* Prepare four different STUN requests */
|
||||
for (i = 0; i < 4; ++i) {
|
||||
|
||||
responses_lens[i] = 0;
|
||||
responses[i] = responses_bufs[i];
|
||||
responses_sizes[i] = sizeof(responses_bufs[i]);
|
||||
|
||||
fds[i] = stun_socket(&local_ports[i]);
|
||||
if (fds[i] < 0) {
|
||||
for (j = 0; j < i; ++j)
|
||||
close(fds[j]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fill_request(requests[i], i/2, i%2);
|
||||
transaction_ids[i] = requests[i]+8;
|
||||
|
||||
}
|
||||
|
||||
/* Unblock local ports */
|
||||
for (i = 0; i < 4; ++i)
|
||||
add_filter_rule2(if_name, NULL, if_addr, local_ports[i], local_ports[i], IPPROTO_UDP, "stun test");
|
||||
|
||||
/* Send STUN requests and wait for responses */
|
||||
for (j = 0; j < 3; ++j) {
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (responses_lens[i])
|
||||
continue;
|
||||
if (sendto(fds[i], requests[i], sizeof(requests[i]), 0, (struct sockaddr *)&remote_addr, sizeof(remote_addr)) != sizeof(requests[i]))
|
||||
break;
|
||||
}
|
||||
|
||||
if (wait_for_stun_responses(fds, transaction_ids, responses, responses_sizes, peer_addrs, responses_lens) != 0)
|
||||
break;
|
||||
|
||||
if (responses_lens[0] && responses_lens[1] && responses_lens[2] && responses_lens[3])
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
/* Remove unblock for local ports */
|
||||
for (i = 0; i < 4; ++i) {
|
||||
delete_filter_rule(if_name, local_ports[i], IPPROTO_UDP);
|
||||
close(fds[i]);
|
||||
}
|
||||
|
||||
/* Parse received STUN messages */
|
||||
have_ext_addr = 0;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (parse_stun_response(responses[i], responses_lens[i], &mapped_addrs[i]) == 0)
|
||||
have_mapped_addrs[i] = 1;
|
||||
else
|
||||
have_mapped_addrs[i] = 0;
|
||||
if (!have_ext_addr && have_mapped_addrs[i]) {
|
||||
memcpy(ext_addr, &mapped_addrs[i].sin_addr, sizeof(*ext_addr));
|
||||
have_ext_addr = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* We have no external address */
|
||||
if (!have_ext_addr) {
|
||||
errno = ENXIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (!have_mapped_addrs[i]) {
|
||||
/* We have not received all four responses, therefore NAT or firewall is doing some filtering */
|
||||
*restrictive_nat = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (memcmp(&remote_addr, &peer_addrs[0], sizeof(peer_addrs[0])) != 0) {
|
||||
/* We received STUN response from different address even we did not asked for it, so some strange NAT is active */
|
||||
*restrictive_nat = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (ntohs(mapped_addrs[i].sin_port) != local_ports[i] || memcmp(&mapped_addrs[i].sin_addr, ext_addr, sizeof(*ext_addr)) != 0) {
|
||||
/* External IP address or port was changed, therefore symmetric NAT is active */
|
||||
*restrictive_nat = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise we are either directly connected or behind unrestricted NAT 1:1 */
|
||||
/* There is no filtering, so port forwarding would work fine */
|
||||
*restrictive_nat = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef TEST_LINUX_DEBUG_APP
|
||||
|
||||
/* This linux test application for debugging purposes can be compiled as: */
|
||||
/* gcc upnpstun.c -o upnpstun -g3 -W -Wall -O2 -DTEST_LINUX_DEBUG_APP */
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <time.h>
|
||||
|
||||
static int add_filter_rule2(const char *ifname, const char *rhost, const char *iaddr, unsigned short eport, unsigned short iport, int proto, const char *desc)
|
||||
{
|
||||
char buffer[100];
|
||||
ifname = ifname;
|
||||
rhost = rhost;
|
||||
iaddr = iaddr;
|
||||
iport = iport;
|
||||
desc = desc;
|
||||
snprintf(buffer, sizeof(buffer), "/sbin/iptables -t filter -I INPUT -p %d --dport %hu -j ACCEPT", proto, eport);
|
||||
printf("Executing: %s\n", buffer);
|
||||
return system(buffer);
|
||||
}
|
||||
|
||||
static int delete_filter_rule(const char * ifname, unsigned short port, int proto)
|
||||
{
|
||||
char buffer[100];
|
||||
ifname = ifname;
|
||||
snprintf(buffer, sizeof(buffer), "/sbin/iptables -t filter -D INPUT -p %d --dport %hu -j ACCEPT", proto, port);
|
||||
printf("Executing: %s\n", buffer);
|
||||
return system(buffer);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct in_addr ext_addr;
|
||||
int restrictive_nat;
|
||||
int ret;
|
||||
char str[INET_ADDRSTRLEN];
|
||||
|
||||
if (argc != 3 && argc != 2) {
|
||||
printf("Usage: %s stun_host [stun_port]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc == 2)
|
||||
argv[2] = "0";
|
||||
|
||||
srandom(time(NULL) * getpid());
|
||||
|
||||
ret = perform_stun(NULL, NULL, argv[1], atoi(argv[2]), &ext_addr, &restrictive_nat);
|
||||
if (ret != 0) {
|
||||
printf("STUN Failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!inet_ntop(AF_INET, &ext_addr, str, INET_ADDRSTRLEN))
|
||||
str[0] = 0;
|
||||
|
||||
printf("External IP address: %s\n", str);
|
||||
printf("Restrictive NAT: %s\n", restrictive_nat ? "active (port forwarding impossible)" : "not used (ready for port forwarding)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,12 @@
|
|||
/* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2018 Pali Rohár
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
|
||||
#ifndef UPNPSTUN_H_INCLUDED
|
||||
#define UPNPSTUN_H_INCLUDED
|
||||
|
||||
int perform_stun(const char *if_name, const char *if_addr, const char *stun_host, unsigned short stun_port, struct in_addr *ext_addr, int *restrictive_nat);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue