miniupnpc: Update to version 2.2.2

This commit is contained in:
Rémi Verschelde 2021-03-14 12:07:28 +01:00
parent 245cfcaba6
commit 69486b1059
No known key found for this signature in database
GPG Key ID: C3336907360768E1
23 changed files with 386 additions and 2069 deletions

View File

@ -269,7 +269,7 @@ License: CC0-1.0
Files: ./thirdparty/miniupnpc/
Comment: MiniUPnPc
Copyright: 2005-2019, Thomas Bernard
Copyright: 2005-2021, Thomas Bernard
License: BSD-3-clause
Files: ./thirdparty/minizip/

View File

@ -12,18 +12,19 @@ thirdparty_obj = []
if env["builtin_miniupnpc"]:
thirdparty_dir = "#thirdparty/miniupnpc/"
thirdparty_sources = [
"miniupnpc.c",
"upnpcommands.c",
"miniwget.c",
"upnpdev.c",
"igd_desc_parse.c",
"minissdpc.c",
"minisoap.c",
"miniupnpc.c",
"minixml.c",
"connecthostport.c",
"receivedata.c",
"portlistingparse.c",
"minisoap.c",
"minissdpc.c",
"miniwget.c",
"upnpcommands.c",
"upnpdev.c",
"upnpreplyparse.c",
"connecthostport.c",
"portlistingparse.c",
"receivedata.c",
"addr_is_reserved.c",
]
thirdparty_sources = [thirdparty_dir + "miniupnpc/" + file for file in thirdparty_sources]

13
thirdparty/README.md vendored
View File

@ -355,17 +355,20 @@ Files extracted from upstream repository:
## miniupnpc
- Upstream: https://github.com/miniupnp/miniupnp/tree/master/miniupnpc
- Version: git (44366328661826603982d1e0d7ebb4062c5f2bfc, 2020)
- Upstream: https://github.com/miniupnp/miniupnp
- Version: 2.2.2 (81029a860baf1f727903e5b85307903b3f40cbc8, 2021)
- License: BSD-3-Clause
Files extracted from upstream source:
- All `*.c` and `*.h` files from `miniupnpc` to `thirdparty/miniupnpc/miniupnpc`
- Remove `test*`, `minihttptestserver.c` and `wingenminiupnpcstrings.c`
- Remove the following test or sample files:
`listdevices.c minihttptestserver.c miniupnpcmodule.c upnpc.c upnperrors.* test* wingenminiupnpcstrings.c`
- `LICENSE`
The only modified file is miniupnpcstrings.h, which was created for Godot
(it is usually autogenerated by cmake).
The only modified file is `miniupnpcstrings.h`, which was created for Godot
(it is usually autogenerated by cmake). Bump the version number for miniupnpc in that
file when upgrading.
## minizip

View File

@ -1,5 +1,5 @@
MiniUPnP Project
Copyright (c) 2005-2019, Thomas BERNARD
MiniUPnPc
Copyright (c) 2005-2020, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -24,3 +24,4 @@ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,79 @@
/* $Id: addr_is_reserved.c,v 1.4 2021/03/02 23:40:32 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas BERNARD
* copyright (c) 2005-2021 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENSE file. */
#ifdef _WIN32
/* Win32 Specific includes and defines */
#include <winsock2.h>
#include <ws2tcpip.h>
#if !defined(_MSC_VER)
#include <stdint.h>
#else /* !defined(_MSC_VER) */
typedef unsigned long uint32_t;
#endif /* !defined(_MSC_VER) */
#else /* _WIN32 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif /* _WIN32 */
/* List of IP address blocks which are private / reserved and therefore not suitable for public external IP addresses */
#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(24) }, /* 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
/**
* @return 1 or 0
*/
int addr_is_reserved(const char * addr_str)
{
uint32_t addr_n, address;
size_t i;
#if defined(_WIN32) && (!defined(_WIN32_WINNT_VISTA) || (_WIN32_WINNT < _WIN32_WINNT_VISTA))
addr_n = inet_addr(addr_str);
if (addr_n == INADDR_NONE)
return 1;
#else
/* was : addr_n = inet_addr(addr_str); */
if (inet_pton(AF_INET, addr_str, &addr_n) <= 0) {
/* error */
return 1;
}
#endif
address = ntohl(addr_n);
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;
}

View File

@ -0,0 +1,14 @@
/* $Id: $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project: miniupnp
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author: Thomas Bernard
* Copyright (c) 2005-2020 Thomas Bernard
* This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */
#ifndef ADDR_IS_RESERVED_H_INCLUDED
#define ADDR_IS_RESERVED_H_INCLUDED
int addr_is_reserved(const char * addr_str);
#endif /* ADDR_IS_RESERVED_H_INCLUDED */

View File

@ -1,8 +1,8 @@
/* $Id: connecthostport.c,v 1.22 2019/10/13 17:22:08 nanard Exp $ */
/* $Id: connecthostport.c,v 1.24 2020/11/09 19:26:53 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2010-2019 Thomas Bernard
* Copyright (c) 2010-2020 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@ -19,7 +19,7 @@
#include <ws2tcpip.h>
#include <io.h>
#define MAXHOSTNAMELEN 64
#define snprintf _snprintf
#include "win32_snprintf.h"
#define herror
#define socklen_t int
#else /* #ifdef _WIN32 */

View File

@ -1,197 +0,0 @@
/* $Id: listdevices.c,v 1.6 2015/07/23 20:40:08 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2013-2015 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#include <winsock2.h>
#endif /* _WIN32 */
#include "miniupnpc.h"
struct upnp_dev_list {
struct upnp_dev_list * next;
char * descURL;
struct UPNPDev * * array;
size_t count;
size_t allocated_count;
};
#define ADD_DEVICE_COUNT_STEP 16
void add_device(struct upnp_dev_list * * list_head, struct UPNPDev * dev)
{
struct upnp_dev_list * elt;
size_t i;
if(dev == NULL)
return;
for(elt = *list_head; elt != NULL; elt = elt->next) {
if(strcmp(elt->descURL, dev->descURL) == 0) {
for(i = 0; i < elt->count; i++) {
if (strcmp(elt->array[i]->st, dev->st) == 0 && strcmp(elt->array[i]->usn, dev->usn) == 0) {
return; /* already found */
}
}
if(elt->count >= elt->allocated_count) {
struct UPNPDev * * tmp;
elt->allocated_count += ADD_DEVICE_COUNT_STEP;
tmp = realloc(elt->array, elt->allocated_count * sizeof(struct UPNPDev *));
if(tmp == NULL) {
fprintf(stderr, "Failed to realloc(%p, %lu)\n", elt->array, (unsigned long)(elt->allocated_count * sizeof(struct UPNPDev *)));
return;
}
elt->array = tmp;
}
elt->array[elt->count++] = dev;
return;
}
}
elt = malloc(sizeof(struct upnp_dev_list));
if(elt == NULL) {
fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)sizeof(struct upnp_dev_list));
return;
}
elt->next = *list_head;
elt->descURL = strdup(dev->descURL);
if(elt->descURL == NULL) {
fprintf(stderr, "Failed to strdup(%s)\n", dev->descURL);
free(elt);
return;
}
elt->allocated_count = ADD_DEVICE_COUNT_STEP;
elt->array = malloc(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *));
if(elt->array == NULL) {
fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *)));
free(elt->descURL);
free(elt);
return;
}
elt->array[0] = dev;
elt->count = 1;
*list_head = elt;
}
void free_device(struct upnp_dev_list * elt)
{
free(elt->descURL);
free(elt->array);
free(elt);
}
int main(int argc, char * * argv)
{
const char * searched_device = NULL;
const char * * searched_devices = NULL;
const char * multicastif = 0;
const char * minissdpdpath = 0;
int ipv6 = 0;
unsigned char ttl = 2;
int error = 0;
struct UPNPDev * devlist = 0;
struct UPNPDev * dev;
struct upnp_dev_list * sorted_list = NULL;
struct upnp_dev_list * dev_array;
int i;
#ifdef _WIN32
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(nResult != NO_ERROR)
{
fprintf(stderr, "WSAStartup() failed.\n");
return -1;
}
#endif
for(i = 1; i < argc; i++) {
if(strcmp(argv[i], "-6") == 0)
ipv6 = 1;
else if(strcmp(argv[i], "-d") == 0) {
if(++i >= argc) {
fprintf(stderr, "%s option needs one argument\n", "-d");
return 1;
}
searched_device = argv[i];
} else if(strcmp(argv[i], "-t") == 0) {
if(++i >= argc) {
fprintf(stderr, "%s option needs one argument\n", "-t");
return 1;
}
ttl = (unsigned char)atoi(argv[i]);
} else if(strcmp(argv[i], "-l") == 0) {
if(++i >= argc) {
fprintf(stderr, "-l option needs at least one argument\n");
return 1;
}
searched_devices = (const char * *)(argv + i);
break;
} else if(strcmp(argv[i], "-m") == 0) {
if(++i >= argc) {
fprintf(stderr, "-m option needs one argument\n");
return 1;
}
multicastif = argv[i];
} else {
printf("usage : %s [options] [-l <device1> <device2> ...]\n", argv[0]);
printf("options :\n");
printf(" -6 : use IPv6\n");
printf(" -m address/ifname : network interface to use for multicast\n");
printf(" -d <device string> : search only for this type of device\n");
printf(" -l <device1> <device2> ... : search only for theses types of device\n");
printf(" -t ttl : set multicast TTL. Default value is 2.\n");
printf(" -h : this help\n");
return 1;
}
}
if(searched_device) {
printf("searching UPnP device type %s\n", searched_device);
devlist = upnpDiscoverDevice(searched_device,
2000, multicastif, minissdpdpath,
0/*localport*/, ipv6, ttl, &error);
} else if(searched_devices) {
printf("searching UPnP device types :\n");
for(i = 0; searched_devices[i]; i++)
printf("\t%s\n", searched_devices[i]);
devlist = upnpDiscoverDevices(searched_devices,
2000, multicastif, minissdpdpath,
0/*localport*/, ipv6, ttl, &error, 1);
} else {
printf("searching all UPnP devices\n");
devlist = upnpDiscoverAll(2000, multicastif, minissdpdpath,
0/*localport*/, ipv6, ttl, &error);
}
if(devlist) {
for(dev = devlist, i = 1; dev != NULL; dev = dev->pNext, i++) {
printf("%3d: %-48s\n", i, dev->st);
printf(" %s\n", dev->descURL);
printf(" %s\n", dev->usn);
add_device(&sorted_list, dev);
}
putchar('\n');
for (dev_array = sorted_list; dev_array != NULL ; dev_array = dev_array->next) {
printf("%s :\n", dev_array->descURL);
for(i = 0; (unsigned)i < dev_array->count; i++) {
printf("%2d: %s\n", i+1, dev_array->array[i]->st);
printf(" %s\n", dev_array->array[i]->usn);
}
putchar('\n');
}
freeUPNPDevlist(devlist);
while(sorted_list != NULL) {
dev_array = sorted_list;
sorted_list = sorted_list->next;
free_device(dev_array);
}
} else {
printf("no device found.\n");
}
return 0;
}

View File

@ -1,8 +1,8 @@
/* $Id: minisoap.c,v 1.25 2017/04/21 10:03:24 nanard Exp $ */
/* $Id: minisoap.c,v 1.30 2020/11/09 19:27:42 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2005-2018 Thomas Bernard
* Copyright (c) 2005-2020 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
*
@ -13,7 +13,7 @@
#ifdef _WIN32
#include <io.h>
#include <winsock2.h>
#define snprintf _snprintf
#include "win32_snprintf.h"
#else
#include <unistd.h>
#include <sys/types.h>

View File

@ -1,15 +1,15 @@
/* $Id: minissdpc.c,v 1.40 2019/04/23 12:12:55 nanard Exp $ */
/* $Id: minissdpc.c,v 1.47 2021/03/02 23:38:30 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Web : http://miniupnp.free.fr/
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas BERNARD
* copyright (c) 2005-2019 Thomas Bernard
* copyright (c) 2005-2021 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
/*#include <syslog.h>*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#if defined (__NetBSD__)
#include <net/if.h>
@ -20,7 +20,7 @@
#include <ws2tcpip.h>
#include <io.h>
#include <iphlpapi.h>
#define snprintf _snprintf
#include "win32_snprintf.h"
#if !defined(_MSC_VER)
#include <stdint.h>
#else /* !defined(_MSC_VER) */
@ -33,6 +33,12 @@ typedef unsigned short uint16_t;
#define strncasecmp memicmp
#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
#endif /* #ifndef strncasecmp */
#if defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PARTITION)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP
#define in6addr_any in6addr_any_init
static const IN6_ADDR in6addr_any_init = {0};
#endif
#endif
#endif /* _WIN32 */
#if defined(__amigaos__) || defined(__amigaos4__)
#include <sys/socket.h>
@ -66,7 +72,7 @@ struct sockaddr_un {
#define HAS_IP_MREQN
#endif
#if !defined(HAS_IP_MREQN) && !defined(_WIN32)
#ifndef _WIN32
#include <sys/ioctl.h>
#if defined(__sun) || defined(__HAIKU__)
#include <sys/sockio.h>
@ -445,6 +451,36 @@ parseMSEARCHReply(const char * reply, int size,
}
}
#if defined(CLOCK_MONOTONIC_FAST)
#define UPNP_CLOCKID CLOCK_MONOTONIC_FAST
#elif defined(CLOCK_MONOTONIC)
#define UPNP_CLOCKID CLOCK_MONOTONIC
#endif
static int upnp_gettimeofday(struct timeval * tv)
{
#if defined(_WIN32)
#if defined(_WIN32_WINNT_VISTA) && (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
ULONGLONG ts = GetTickCount64();
#else
DWORD ts = GetTickCount();
#endif
tv->tv_sec = (long)(ts / 1000);
tv->tv_usec = (ts % 1000) * 1000;
return 0; /* success */
#elif defined(CLOCK_MONOTONIC_FAST) || defined(CLOCK_MONOTONIC)
struct timespec ts;
int ret_code = clock_gettime(UPNP_CLOCKID, &ts);
if (ret_code == 0)
{
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec / 1000;
}
return ret_code;
#else
return gettimeofday(tv, NULL);
#endif
}
/* port upnp discover : SSDP protocol */
#define SSDP_PORT 1900
#define XSTR(s) STR(s)
@ -540,12 +576,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
* in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
if(!ipv6) {
DWORD ifbestidx;
SOCKADDR_IN destAddr;
memset(&destAddr, 0, sizeof(destAddr));
destAddr.sin_family = AF_INET;
destAddr.sin_addr.s_addr = inet_addr("223.255.255.255");
destAddr.sin_port = 0;
if (GetBestInterfaceEx((struct sockaddr *)&destAddr, &ifbestidx) == NO_ERROR) {
if (GetBestInterface(inet_addr("223.255.255.255"), &ifbestidx) == NO_ERROR) {
DWORD dwRetVal = NO_ERROR;
PIP_ADAPTER_ADDRESSES pAddresses = NULL;
ULONG outBufLen = 15360;
@ -672,6 +703,13 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
* MS Windows Vista and MS Windows Server 2008.
* http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
if(ifindex == 0)
{
if(error)
*error = MINISSDPC_INVALID_INPUT;
fprintf(stderr, "Invalid multicast interface name %s\n", multicastif);
goto error;
}
if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
{
PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF");
@ -683,10 +721,17 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
#endif
} else {
struct in_addr mc_if;
#if defined(_WIN32) && (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
#if defined(_WIN32)
#if defined(_WIN32_WINNT_VISTA) && (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
InetPtonA(AF_INET, multicastif, &mc_if);
#else
mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
mc_if.s_addr = inet_addr(multicastif); /* old Windows SDK do not support InetPtoA() */
#endif
#else
/* was : mc_if.s_addr = inet_addr(multicastif); */ /* ex: 192.168.x.x */
if (inet_pton(AF_INET, multicastif, &mc_if.s_addr) <= 0) {
mc_if.s_addr = INADDR_NONE;
}
#endif
if(mc_if.s_addr != INADDR_NONE)
{
@ -696,16 +741,11 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
}
} else {
#ifdef HAS_IP_MREQN
/* was not an ip address, try with an interface name */
#ifndef _WIN32
#ifdef HAS_IP_MREQN
struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
memset(&reqn, 0, sizeof(struct ip_mreqn));
reqn.imr_ifindex = if_nametoindex(multicastif);
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
{
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
}
#elif !defined(_WIN32)
#endif
struct ifreq ifr;
int ifrlen = sizeof(ifr);
strncpy(ifr.ifr_name, multicastif, IFNAMSIZ);
@ -713,12 +753,30 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0)
{
PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)");
goto error;
}
mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
#ifdef HAS_IP_MREQN
memset(&reqn, 0, sizeof(struct ip_mreqn));
reqn.imr_address.s_addr = mc_if.s_addr;
reqn.imr_ifindex = if_nametoindex(multicastif);
if(reqn.imr_ifindex == 0)
{
if(error)
*error = MINISSDPC_INVALID_INPUT;
fprintf(stderr, "Invalid multicast ip address / interface name %s\n", multicastif);
goto error;
}
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
{
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
}
#else
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
{
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
}
#endif
#else /* _WIN32 */
#ifdef DEBUG
printf("Setting of multicast interface not supported with interface name.\n");
@ -842,73 +900,84 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
/* Waiting for SSDP REPLY packet to M-SEARCH
* if searchalltypes is set, enter the loop only
* when the last deviceType is reached */
if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) do {
n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
if (n < 0) {
/* error */
if(error)
*error = MINISSDPC_SOCKET_ERROR;
goto error;
} else if (n == 0) {
/* no data or Time Out */
#ifdef DEBUG
printf("NODATA or TIMEOUT\n");
#endif /* DEBUG */
if (devlist && !searchalltypes) {
/* found some devices, stop now*/
if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) {
struct timeval start = {0, 0}, current = {0, 0};
upnp_gettimeofday(&start);
do {
n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
if (n < 0) {
/* error */
if(error)
*error = MINISSDPC_SUCCESS;
*error = MINISSDPC_SOCKET_ERROR;
goto error;
}
} else {
const char * descURL=NULL;
int urlsize=0;
const char * st=NULL;
int stsize=0;
const char * usn=NULL;
int usnsize=0;
parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize);
if(st&&descURL) {
} else if (n == 0) {
/* no data or Time Out */
#ifdef DEBUG
printf("M-SEARCH Reply:\n ST: %.*s\n USN: %.*s\n Location: %.*s\n",
stsize, st, usnsize, (usn?usn:""), urlsize, descURL);
printf("NODATA or TIMEOUT\n");
#endif /* DEBUG */
for(tmp=devlist; tmp; tmp = tmp->pNext) {
if(strncmp(tmp->descURL, descURL, urlsize) == 0 &&
tmp->descURL[urlsize] == '\0' &&
strncmp(tmp->st, st, stsize) == 0 &&
tmp->st[stsize] == '\0' &&
(usnsize == 0 || strncmp(tmp->usn, usn, usnsize) == 0) &&
tmp->usn[usnsize] == '\0')
break;
}
/* at the exit of the loop above, tmp is null if
* no duplicate device was found */
if(tmp)
continue;
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
if(!tmp) {
/* memory allocation error */
if (devlist && !searchalltypes) {
/* found some devices, stop now*/
if(error)
*error = MINISSDPC_MEMORY_ERROR;
*error = MINISSDPC_SUCCESS;
goto error;
}
tmp->pNext = devlist;
tmp->descURL = tmp->buffer;
tmp->st = tmp->buffer + 1 + urlsize;
tmp->usn = tmp->st + 1 + stsize;
memcpy(tmp->buffer, descURL, urlsize);
tmp->buffer[urlsize] = '\0';
memcpy(tmp->st, st, stsize);
tmp->buffer[urlsize+1+stsize] = '\0';
if(usn != NULL)
memcpy(tmp->usn, usn, usnsize);
tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
tmp->scope_id = scope_id;
devlist = tmp;
} else {
const char * descURL=NULL;
int urlsize=0;
const char * st=NULL;
int stsize=0;
const char * usn=NULL;
int usnsize=0;
parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize);
if(st&&descURL) {
#ifdef DEBUG
printf("M-SEARCH Reply:\n ST: %.*s\n USN: %.*s\n Location: %.*s\n",
stsize, st, usnsize, (usn?usn:""), urlsize, descURL);
#endif /* DEBUG */
for(tmp=devlist; tmp; tmp = tmp->pNext) {
if(strncmp(tmp->descURL, descURL, urlsize) == 0 &&
tmp->descURL[urlsize] == '\0' &&
strncmp(tmp->st, st, stsize) == 0 &&
tmp->st[stsize] == '\0' &&
(usnsize == 0 || strncmp(tmp->usn, usn, usnsize) == 0) &&
tmp->usn[usnsize] == '\0')
break;
}
/* at the exit of the loop above, tmp is null if
* no duplicate device was found */
if(tmp)
continue;
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3);
if(!tmp) {
/* memory allocation error */
if(error)
*error = MINISSDPC_MEMORY_ERROR;
goto error;
}
tmp->pNext = devlist;
tmp->descURL = tmp->buffer;
tmp->st = tmp->buffer + 1 + urlsize;
tmp->usn = tmp->st + 1 + stsize;
memcpy(tmp->buffer, descURL, urlsize);
tmp->buffer[urlsize] = '\0';
memcpy(tmp->st, st, stsize);
tmp->buffer[urlsize+1+stsize] = '\0';
if(usn != NULL)
memcpy(tmp->usn, usn, usnsize);
tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
tmp->scope_id = scope_id;
devlist = tmp;
}
if (upnp_gettimeofday(&current) >= 0) {
/* exit the loop if delay is reached */
long interval = (current.tv_sec - start.tv_sec) * 1000;
interval += (current.tv_usec - start.tv_usec) / 1000;
if (interval > (long)delay)
break;
}
}
}
} while(n > 0);
} while(n > 0);
}
if(ipv6) {
/* switch linklocal flag */
if(linklocal) {
@ -923,4 +992,3 @@ error:
closesocket(sudp);
return devlist;
}

View File

@ -1,9 +1,9 @@
/* $Id: miniupnpc.c,v 1.154 2019/04/23 12:12:13 nanard Exp $ */
/* $Id: miniupnpc.c,v 1.159 2021/03/02 23:36:32 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Web : http://miniupnp.free.fr/
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas BERNARD
* copyright (c) 2005-2019 Thomas Bernard
* copyright (c) 2005-2021 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENSE file. */
#include <stdlib.h>
@ -15,7 +15,7 @@
#include <ws2tcpip.h>
#include <io.h>
#include <iphlpapi.h>
#define snprintf _snprintf
#include "win32_snprintf.h"
#define strdup _strdup
#ifndef strncasecmp
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
@ -61,6 +61,7 @@
#include "minixml.h"
#include "upnpcommands.h"
#include "connecthostport.h"
#include "addr_is_reserved.h"
/* compare the beginning of a string with a constant string */
#define COMPARE(str, cstr) (0==strncmp(str, cstr, sizeof(cstr) - 1))
@ -73,24 +74,6 @@
#define SERVICEPREFIX "u"
#define SERVICEPREFIX2 'u'
/* check if an ip address is a private (LAN) address
* see https://tools.ietf.org/html/rfc1918 */
static int is_rfc1918addr(const char * addr)
{
/* 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) */
if(COMPARE(addr, "192.168."))
return 1;
/* 10.0.0.0 - 10.255.255.255 (10/8 prefix) */
if(COMPARE(addr, "10."))
return 1;
/* 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) */
if(COMPARE(addr, "172.")) {
if((atoi(addr + 4) | 0x0f) == 0x1f)
return 1;
}
return 0;
}
/* root description parsing */
MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
{
@ -337,6 +320,8 @@ upnpDiscoverDevices(const char * const deviceTypes[],
return devlist;
}
}
#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
(void)minissdpdsock; /* unused */
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
/* direct discovery if minissdpd responses are not sufficient */
@ -643,8 +628,7 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
/* checks that status is connected AND there is a external IP address assigned */
if(is_connected &&
(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
&& (0 != strcmp(extIpAddr, "0.0.0.0")))
if(!addr_is_reserved(extIpAddr))
goto free_and_return;
}
FreeUPNPUrls(urls);
@ -665,8 +649,7 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
#endif
if(is_connected &&
(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
&& (0 != strcmp(extIpAddr, "0.0.0.0")))
if(!addr_is_reserved(extIpAddr))
goto free_and_return;
}
FreeUPNPUrls(urls);

View File

@ -1,9 +1,9 @@
/* $Id: miniupnpc.h,v 1.53 2018/05/07 11:05:16 nanard Exp $ */
/* $Id: miniupnpc.h,v 1.58 2021/03/02 23:49:52 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project: miniupnp
* http://miniupnp.free.fr/
* Author: Thomas Bernard
* Copyright (c) 2005-2018 Thomas Bernard
* Copyright (c) 2005-2021 Thomas Bernard
* This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */
#ifndef MINIUPNPC_H_INCLUDED
@ -20,7 +20,7 @@
#define UPNPDISCOVER_MEMORY_ERROR (-102)
/* versions : */
#define MINIUPNPC_VERSION "2.1"
#define MINIUPNPC_VERSION "2.2.2"
#define MINIUPNPC_API_VERSION 17
/* Source port:

View File

@ -7,7 +7,7 @@
#ifndef MINIUPNPC_SOCKETDEF_H_INCLUDED
#define MINIUPNPC_SOCKETDEF_H_INCLUDED
#ifdef _MSC_VER
#ifdef _WIN32
#define ISINVALID(s) (INVALID_SOCKET==(s))

View File

@ -1,721 +0,0 @@
/* $Id: miniupnpcmodule.c,v 1.34 2019/05/20 19:07:16 nanard Exp $*/
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Author : Thomas BERNARD
* website : https://miniupnp.tuxfamily.org/
* copyright (c) 2007-2019 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
#include <Python.h>
#define MINIUPNP_STATICLIB
#include "structmember.h"
#include "miniupnpc.h"
#include "upnpcommands.h"
#include "upnperrors.h"
#ifdef _WIN32
#include <winsock2.h>
#endif
/* for compatibility with Python < 2.4 */
#ifndef Py_RETURN_NONE
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
#endif
#ifndef Py_RETURN_TRUE
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
#endif
#ifndef Py_RETURN_FALSE
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
#endif
/* for compatibility with Python < 3.0 */
#ifndef PyVarObject_HEAD_INIT
#define PyVarObject_HEAD_INIT(type, size) \
PyObject_HEAD_INIT(type) size,
#endif
#ifndef Py_TYPE
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#endif
typedef struct {
PyObject_HEAD
/* Type-specific fields go here. */
struct UPNPDev * devlist;
struct UPNPUrls urls;
struct IGDdatas data;
unsigned int discoverdelay; /* value passed to upnpDiscover() */
unsigned int localport; /* value passed to upnpDiscover() */
char lanaddr[40]; /* our ip address on the LAN */
char * multicastif;
char * minissdpdsocket;
} UPnPObject;
static PyMemberDef UPnP_members[] = {
{"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr),
READONLY, "ip address on the LAN"
},
{"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay),
0/*READWRITE*/, "value in ms used to wait for SSDP responses"
},
{"localport", T_UINT, offsetof(UPnPObject, localport),
0/*READWRITE*/,
"If localport is set to UPNP_LOCAL_PORT_SAME(1) "
"SSDP packets will be sent from the source port "
"1900 (same as destination port), if set to "
"UPNP_LOCAL_PORT_ANY(0) system assign a source "
"port, any other value will be attempted as the "
"source port"
},
/* T_STRING is allways readonly :( */
{"multicastif", T_STRING, offsetof(UPnPObject, multicastif),
0, "IP of the network interface to be used for multicast operations"
},
{"minissdpdsocket", T_STRING, offsetof(UPnPObject, minissdpdsocket),
0, "path of the MiniSSDPd unix socket"
},
{NULL}
};
static int UPnP_init(UPnPObject *self, PyObject *args, PyObject *kwds)
{
char* multicastif = NULL;
char* minissdpdsocket = NULL;
static char *kwlist[] = {
"multicastif", "minissdpdsocket", "discoverdelay",
"localport", NULL
};
if(!PyArg_ParseTupleAndKeywords(args, kwds, "|zzII", kwlist,
&multicastif,
&minissdpdsocket,
&self->discoverdelay,
&self->localport))
return -1;
if(self->localport>1 &&
(self->localport>65534||self->localport<1024)) {
PyErr_SetString(PyExc_Exception, "Invalid localport value");
return -1;
}
if(multicastif)
self->multicastif = strdup(multicastif);
if(minissdpdsocket)
self->minissdpdsocket = strdup(minissdpdsocket);
return 0;
}
static void
UPnPObject_dealloc(UPnPObject *self)
{
freeUPNPDevlist(self->devlist);
FreeUPNPUrls(&self->urls);
free(self->multicastif);
free(self->minissdpdsocket);
Py_TYPE(self)->tp_free((PyObject*)self);
}
static PyObject *
UPnP_discover(UPnPObject *self)
{
struct UPNPDev * dev;
int i;
PyObject *res = NULL;
if(self->devlist)
{
freeUPNPDevlist(self->devlist);
self->devlist = 0;
}
Py_BEGIN_ALLOW_THREADS
self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/,
self->multicastif,
self->minissdpdsocket,
(int)self->localport,
0/*ip v6*/,
2/* TTL */,
0/*error */);
Py_END_ALLOW_THREADS
/* Py_RETURN_NONE ??? */
for(dev = self->devlist, i = 0; dev; dev = dev->pNext)
i++;
res = Py_BuildValue("i", i);
return res;
}
static PyObject *
UPnP_selectigd(UPnPObject *self)
{
int r;
Py_BEGIN_ALLOW_THREADS
r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data,
self->lanaddr, sizeof(self->lanaddr));
Py_END_ALLOW_THREADS
if(r)
{
return Py_BuildValue("s", self->urls.controlURL);
}
else
{
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, "No UPnP device discovered");
return NULL;
}
}
static PyObject *
UPnP_totalbytesent(UPnPObject *self)
{
UNSIGNED_INTEGER i;
Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF,
self->data.CIF.servicetype);
Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
}
static PyObject *
UPnP_totalbytereceived(UPnPObject *self)
{
UNSIGNED_INTEGER i;
Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF,
self->data.CIF.servicetype);
Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
}
static PyObject *
UPnP_totalpacketsent(UPnPObject *self)
{
UNSIGNED_INTEGER i;
Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF,
self->data.CIF.servicetype);
Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
}
static PyObject *
UPnP_totalpacketreceived(UPnPObject *self)
{
UNSIGNED_INTEGER i;
Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF,
self->data.CIF.servicetype);
Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
}
static PyObject *
UPnP_statusinfo(UPnPObject *self)
{
char status[64];
char lastconnerror[64];
unsigned int uptime = 0;
int r;
status[0] = '\0';
lastconnerror[0] = '\0';
Py_BEGIN_ALLOW_THREADS
r = UPNP_GetStatusInfo(self->urls.controlURL, self->data.first.servicetype,
status, &uptime, lastconnerror);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror);
#else
return Py_BuildValue("(s,i,s)", status, (int)uptime, lastconnerror);
#endif
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
static PyObject *
UPnP_connectiontype(UPnPObject *self)
{
char connectionType[64];
int r;
connectionType[0] = '\0';
Py_BEGIN_ALLOW_THREADS
r = UPNP_GetConnectionTypeInfo(self->urls.controlURL,
self->data.first.servicetype,
connectionType);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
return Py_BuildValue("s", connectionType);
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
static PyObject *
UPnP_externalipaddress(UPnPObject *self)
{
char externalIPAddress[40];
int r;
externalIPAddress[0] = '\0';
Py_BEGIN_ALLOW_THREADS
r = UPNP_GetExternalIPAddress(self->urls.controlURL,
self->data.first.servicetype,
externalIPAddress);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
return Py_BuildValue("s", externalIPAddress);
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
/* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc,
* remoteHost, leaseDuration)
* protocol is 'UDP' or 'TCP' */
static PyObject *
UPnP_addportmapping(UPnPObject *self, PyObject *args)
{
char extPort[6];
unsigned short ePort;
char inPort[6];
unsigned short iPort;
const char * proto;
const char * host;
const char * desc;
const char * remoteHost;
unsigned int intLeaseDuration = 0;
char strLeaseDuration[12];
int r;
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
if (!PyArg_ParseTuple(args, "HssHzz|I", &ePort, &proto,
&host, &iPort, &desc, &remoteHost, &intLeaseDuration))
#else
if (!PyArg_ParseTuple(args, "HssHzz|i", &ePort, &proto,
&host, &iPort, &desc, &remoteHost, (int *)&intLeaseDuration))
#endif
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
sprintf(inPort, "%hu", iPort);
sprintf(strLeaseDuration, "%u", intLeaseDuration);
r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype,
extPort, inPort, host, desc, proto,
remoteHost, strLeaseDuration);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS)
{
Py_RETURN_TRUE;
}
else
{
// TODO: RAISE an Exception. See upnpcommands.h for errors codes.
// upnperrors.c
//Py_RETURN_FALSE;
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
/* AddAnyPortMapping(externalPort, protocol, internalHost, internalPort, desc,
* remoteHost)
* protocol is 'UDP' or 'TCP' */
static PyObject *
UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
{
char extPort[6];
unsigned short ePort;
char inPort[6];
unsigned short iPort;
char reservedPort[6];
const char * proto;
const char * host;
const char * desc;
const char * remoteHost;
const char * leaseDuration = "0";
int r;
if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
sprintf(inPort, "%hu", iPort);
r = UPNP_AddAnyPortMapping(self->urls.controlURL, self->data.first.servicetype,
extPort, inPort, host, desc, proto,
remoteHost, leaseDuration, reservedPort);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
return Py_BuildValue("i", atoi(reservedPort));
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
/* DeletePortMapping(extPort, proto, removeHost='')
* proto = 'UDP', 'TCP' */
static PyObject *
UPnP_deleteportmapping(UPnPObject *self, PyObject *args)
{
char extPort[6];
unsigned short ePort;
const char * proto;
const char * remoteHost = "";
int r;
if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
r = UPNP_DeletePortMapping(self->urls.controlURL, self->data.first.servicetype,
extPort, proto, remoteHost);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
Py_RETURN_TRUE;
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
/* DeletePortMappingRange(extPort, proto, removeHost='')
* proto = 'UDP', 'TCP' */
static PyObject *
UPnP_deleteportmappingrange(UPnPObject *self, PyObject *args)
{
char extPortStart[6];
unsigned short ePortStart;
char extPortEnd[6];
unsigned short ePortEnd;
const char * proto;
unsigned char manage;
char manageStr[6];
int r;
if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage))
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPortStart, "%hu", ePortStart);
sprintf(extPortEnd, "%hu", ePortEnd);
sprintf(manageStr, "%hu", (unsigned short)manage);
r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype,
extPortStart, extPortEnd, proto, manageStr);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
Py_RETURN_TRUE;
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
static PyObject *
UPnP_getportmappingnumberofentries(UPnPObject *self)
{
unsigned int n = 0;
int r;
Py_BEGIN_ALLOW_THREADS
r = UPNP_GetPortMappingNumberOfEntries(self->urls.controlURL,
self->data.first.servicetype,
&n);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) {
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", n);
#else
return Py_BuildValue("i", (int)n);
#endif
} else {
/* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r));
return NULL;
}
}
/* GetSpecificPortMapping(ePort, proto, remoteHost='')
* proto = 'UDP' or 'TCP' */
static PyObject *
UPnP_getspecificportmapping(UPnPObject *self, PyObject *args)
{
char extPort[6];
unsigned short ePort;
const char * proto;
const char * remoteHost = "";
char intClient[40];
char intPort[6];
unsigned short iPort;
char desc[80];
char enabled[4];
char leaseDuration[16];
if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
return NULL;
extPort[0] = '\0'; intClient[0] = '\0'; intPort[0] = '\0';
desc[0] = '\0'; enabled[0] = '\0'; leaseDuration[0] = '\0';
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
UPNP_GetSpecificPortMappingEntry(self->urls.controlURL,
self->data.first.servicetype,
extPort, proto, remoteHost,
intClient, intPort,
desc, enabled, leaseDuration);
Py_END_ALLOW_THREADS
if(intClient[0])
{
iPort = (unsigned short)atoi(intPort);
return Py_BuildValue("(s,H,s,O,i)",
intClient, iPort, desc,
PyBool_FromLong(atoi(enabled)),
atoi(leaseDuration));
}
else
{
Py_RETURN_NONE;
}
}
/* GetGenericPortMapping(index) */
static PyObject *
UPnP_getgenericportmapping(UPnPObject *self, PyObject *args)
{
int i, r;
char index[8];
char intClient[40];
char intPort[6];
unsigned short iPort;
char extPort[6];
unsigned short ePort;
char protocol[4];
char desc[80];
char enabled[6];
char rHost[64];
char duration[16]; /* lease duration */
unsigned int dur;
if(!PyArg_ParseTuple(args, "i", &i))
return NULL;
Py_BEGIN_ALLOW_THREADS
snprintf(index, sizeof(index), "%d", i);
rHost[0] = '\0'; enabled[0] = '\0';
duration[0] = '\0'; desc[0] = '\0';
extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
r = UPNP_GetGenericPortMappingEntry(self->urls.controlURL,
self->data.first.servicetype,
index,
extPort, intClient, intPort,
protocol, desc, enabled, rHost,
duration);
Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS)
{
ePort = (unsigned short)atoi(extPort);
iPort = (unsigned short)atoi(intPort);
dur = (unsigned int)strtoul(duration, 0, 0);
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("(H,s,(s,H),s,s,s,I)",
ePort, protocol, intClient, iPort,
desc, enabled, rHost, dur);
#else
return Py_BuildValue("(i,s,(s,i),s,s,s,i)",
(int)ePort, protocol, intClient, (int)iPort,
desc, enabled, rHost, (int)dur);
#endif
}
else
{
Py_RETURN_NONE;
}
}
/* miniupnpc.UPnP object Method Table */
static PyMethodDef UPnP_methods[] = {
{"discover", (PyCFunction)UPnP_discover, METH_NOARGS,
"discover UPnP IGD devices on the network"
},
{"selectigd", (PyCFunction)UPnP_selectigd, METH_NOARGS,
"select a valid UPnP IGD among discovered devices"
},
{"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS,
"return the total number of bytes sent by UPnP IGD"
},
{"totalbytereceived", (PyCFunction)UPnP_totalbytereceived, METH_NOARGS,
"return the total number of bytes received by UPnP IGD"
},
{"totalpacketsent", (PyCFunction)UPnP_totalpacketsent, METH_NOARGS,
"return the total number of packets sent by UPnP IGD"
},
{"totalpacketreceived", (PyCFunction)UPnP_totalpacketreceived, METH_NOARGS,
"return the total number of packets received by UPnP IGD"
},
{"statusinfo", (PyCFunction)UPnP_statusinfo, METH_NOARGS,
"return status and uptime"
},
{"connectiontype", (PyCFunction)UPnP_connectiontype, METH_NOARGS,
"return IGD WAN connection type"
},
{"externalipaddress", (PyCFunction)UPnP_externalipaddress, METH_NOARGS,
"return external IP address"
},
{"addportmapping", (PyCFunction)UPnP_addportmapping, METH_VARARGS,
"add a port mapping"
},
{"addanyportmapping", (PyCFunction)UPnP_addanyportmapping, METH_VARARGS,
"add a port mapping, IGD to select alternative if necessary"
},
{"deleteportmapping", (PyCFunction)UPnP_deleteportmapping, METH_VARARGS,
"delete a port mapping"
},
{"deleteportmappingrange", (PyCFunction)UPnP_deleteportmappingrange, METH_VARARGS,
"delete a range of port mappings"
},
{"getportmappingnumberofentries", (PyCFunction)UPnP_getportmappingnumberofentries, METH_NOARGS,
"-- non standard --"
},
{"getspecificportmapping", (PyCFunction)UPnP_getspecificportmapping, METH_VARARGS,
"get details about a specific port mapping entry"
},
{"getgenericportmapping", (PyCFunction)UPnP_getgenericportmapping, METH_VARARGS,
"get all details about the port mapping at index"
},
{NULL} /* Sentinel */
};
static PyTypeObject UPnPType = {
PyVarObject_HEAD_INIT(NULL,
0) /*ob_size*/
"miniupnpc.UPnP", /*tp_name*/
sizeof(UPnPObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)UPnPObject_dealloc,/*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
0, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"UPnP objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
UPnP_methods, /* tp_methods */
UPnP_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)UPnP_init, /* tp_init */
0, /* tp_alloc */
#ifndef _WIN32
PyType_GenericNew,/*UPnP_new,*/ /* tp_new */
#else
0,
#endif
};
/* module methods */
static PyMethodDef miniupnpc_methods[] = {
{NULL} /* Sentinel */
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"miniupnpc", /* m_name */
"miniupnpc module.", /* m_doc */
-1, /* m_size */
miniupnpc_methods, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
#endif
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_miniupnpc(void)
#else
initminiupnpc(void)
#endif
{
PyObject* m;
#ifdef _WIN32
/* initialize Winsock. */
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (nResult != 0)
{
/* error code could be WSASYSNOTREADY WSASYSNOTREADY
* WSASYSNOTREADY WSASYSNOTREADY WSASYSNOTREADY */
#if PY_MAJOR_VERSION >= 3
return 0;
#else
return;
#endif
}
UPnPType.tp_new = PyType_GenericNew;
#endif
if (PyType_Ready(&UPnPType) < 0)
#if PY_MAJOR_VERSION >= 3
return 0;
#else
return;
#endif
#if PY_MAJOR_VERSION >= 3
m = PyModule_Create(&moduledef);
#else
m = Py_InitModule3("miniupnpc", miniupnpc_methods,
"miniupnpc module.");
#endif
Py_INCREF(&UPnPType);
PyModule_AddObject(m, "UPnP", (PyObject *)&UPnPType);
#if PY_MAJOR_VERSION >= 3
return m;
#endif
}

View File

@ -4,7 +4,7 @@
#include "core/version.h"
#define OS_STRING VERSION_NAME "/1.0"
#define MINIUPNPC_VERSION_STRING "2.1"
#define MINIUPNPC_VERSION_STRING "2.2.2"
#if 0
/* according to "UPnP Device Architecture 1.0" */

View File

@ -1,8 +1,8 @@
/* $Id: miniwget.c,v 1.78 2018/03/13 23:22:18 nanard Exp $ */
/* $Id: miniwget.c,v 1.82 2020/05/29 21:14:22 nanard Exp $ */
/* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Website : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas Bernard
* Copyright (c) 2005-2018 Thomas Bernard
* Copyright (c) 2005-2020 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@ -15,7 +15,7 @@
#include <ws2tcpip.h>
#include <io.h>
#define MAXHOSTNAMELEN 64
#define snprintf _snprintf
#include "win32_snprintf.h"
#define socklen_t int
#ifndef strncasecmp
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
@ -176,11 +176,14 @@ getHTTPResponse(SOCKET s, int * size, int * status_code)
/* Status line
* HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
int sp;
for(sp = 0; sp < i; sp++)
for(sp = 0; sp < i - 1; sp++)
if(header_buf[sp] == ' ')
{
if(*status_code < 0)
*status_code = atoi(header_buf + sp + 1);
{
if (header_buf[sp+1] >= '1' && header_buf[sp+1] <= '9')
*status_code = atoi(header_buf + sp + 1);
}
else
{
#ifdef DEBUG

View File

@ -15,7 +15,7 @@
#if defined(__HAIKU__)
/* rename our private function because Haiku already defines a atoui() function */
#define atoui atoui2
#endif
#endif
/* list of the elements */
static const struct {

View File

@ -1,8 +1,8 @@
/* $Id: receivedata.c,v 1.7 2015/11/09 21:51:41 nanard Exp $ */
/* $Id: receivedata.c,v 1.10 2021/03/02 23:33:07 nanard Exp $ */
/* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
* Copyright (c) 2011-2014 Thomas Bernard
* Copyright (c) 2011-2021 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@ -92,7 +92,13 @@ receivedata(SOCKET socket,
#endif /* DEBUG */
if(scope_id)
*scope_id = src_addr6->sin6_scope_id;
} else {
if(scope_id)
*scope_id = 0;
}
#else /* MINIUPNPC_GET_SRC_ADDR */
if(scope_id)
*scope_id = 0;
#endif /* MINIUPNPC_GET_SRC_ADDR */
return n;
}

View File

@ -1,864 +0,0 @@
/* $Id: upnpc.c,v 1.119 2018/03/13 23:34:46 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
* Copyright (c) 2005-2020 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <winsock2.h>
#define snprintf _snprintf
#else
/* for IPPROTO_TCP / IPPROTO_UDP */
#include <netinet/in.h>
#endif
#include <ctype.h>
#include "miniwget.h"
#include "miniupnpc.h"
#include "upnpcommands.h"
#include "portlistingparse.h"
#include "upnperrors.h"
#include "miniupnpcstrings.h"
/* protofix() checks if protocol is "UDP" or "TCP"
* returns NULL if not */
const char * protofix(const char * proto)
{
static const char proto_tcp[4] = { 'T', 'C', 'P', 0};
static const char proto_udp[4] = { 'U', 'D', 'P', 0};
int i, b;
for(i=0, b=1; i<4; i++)
b = b && ( (proto[i] == proto_tcp[i])
|| (proto[i] == (proto_tcp[i] | 32)) );
if(b)
return proto_tcp;
for(i=0, b=1; i<4; i++)
b = b && ( (proto[i] == proto_udp[i])
|| (proto[i] == (proto_udp[i] | 32)) );
if(b)
return proto_udp;
return 0;
}
/* is_int() checks if parameter is an integer or not
* 1 for integer
* 0 for not an integer */
int is_int(char const* s)
{
if(s == NULL)
return 0;
while(*s) {
/* #define isdigit(c) ((c) >= '0' && (c) <= '9') */
if(!isdigit(*s))
return 0;
s++;
}
return 1;
}
static void DisplayInfos(struct UPNPUrls * urls,
struct IGDdatas * data)
{
char externalIPAddress[40];
char connectionType[64];
char status[64];
char lastconnerr[64];
unsigned int uptime = 0;
unsigned int brUp, brDown;
time_t timenow, timestarted;
int r;
if(UPNP_GetConnectionTypeInfo(urls->controlURL,
data->first.servicetype,
connectionType) != UPNPCOMMAND_SUCCESS)
printf("GetConnectionTypeInfo failed.\n");
else
printf("Connection Type : %s\n", connectionType);
if(UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
status, &uptime, lastconnerr) != UPNPCOMMAND_SUCCESS)
printf("GetStatusInfo failed.\n");
else
printf("Status : %s, uptime=%us, LastConnectionError : %s\n",
status, uptime, lastconnerr);
if(uptime > 0) {
timenow = time(NULL);
timestarted = timenow - uptime;
printf(" Time started : %s", ctime(&timestarted));
}
if(UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype,
&brDown, &brUp) != UPNPCOMMAND_SUCCESS) {
printf("GetLinkLayerMaxBitRates failed.\n");
} else {
printf("MaxBitRateDown : %u bps", brDown);
if(brDown >= 1000000) {
printf(" (%u.%u Mbps)", brDown / 1000000, (brDown / 100000) % 10);
} else if(brDown >= 1000) {
printf(" (%u Kbps)", brDown / 1000);
}
printf(" MaxBitRateUp %u bps", brUp);
if(brUp >= 1000000) {
printf(" (%u.%u Mbps)", brUp / 1000000, (brUp / 100000) % 10);
} else if(brUp >= 1000) {
printf(" (%u Kbps)", brUp / 1000);
}
printf("\n");
}
r = UPNP_GetExternalIPAddress(urls->controlURL,
data->first.servicetype,
externalIPAddress);
if(r != UPNPCOMMAND_SUCCESS) {
printf("GetExternalIPAddress failed. (errorcode=%d)\n", r);
} else {
printf("ExternalIPAddress = %s\n", externalIPAddress);
}
}
static void GetConnectionStatus(struct UPNPUrls * urls,
struct IGDdatas * data)
{
unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
DisplayInfos(urls, data);
bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
}
static void ListRedirections(struct UPNPUrls * urls,
struct IGDdatas * data)
{
int r;
int i = 0;
char index[6];
char intClient[40];
char intPort[6];
char extPort[6];
char protocol[4];
char desc[80];
char enabled[6];
char rHost[64];
char duration[16];
/*unsigned int num=0;
UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num);
printf("PortMappingNumberOfEntries : %u\n", num);*/
printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n");
do {
snprintf(index, 6, "%d", i);
rHost[0] = '\0'; enabled[0] = '\0';
duration[0] = '\0'; desc[0] = '\0';
extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
r = UPNP_GetGenericPortMappingEntry(urls->controlURL,
data->first.servicetype,
index,
extPort, intClient, intPort,
protocol, desc, enabled,
rHost, duration);
if(r==0)
/*
printf("%02d - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n"
" desc='%s' rHost='%s'\n",
i, protocol, extPort, intClient, intPort,
enabled, duration,
desc, rHost);
*/
printf("%2d %s %5s->%s:%-5s '%s' '%s' %s\n",
i, protocol, extPort, intClient, intPort,
desc, rHost, duration);
else
printf("GetGenericPortMappingEntry() returned %d (%s)\n",
r, strupnperror(r));
i++;
} while(r==0);
}
static void NewListRedirections(struct UPNPUrls * urls,
struct IGDdatas * data)
{
int r;
int i = 0;
struct PortMappingParserData pdata;
struct PortMapping * pm;
memset(&pdata, 0, sizeof(struct PortMappingParserData));
r = UPNP_GetListOfPortMappings(urls->controlURL,
data->first.servicetype,
"0",
"65535",
"TCP",
"1000",
&pdata);
if(r == UPNPCOMMAND_SUCCESS)
{
printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n");
for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
{
printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
i, pm->protocol, pm->externalPort, pm->internalClient,
pm->internalPort,
pm->description, pm->remoteHost,
(unsigned)pm->leaseTime);
i++;
}
FreePortListing(&pdata);
}
else
{
printf("GetListOfPortMappings() returned %d (%s)\n",
r, strupnperror(r));
}
r = UPNP_GetListOfPortMappings(urls->controlURL,
data->first.servicetype,
"0",
"65535",
"UDP",
"1000",
&pdata);
if(r == UPNPCOMMAND_SUCCESS)
{
for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
{
printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
i, pm->protocol, pm->externalPort, pm->internalClient,
pm->internalPort,
pm->description, pm->remoteHost,
(unsigned)pm->leaseTime);
i++;
}
FreePortListing(&pdata);
}
else
{
printf("GetListOfPortMappings() returned %d (%s)\n",
r, strupnperror(r));
}
}
/* Test function
* 1 - get connection type
* 2 - get extenal ip address
* 3 - Add port mapping
* 4 - get this port mapping from the IGD */
static int SetRedirectAndTest(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * iaddr,
const char * iport,
const char * eport,
const char * proto,
const char * leaseDuration,
const char * remoteHost,
const char * description,
int addAny)
{
char externalIPAddress[40];
char intClient[40];
char intPort[6];
char reservedPort[6];
char duration[16];
int r;
if(!iaddr || !iport || !eport || !proto)
{
fprintf(stderr, "Wrong arguments\n");
return -1;
}
proto = protofix(proto);
if(!proto)
{
fprintf(stderr, "invalid protocol\n");
return -1;
}
r = UPNP_GetExternalIPAddress(urls->controlURL,
data->first.servicetype,
externalIPAddress);
if(r!=UPNPCOMMAND_SUCCESS)
printf("GetExternalIPAddress failed.\n");
else
printf("ExternalIPAddress = %s\n", externalIPAddress);
if (addAny) {
r = UPNP_AddAnyPortMapping(urls->controlURL, data->first.servicetype,
eport, iport, iaddr, description,
proto, remoteHost, leaseDuration, reservedPort);
if(r==UPNPCOMMAND_SUCCESS)
eport = reservedPort;
else
printf("AddAnyPortMapping(%s, %s, %s) failed with code %d (%s)\n",
eport, iport, iaddr, r, strupnperror(r));
} else {
r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
eport, iport, iaddr, description,
proto, remoteHost, leaseDuration);
if(r!=UPNPCOMMAND_SUCCESS) {
printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
eport, iport, iaddr, r, strupnperror(r));
return -2;
}
}
r = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
data->first.servicetype,
eport, proto, remoteHost,
intClient, intPort, NULL/*desc*/,
NULL/*enabled*/, duration);
if(r!=UPNPCOMMAND_SUCCESS) {
printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
r, strupnperror(r));
return -2;
} else {
printf("InternalIP:Port = %s:%s\n", intClient, intPort);
printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
externalIPAddress, eport, proto, intClient, intPort, duration);
}
return 0;
}
static int
RemoveRedirect(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * eport,
const char * proto,
const char * remoteHost)
{
int r;
if(!proto || !eport)
{
fprintf(stderr, "invalid arguments\n");
return -1;
}
proto = protofix(proto);
if(!proto)
{
fprintf(stderr, "protocol invalid\n");
return -1;
}
r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost);
if(r!=UPNPCOMMAND_SUCCESS) {
printf("UPNP_DeletePortMapping() failed with code : %d\n", r);
return -2;
}else {
printf("UPNP_DeletePortMapping() returned : %d\n", r);
}
return 0;
}
static int
RemoveRedirectRange(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * ePortStart, char const * ePortEnd,
const char * proto, const char * manage)
{
int r;
if (!manage)
manage = "0";
if(!proto || !ePortStart || !ePortEnd)
{
fprintf(stderr, "invalid arguments\n");
return -1;
}
proto = protofix(proto);
if(!proto)
{
fprintf(stderr, "protocol invalid\n");
return -1;
}
r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage);
if(r!=UPNPCOMMAND_SUCCESS) {
printf("UPNP_DeletePortMappingRange() failed with code : %d\n", r);
return -2;
}else {
printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
}
return 0;
}
/* IGD:2, functions for service WANIPv6FirewallControl:1 */
static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data)
{
unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
int firewallEnabled = 0, inboundPinholeAllowed = 0;
UPNP_GetFirewallStatus(urls->controlURL_6FC, data->IPv6FC.servicetype, &firewallEnabled, &inboundPinholeAllowed);
printf("FirewallEnabled: %d & Inbound Pinhole Allowed: %d\n", firewallEnabled, inboundPinholeAllowed);
printf("GetFirewallStatus:\n Firewall Enabled: %s\n Inbound Pinhole Allowed: %s\n", (firewallEnabled)? "Yes":"No", (inboundPinholeAllowed)? "Yes":"No");
bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
}
/* Test function
* 1 - Add pinhole
* 2 - Check if pinhole is working from the IGD side */
static void SetPinholeAndTest(struct UPNPUrls * urls, struct IGDdatas * data,
const char * remoteaddr, const char * eport,
const char * intaddr, const char * iport,
const char * proto, const char * lease_time)
{
char uniqueID[8];
/*int isWorking = 0;*/
int r;
char proto_tmp[8];
if(!intaddr || !remoteaddr || !iport || !eport || !proto || !lease_time)
{
fprintf(stderr, "Wrong arguments\n");
return;
}
if(atoi(proto) == 0)
{
const char * protocol;
protocol = protofix(proto);
if(protocol && (strcmp("TCP", protocol) == 0))
{
snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_TCP);
proto = proto_tmp;
}
else if(protocol && (strcmp("UDP", protocol) == 0))
{
snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_UDP);
proto = proto_tmp;
}
else
{
fprintf(stderr, "invalid protocol\n");
return;
}
}
r = UPNP_AddPinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, lease_time, uniqueID);
if(r!=UPNPCOMMAND_SUCCESS)
printf("AddPinhole([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
remoteaddr, eport, intaddr, iport, r, strupnperror(r));
else
{
printf("AddPinhole: ([%s]:%s -> [%s]:%s) / Pinhole ID = %s\n",
remoteaddr, eport, intaddr, iport, uniqueID);
/*r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->servicetype_6FC, uniqueID, &isWorking);
if(r!=UPNPCOMMAND_SUCCESS)
printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");*/
}
}
/* Test function
* 1 - Check if pinhole is working from the IGD side
* 2 - Update pinhole */
static void GetPinholeAndUpdate(struct UPNPUrls * urls, struct IGDdatas * data,
const char * uniqueID, const char * lease_time)
{
int isWorking = 0;
int r;
if(!uniqueID || !lease_time)
{
fprintf(stderr, "Wrong arguments\n");
return;
}
r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
if(r!=UPNPCOMMAND_SUCCESS)
printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
if(isWorking || r==709)
{
r = UPNP_UpdatePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, lease_time);
printf("UpdatePinhole: Pinhole ID = %s with Lease Time: %s\n", uniqueID, lease_time);
if(r!=UPNPCOMMAND_SUCCESS)
printf("UpdatePinhole: ID (%s) failed with code %d (%s)\n", uniqueID, r, strupnperror(r));
}
}
/* Test function
* Get pinhole timeout
*/
static void GetPinholeOutboundTimeout(struct UPNPUrls * urls, struct IGDdatas * data,
const char * remoteaddr, const char * eport,
const char * intaddr, const char * iport,
const char * proto)
{
int timeout = 0;
int r;
if(!intaddr || !remoteaddr || !iport || !eport || !proto)
{
fprintf(stderr, "Wrong arguments\n");
return;
}
r = UPNP_GetOutboundPinholeTimeout(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, &timeout);
if(r!=UPNPCOMMAND_SUCCESS)
printf("GetOutboundPinholeTimeout([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
intaddr, iport, remoteaddr, eport, r, strupnperror(r));
else
printf("GetOutboundPinholeTimeout: ([%s]:%s -> [%s]:%s) / Timeout = %d\n", intaddr, iport, remoteaddr, eport, timeout);
}
static void
GetPinholePackets(struct UPNPUrls * urls,
struct IGDdatas * data, const char * uniqueID)
{
int r, pinholePackets = 0;
if(!uniqueID)
{
fprintf(stderr, "invalid arguments\n");
return;
}
r = UPNP_GetPinholePackets(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &pinholePackets);
if(r!=UPNPCOMMAND_SUCCESS)
printf("GetPinholePackets() failed with code %d (%s)\n", r, strupnperror(r));
else
printf("GetPinholePackets: Pinhole ID = %s / PinholePackets = %d\n", uniqueID, pinholePackets);
}
static void
CheckPinhole(struct UPNPUrls * urls,
struct IGDdatas * data, const char * uniqueID)
{
int r, isWorking = 0;
if(!uniqueID)
{
fprintf(stderr, "invalid arguments\n");
return;
}
r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
if(r!=UPNPCOMMAND_SUCCESS)
printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
else
printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
}
static void
RemovePinhole(struct UPNPUrls * urls,
struct IGDdatas * data, const char * uniqueID)
{
int r;
if(!uniqueID)
{
fprintf(stderr, "invalid arguments\n");
return;
}
r = UPNP_DeletePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID);
printf("UPNP_DeletePinhole() returned : %d\n", r);
}
/* sample upnp client program */
int main(int argc, char ** argv)
{
char command = 0;
char ** commandargv = 0;
int commandargc = 0;
struct UPNPDev * devlist = 0;
char lanaddr[64] = "unset"; /* my ip address on the LAN */
int i;
const char * rootdescurl = 0;
const char * multicastif = 0;
const char * minissdpdpath = 0;
int localport = UPNP_LOCAL_PORT_ANY;
int retcode = 0;
int error = 0;
int ipv6 = 0;
unsigned char ttl = 2; /* defaulting to 2 */
const char * description = 0;
#ifdef _WIN32
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(nResult != NO_ERROR)
{
fprintf(stderr, "WSAStartup() failed.\n");
return -1;
}
#endif
printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING);
printf(" (c) 2005-2020 Thomas Bernard.\n");
printf("Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/\n"
"for more information.\n");
/* command line processing */
for(i=1; i<argc; i++)
{
if(0 == strcmp(argv[i], "--help") || 0 == strcmp(argv[i], "-h"))
{
command = 0;
break;
}
if(argv[i][0] == '-')
{
if(argv[i][1] == 'u')
rootdescurl = argv[++i];
else if(argv[i][1] == 'm')
{
multicastif = argv[++i];
minissdpdpath = ""; /* Disable usage of minissdpd */
}
else if(argv[i][1] == 'z')
{
char junk;
if(sscanf(argv[++i], "%d%c", &localport, &junk)!=1 ||
localport<0 || localport>65535 ||
(localport >1 && localport < 1024))
{
fprintf(stderr, "Invalid localport '%s'\n", argv[i]);
localport = UPNP_LOCAL_PORT_ANY;
break;
}
}
else if(argv[i][1] == 'p')
minissdpdpath = argv[++i];
else if(argv[i][1] == '6')
ipv6 = 1;
else if(argv[i][1] == 'e')
description = argv[++i];
else if(argv[i][1] == 't')
ttl = (unsigned char)atoi(argv[++i]);
else
{
command = argv[i][1];
i++;
commandargv = argv + i;
commandargc = argc - i;
break;
}
}
else
{
fprintf(stderr, "option '%s' invalid\n", argv[i]);
}
}
if(!command
|| (command == 'a' && commandargc<4)
|| (command == 'd' && argc<2)
|| (command == 'r' && argc<2)
|| (command == 'A' && commandargc<6)
|| (command == 'U' && commandargc<2)
|| (command == 'D' && commandargc<1))
{
fprintf(stderr, "Usage :\t%s [options] -a ip port external_port protocol [duration] [remote host]\n\t\tAdd port redirection\n", argv[0]);
fprintf(stderr, " \t%s [options] -d external_port protocol [remote host]\n\t\tDelete port redirection\n", argv[0]);
fprintf(stderr, " \t%s [options] -s\n\t\tGet Connection status\n", argv[0]);
fprintf(stderr, " \t%s [options] -l\n\t\tList redirections\n", argv[0]);
fprintf(stderr, " \t%s [options] -L\n\t\tList redirections (using GetListOfPortMappings (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -n ip port external_port protocol [duration] [remote host]\n\t\tAdd (any) port redirection allowing IGD to use alternative external_port (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -N external_port_start external_port_end protocol [manage]\n\t\tDelete range of port redirections (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -r port1 [external_port1] protocol1 [port2 [external_port2] protocol2] [...]\n\t\tAdd all redirections to the current host\n", argv[0]);
fprintf(stderr, " \t%s [options] -A remote_ip remote_port internal_ip internal_port protocol lease_time\n\t\tAdd Pinhole (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -U uniqueID new_lease_time\n\t\tUpdate Pinhole (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -C uniqueID\n\t\tCheck if Pinhole is Working (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -K uniqueID\n\t\tGet Number of packets going through the rule (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -D uniqueID\n\t\tDelete Pinhole (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -S\n\t\tGet Firewall status (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -G remote_ip remote_port internal_ip internal_port protocol\n\t\tGet Outbound Pinhole Timeout (for IGD:2 only)\n", argv[0]);
fprintf(stderr, " \t%s [options] -P\n\t\tGet Presentation url\n", argv[0]);
fprintf(stderr, "\nprotocol is UDP or TCP\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " -e description : set description for port mapping.\n");
fprintf(stderr, " -6 : use ip v6 instead of ip v4.\n");
fprintf(stderr, " -u url : bypass discovery process by providing the XML root description url.\n");
fprintf(stderr, " -m address/interface : provide ip address (ip v4) or interface name (ip v4 or v6) to use for sending SSDP multicast packets.\n");
fprintf(stderr, " -z localport : SSDP packets local (source) port (1024-65535).\n");
fprintf(stderr, " -p path : use this path for MiniSSDPd socket.\n");
fprintf(stderr, " -t ttl : set multicast TTL. Default value is 2.\n");
return 1;
}
if( rootdescurl
|| (devlist = upnpDiscover(2000, multicastif, minissdpdpath,
localport, ipv6, ttl, &error)))
{
struct UPNPDev * device;
struct UPNPUrls urls;
struct IGDdatas data;
if(devlist)
{
printf("List of UPNP devices found on the network :\n");
for(device = devlist; device; device = device->pNext)
{
printf(" desc: %s\n st: %s\n\n",
device->descURL, device->st);
}
}
else if(!rootdescurl)
{
printf("upnpDiscover() error code=%d\n", error);
}
i = 1;
if( (rootdescurl && UPNP_GetIGDFromUrl(rootdescurl, &urls, &data, lanaddr, sizeof(lanaddr)))
|| (i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr))))
{
switch(i) {
case 1:
printf("Found valid IGD : %s\n", urls.controlURL);
break;
case 2:
printf("Found a (not connected?) IGD : %s\n", urls.controlURL);
printf("Trying to continue anyway\n");
break;
case 3:
printf("UPnP device found. Is it an IGD ? : %s\n", urls.controlURL);
printf("Trying to continue anyway\n");
break;
default:
printf("Found device (igd ?) : %s\n", urls.controlURL);
printf("Trying to continue anyway\n");
}
printf("Local LAN ip address : %s\n", lanaddr);
#if 0
printf("getting \"%s\"\n", urls.ipcondescURL);
descXML = miniwget(urls.ipcondescURL, &descXMLsize);
if(descXML)
{
/*fwrite(descXML, 1, descXMLsize, stdout);*/
free(descXML); descXML = NULL;
}
#endif
switch(command)
{
case 'l':
DisplayInfos(&urls, &data);
ListRedirections(&urls, &data);
break;
case 'L':
NewListRedirections(&urls, &data);
break;
case 'a':
if (SetRedirectAndTest(&urls, &data,
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
(commandargc > 4)&is_int(commandargv[4])?commandargv[4]:"0",
(commandargc > 4)&!is_int(commandargv[4])?commandargv[4]:(commandargc > 5)?commandargv[5]:NULL,
description, 0) < 0)
retcode = 2;
break;
case 'd':
if (RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
commandargc > 2 ? commandargv[2] : NULL) < 0)
retcode = 2;
break;
case 'n': /* aNy */
if (SetRedirectAndTest(&urls, &data,
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
(commandargc > 4)&is_int(commandargv[4])?commandargv[4]:"0",
(commandargc > 4)&!is_int(commandargv[4])?commandargv[4]:(commandargc > 5)?commandargv[5]:NULL,
description, 1) < 0)
retcode = 2;
break;
case 'N':
if (commandargc < 3)
fprintf(stderr, "too few arguments\n");
if (RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
commandargc > 3 ? commandargv[3] : NULL) < 0)
retcode = 2;
break;
case 's':
GetConnectionStatus(&urls, &data);
break;
case 'r':
i = 0;
while(i<commandargc)
{
if(!is_int(commandargv[i])) {
/* 1st parameter not an integer : error */
fprintf(stderr, "command -r : %s is not an port number\n", commandargv[i]);
retcode = 1;
break;
} else if(is_int(commandargv[i+1])){
/* 2nd parameter is an integer : <port> <external_port> <protocol> */
if (SetRedirectAndTest(&urls, &data,
lanaddr, commandargv[i],
commandargv[i+1], commandargv[i+2], "0", NULL,
description, 0) < 0)
retcode = 2;
i+=3; /* 3 parameters parsed */
} else {
/* 2nd parameter not an integer : <port> <protocol> */
if (SetRedirectAndTest(&urls, &data,
lanaddr, commandargv[i],
commandargv[i], commandargv[i+1], "0", NULL,
description, 0) < 0)
retcode = 2;
i+=2; /* 2 parameters parsed */
}
}
break;
case 'A':
SetPinholeAndTest(&urls, &data,
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
commandargv[4], commandargv[5]);
break;
case 'U':
GetPinholeAndUpdate(&urls, &data,
commandargv[0], commandargv[1]);
break;
case 'C':
for(i=0; i<commandargc; i++)
{
CheckPinhole(&urls, &data, commandargv[i]);
}
break;
case 'K':
for(i=0; i<commandargc; i++)
{
GetPinholePackets(&urls, &data, commandargv[i]);
}
break;
case 'D':
for(i=0; i<commandargc; i++)
{
RemovePinhole(&urls, &data, commandargv[i]);
}
break;
case 'S':
GetFirewallStatus(&urls, &data);
break;
case 'G':
GetPinholeOutboundTimeout(&urls, &data,
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
commandargv[4]);
break;
case 'P':
printf("Presentation URL found:\n");
printf(" %s\n", data.presentationurl);
break;
default:
fprintf(stderr, "Unknown switch -%c\n", command);
retcode = 1;
}
FreeUPNPUrls(&urls);
}
else
{
fprintf(stderr, "No valid UPNP Internet Gateway Device found.\n");
retcode = 1;
}
freeUPNPDevlist(devlist); devlist = 0;
}
else
{
fprintf(stderr, "No IGD UPnP Device found on the network !\n");
retcode = 1;
}
#ifdef _WIN32
nResult = WSACleanup();
if(nResult != NO_ERROR) {
fprintf(stderr, "WSACleanup() failed.\n");
}
#endif /* _WIN32 */
return retcode;
}

View File

@ -1,8 +1,8 @@
/* $Id: upnpdev.h,v 1.1 2015/08/28 12:14:19 nanard Exp $ */
/* $Id: upnpdev.h,v 1.3 2020/05/29 15:57:42 nanard Exp $ */
/* Project : miniupnp
* Web : http://miniupnp.free.fr/
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas BERNARD
* copyright (c) 2005-2018 Thomas Bernard
* copyright (c) 2005-2020 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENSE file. */
#ifndef UPNPDEV_H_INCLUDED
@ -20,7 +20,15 @@ struct UPNPDev {
char * st;
char * usn;
unsigned int scope_id;
char buffer[3];
#if defined(__STDC_VERSION) && __STDC_VERSION__ >= 199901L
/* C99 flexible array member */
char buffer[];
#elif defined(__GNUC__)
char buffer[0];
#else
/* Fallback to a hack */
char buffer[1];
#endif
};
/* freeUPNPDevlist()

View File

@ -1,112 +0,0 @@
/* $Id: upnperrors.c,v 1.10 2019/08/24 08:49:53 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Author : Thomas BERNARD
* copyright (c) 2007-2019 Thomas Bernard
* All Right reserved.
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
#include <string.h>
#include "upnperrors.h"
#include "upnpcommands.h"
#include "miniupnpc.h"
const char * strupnperror(int err)
{
const char * s = NULL;
switch(err) {
case UPNPCOMMAND_SUCCESS:
s = "Success";
break;
case UPNPCOMMAND_UNKNOWN_ERROR:
s = "Miniupnpc Unknown Error";
break;
case UPNPCOMMAND_INVALID_ARGS:
s = "Miniupnpc Invalid Arguments";
break;
case UPNPCOMMAND_INVALID_RESPONSE:
s = "Miniupnpc Invalid response";
break;
case UPNPCOMMAND_HTTP_ERROR:
s = "Miniupnpc HTTP error";
break;
case UPNPDISCOVER_SOCKET_ERROR:
s = "Miniupnpc Socket error";
break;
case UPNPDISCOVER_MEMORY_ERROR:
case UPNPCOMMAND_MEM_ALLOC_ERROR:
s = "Miniupnpc Memory allocation error";
break;
case 401:
s = "Invalid Action";
break;
case 402:
s = "Invalid Args";
break;
case 501:
s = "Action Failed";
break;
case 606:
s = "Action not authorized";
break;
case 701:
s = "PinholeSpaceExhausted";
break;
case 702:
s = "FirewallDisabled";
break;
case 703:
s = "InboundPinholeNotAllowed";
break;
case 704:
s = "NoSuchEntry";
break;
case 705:
s = "ProtocolNotSupported";
break;
case 706:
s = "InternalPortWildcardingNotAllowed";
break;
case 707:
s = "ProtocolWildcardingNotAllowed";
break;
case 708:
s = "InvalidLayer2Address";
break;
case 709:
s = "NoPacketSent";
break;
case 713:
s = "SpecifiedArrayIndexInvalid";
break;
case 714:
s = "NoSuchEntryInArray";
break;
case 715:
s = "WildCardNotPermittedInSrcIP";
break;
case 716:
s = "WildCardNotPermittedInExtPort";
break;
case 718:
s = "ConflictInMappingEntry";
break;
case 724:
s = "SamePortValuesRequired";
break;
case 725:
s = "OnlyPermanentLeasesSupported";
break;
case 726:
s = "RemoteHostOnlySupportsWildcard";
break;
case 727:
s = "ExternalPortOnlySupportsWildcard";
break;
default:
s = "UnknownError";
break;
}
return s;
}

View File

@ -1,26 +0,0 @@
/* $Id: upnperrors.h,v 1.2 2008/07/02 23:31:15 nanard Exp $ */
/* (c) 2007-2015 Thomas Bernard
* All rights reserved.
* MiniUPnP Project.
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
#ifndef UPNPERRORS_H_INCLUDED
#define UPNPERRORS_H_INCLUDED
#include "miniupnpc_declspec.h"
#ifdef __cplusplus
extern "C" {
#endif
/* strupnperror()
* Return a string description of the UPnP error code
* or NULL for undefinded errors */
MINIUPNP_LIBSPEC const char * strupnperror(int err);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,71 @@
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* MiniUPnP project
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* (c) 2020 Pali Rohár
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#ifndef WIN32_SNPRINTF_H
#define WIN32_SNPRINTF_H
#ifdef _WIN32
#include <stdio.h>
/* snprintf is supported by:
* - Visual Studio 2015 or new
* - mingw32 with iso c ext
* - mingw-w64 with ansi stdio
* - mingw-w64 6.0.0 or new with ucrt
* - mingw-w64 8.0.0 or new with iso c ext
*/
#if ( \
(defined(_MSC_VER) && _MSC_VER < 1900) /* Visual Studio older than 2015 */ || \
(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) && defined(__NO_ISOCEXT)) /* mingw32 without iso c ext */ || \
(defined(__MINGW64_VERSION_MAJOR) && /* mingw-w64 not ... */ !( \
(defined (__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO != 0)) /* ... with ansi stdio */ || \
(__MINGW64_VERSION_MAJOR >= 6 && defined(_UCRT)) /* ... at least 6.0.0 with ucrt */ || \
(__MINGW64_VERSION_MAJOR >= 8 && !defined(__NO_ISOCEXT)) /* ... at least 8.0.0 with iso c ext */ || \
0) || \
0)
/* _scprintf is supported by:
* - Visual Studio 2002 or new
* - msvcr70.dll or new
* - msvcrt.dll on Windows XP or new
*/
#if ( \
(defined(_MSC_VER) && _MSC_VER < 1300) /* Visual Studio older than 2002 */ || \
(defined(__MSVCRT_VERSION__) && __MSVCRT_VERSION__ < 0x700) /* msvcrt older than 7.0 */ || \
0)
#define CHECK_SCPRINTF 0
#define IF_SCPRINTF(expr) 0
#define ELSE_SCPRINTF(expr) expr
#else
#define CHECK_SCPRINTF 1
#define IF_SCPRINTF(expr) expr
#define ELSE_SCPRINTF(expr) 0
#endif
/* Emulation of snprintf for win32 */
#define snprintf(buf, size, fmt, ...) ( \
(((size) != 0 && (buf) != NULL) ? ( /* _snprintf does not work with NULL buffer */ \
_snprintf((buf), (size), (fmt), __VA_ARGS__), /* _snprintf returns -1 on overflow, so ignore its value */ \
(((char *)buf)[(size_t)(size)-1] = 0), /* _snprintf does not fill nul byte on overflow */ \
0) : 0), \
(CHECK_SCPRINTF ? IF_SCPRINTF( \
_scprintf((fmt), __VA_ARGS__) /* calculate return value for snprintf via _scprintf */ \
) : ELSE_SCPRINTF( \
((size) != 0 && (buf) != NULL) ? \
strlen((buf)) /* return just length of buffer */ \
: \
1 /* no buffer, impossible to calculate, return just non-zero number */ \
) \
) \
)
#endif
#endif /* _WIN32 */
#endif /* WIN32_SNPRINTF_H */