Update miniupnpc to version 1.5.
Thanks to pabs for the needed changes to netplay.cpp (refs #2513).master
parent
583c318c2e
commit
d180026116
|
@ -1,6 +1,89 @@
|
||||||
$Id: Changelog.txt,v 1.95 2009/10/30 09:18:18 nanard Exp $
|
$Id: Changelog.txt,v 1.125 2010/12/21 16:13:13 nanard Exp $
|
||||||
miniUPnP client Changelog.
|
miniUPnP client Changelog.
|
||||||
|
|
||||||
|
2010/12/21:
|
||||||
|
use NO_GETADDRINFO macro to disable the use of getaddrinfo/freeaddrinfo
|
||||||
|
|
||||||
|
2010/12/11:
|
||||||
|
Improvements on getHTTPResponse() code.
|
||||||
|
|
||||||
|
2010/12/09:
|
||||||
|
new code for miniwget that handle Chunked transfer encoding
|
||||||
|
using getHTTPResponse() in SOAP call code
|
||||||
|
Adding MANIFEST.in for 'python setup.py bdist_rpm'
|
||||||
|
|
||||||
|
2010/11/25:
|
||||||
|
changes to minissdpc.c to compile under Win32.
|
||||||
|
see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=729
|
||||||
|
|
||||||
|
2010/09/17:
|
||||||
|
Various improvement to Makefile from Michał Górny
|
||||||
|
|
||||||
|
2010/08/05:
|
||||||
|
Adding the script "external-ip.sh" from Reuben Hawkins
|
||||||
|
|
||||||
|
2010/06/09:
|
||||||
|
update to python module to match modification made on 2010/04/05
|
||||||
|
update to Java test code to match modification made on 2010/04/05
|
||||||
|
all UPNP_* function now return an error if the SOAP request failed
|
||||||
|
at HTTP level.
|
||||||
|
|
||||||
|
2010/04/17:
|
||||||
|
Using GetBestRoute() under win32 in order to find the
|
||||||
|
right interface to use.
|
||||||
|
|
||||||
|
2010/04/12:
|
||||||
|
Retrying with HTTP/1.1 if HTTP/1.0 failed. see
|
||||||
|
http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1703
|
||||||
|
|
||||||
|
2010/04/07:
|
||||||
|
avoid returning duplicates in upnpDiscover()
|
||||||
|
|
||||||
|
2010/04/05:
|
||||||
|
Create a connecthostport.h/.c with connecthostport() function
|
||||||
|
and use it in miniwget and miniupnpc.
|
||||||
|
Use getnameinfo() instead of inet_ntop or inet_ntoa
|
||||||
|
Work to make miniupnpc IPV6 compatible...
|
||||||
|
Add java test code.
|
||||||
|
Big changes in order to support device having both WANIPConnection
|
||||||
|
and WANPPPConnection.
|
||||||
|
|
||||||
|
2010/04/04:
|
||||||
|
Use getaddrinfo() instead of gethostbyname() in miniwget.
|
||||||
|
|
||||||
|
2010/01/06:
|
||||||
|
#define _DARWIN_C_SOURCE for Mac OS X
|
||||||
|
|
||||||
|
2009/12/19:
|
||||||
|
Improve MinGW32 build
|
||||||
|
|
||||||
|
2009/12/11:
|
||||||
|
adding a MSVC9 project to build the static library and executable
|
||||||
|
|
||||||
|
2009/12/10:
|
||||||
|
Fixing some compilation stuff for Windows/MinGW
|
||||||
|
|
||||||
|
2009/12/07:
|
||||||
|
adaptations in Makefile and updateminiupnpcstring.sh for AmigaOS
|
||||||
|
some fixes for Windows when using virtual ethernet adapters (it is the
|
||||||
|
case with VMWare installed).
|
||||||
|
|
||||||
|
2009/12/04:
|
||||||
|
some fixes for AmigaOS compilation
|
||||||
|
Changed HTTP version to HTTP/1.0 for Soap too (to prevent chunked
|
||||||
|
transfer encoding)
|
||||||
|
|
||||||
|
2009/12/03:
|
||||||
|
updating printIDG and testigddescparse.c for debug.
|
||||||
|
modifications to compile under AmigaOS
|
||||||
|
adding a testminiwget program
|
||||||
|
Changed miniwget to advertise itself as HTTP/1.0 to prevent chunked
|
||||||
|
transfer encoding
|
||||||
|
|
||||||
|
2009/11/26:
|
||||||
|
fixing updateminiupnpcstrings.sh to take into account
|
||||||
|
which command that does not return an error code.
|
||||||
|
|
||||||
VERSION 1.4 : released 2009/10/30
|
VERSION 1.4 : released 2009/10/30
|
||||||
|
|
||||||
2009/10/16:
|
2009/10/16:
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
AM_CPPFLAGS = -DSTATICLIB $(SDL_CFLAGS) $(WZ_CPPFLAGS)
|
AM_CPPFLAGS = -DSTATICLIB $(SDL_CFLAGS) $(WZ_CPPFLAGS)
|
||||||
AM_CFLAGS = -DSTATICLIB $(WZ_CFLAGS)
|
AM_CFLAGS = -DSTATICLIB $(WZ_CFLAGS) -UDEBUG
|
||||||
|
|
||||||
noinst_LIBRARIES = libminiupnpc.a
|
noinst_LIBRARIES = libminiupnpc.a
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
bsdqueue.h \
|
bsdqueue.h \
|
||||||
codelength.h \
|
codelength.h \
|
||||||
|
connecthostport.h \
|
||||||
declspec.h \
|
declspec.h \
|
||||||
igd_desc_parse.h \
|
igd_desc_parse.h \
|
||||||
minisoap.h \
|
minisoap.h \
|
||||||
|
@ -18,6 +19,7 @@ noinst_HEADERS = \
|
||||||
upnpreplyparse.h
|
upnpreplyparse.h
|
||||||
|
|
||||||
libminiupnpc_a_SOURCES = \
|
libminiupnpc_a_SOURCES = \
|
||||||
|
connecthostport.c \
|
||||||
igd_desc_parse.c \
|
igd_desc_parse.c \
|
||||||
minisoap.c \
|
minisoap.c \
|
||||||
minissdpc.c \
|
minissdpc.c \
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
Project: miniupnp
|
Project: miniupnp
|
||||||
Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||||
Author: Thomas Bernard
|
Author: Thomas Bernard
|
||||||
Copyright (c) 2005-2008 Thomas Bernard
|
Copyright (c) 2005-2009 Thomas Bernard
|
||||||
This software is subject to the conditions detailed in the
|
This software is subject to the conditions detailed in the
|
||||||
LICENCE file provided within this distribution.
|
LICENSE file provided within this distribution.
|
||||||
|
|
||||||
For the comfort of Win32 users, bsdqueue.h is included in the distribution.
|
For the comfort of Win32 users, bsdqueue.h is included in the distribution.
|
||||||
Its licence is included in the header of the file.
|
Its licence is included in the header of the file.
|
||||||
|
@ -11,10 +11,12 @@ bsdqueue.h is a copy of the sys/queue.h of an OpenBSD system.
|
||||||
|
|
||||||
* miniupnp Client *
|
* miniupnp Client *
|
||||||
|
|
||||||
To compile, simply run 'gmake' (could be 'make').
|
To compile, simply run 'gmake' (could be 'make' on your system).
|
||||||
Under win32, to compile with MinGW, type "mingw32make.bat".
|
Under win32, to compile with MinGW, type "mingw32make.bat".
|
||||||
The compilation is known to work under linux, FreeBSD,
|
The compilation is known to work under linux, FreeBSD,
|
||||||
OpenBSD, MacOS X and cygwin.
|
OpenBSD, MacOS X, AmigaOS and cygwin.
|
||||||
|
The official AmigaOS4.1 SDK was used for AmigaOS4 and GeekGadgets for AmigaOS3.
|
||||||
|
|
||||||
To install the library and headers on the system use :
|
To install the library and headers on the system use :
|
||||||
> su
|
> su
|
||||||
> make install
|
> make install
|
||||||
|
@ -25,7 +27,7 @@ alternatively, to install in a specific location, use :
|
||||||
|
|
||||||
upnpc.c is a sample client using the libminiupnpc.
|
upnpc.c is a sample client using the libminiupnpc.
|
||||||
To use the libminiupnpc in your application, link it with
|
To use the libminiupnpc in your application, link it with
|
||||||
libminiupnpc.a and use the following functions found in miniupnpc.h,
|
libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h,
|
||||||
upnpcommands.h and miniwget.h :
|
upnpcommands.h and miniwget.h :
|
||||||
- upnpDiscover()
|
- upnpDiscover()
|
||||||
- miniwget()
|
- miniwget()
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
#ifdef MINIUPNP_EXPORTS
|
#ifdef MINIUPNP_EXPORTS
|
||||||
#define LIBSPEC __declspec(dllexport)
|
#define LIBSPEC __declspec(dllexport)
|
||||||
#else
|
#else
|
||||||
// NOTE: with the cross compiler, even if we have -DSTATICLIB, for some odd reason it still does this
|
#define LIBSPEC __declspec(dllimport)
|
||||||
// #define LIBSPEC __declspec(dllimport) which is not what we want. So we hack the line like so.
|
|
||||||
#define LIBSPEC
|
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#define LIBSPEC
|
#define LIBSPEC
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
/* $Id: igd_desc_parse.c,v 1.8 2008/04/23 11:51:06 nanard Exp $ */
|
/* $Id: igd_desc_parse.c,v 1.11 2010/12/11 17:56:51 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* http://miniupnp.free.fr/
|
* http://miniupnp.free.fr/
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2005-2008 Thomas Bernard
|
* Copyright (c) 2005-2010 Thomas Bernard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided in this distribution.
|
* LICENCE file provided in this distribution. */
|
||||||
* */
|
|
||||||
#include "igd_desc_parse.h"
|
#include "igd_desc_parse.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* TODO : rewrite this code so it correctly handle descriptions with
|
|
||||||
* both WANIPConnection and/or WANPPPConnection */
|
|
||||||
|
|
||||||
/* Start element handler :
|
/* Start element handler :
|
||||||
* update nesting level counter and copy element name */
|
* update nesting level counter and copy element name */
|
||||||
void IGDstartelt(void * d, const char * name, int l)
|
void IGDstartelt(void * d, const char * name, int l)
|
||||||
|
@ -22,10 +19,10 @@ void IGDstartelt(void * d, const char * name, int l)
|
||||||
datas->cureltname[l] = '\0';
|
datas->cureltname[l] = '\0';
|
||||||
datas->level++;
|
datas->level++;
|
||||||
if( (l==7) && !memcmp(name, "service", l) ) {
|
if( (l==7) && !memcmp(name, "service", l) ) {
|
||||||
datas->controlurl_tmp[0] = '\0';
|
datas->tmp.controlurl[0] = '\0';
|
||||||
datas->eventsuburl_tmp[0] = '\0';
|
datas->tmp.eventsuburl[0] = '\0';
|
||||||
datas->scpdurl_tmp[0] = '\0';
|
datas->tmp.scpdurl[0] = '\0';
|
||||||
datas->servicetype_tmp[0] = '\0';
|
datas->tmp.servicetype[0] = '\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,20 +43,18 @@ void IGDendelt(void * d, const char * name, int l)
|
||||||
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
|
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
|
||||||
datas->state ++;
|
datas->state ++;
|
||||||
*/
|
*/
|
||||||
if(0==strcmp(datas->servicetype_tmp,
|
if(0==strcmp(datas->tmp.servicetype,
|
||||||
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) {
|
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) {
|
||||||
memcpy(datas->controlurl_CIF, datas->controlurl_tmp, MINIUPNPC_URL_MAXSIZE);
|
memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||||
memcpy(datas->eventsuburl_CIF, datas->eventsuburl_tmp, MINIUPNPC_URL_MAXSIZE);
|
} else if(0==strcmp(datas->tmp.servicetype,
|
||||||
memcpy(datas->scpdurl_CIF, datas->scpdurl_tmp, MINIUPNPC_URL_MAXSIZE);
|
|
||||||
memcpy(datas->servicetype_CIF, datas->servicetype_tmp, MINIUPNPC_URL_MAXSIZE);
|
|
||||||
} else if(0==strcmp(datas->servicetype_tmp,
|
|
||||||
"urn:schemas-upnp-org:service:WANIPConnection:1")
|
"urn:schemas-upnp-org:service:WANIPConnection:1")
|
||||||
|| 0==strcmp(datas->servicetype_tmp,
|
|| 0==strcmp(datas->tmp.servicetype,
|
||||||
"urn:schemas-upnp-org:service:WANPPPConnection:1") ) {
|
"urn:schemas-upnp-org:service:WANPPPConnection:1") ) {
|
||||||
memcpy(datas->controlurl, datas->controlurl_tmp, MINIUPNPC_URL_MAXSIZE);
|
if(datas->first.servicetype[0] == '\0') {
|
||||||
memcpy(datas->eventsuburl, datas->eventsuburl_tmp, MINIUPNPC_URL_MAXSIZE);
|
memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||||
memcpy(datas->scpdurl, datas->scpdurl_tmp, MINIUPNPC_URL_MAXSIZE);
|
} else {
|
||||||
memcpy(datas->servicetype, datas->servicetype_tmp, MINIUPNPC_URL_MAXSIZE);
|
memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,13 +70,13 @@ void IGDdata(void * d, const char * data, int l)
|
||||||
if( !strcmp(datas->cureltname, "URLBase") )
|
if( !strcmp(datas->cureltname, "URLBase") )
|
||||||
dstmember = datas->urlbase;
|
dstmember = datas->urlbase;
|
||||||
else if( !strcmp(datas->cureltname, "serviceType") )
|
else if( !strcmp(datas->cureltname, "serviceType") )
|
||||||
dstmember = datas->servicetype_tmp;
|
dstmember = datas->tmp.servicetype;
|
||||||
else if( !strcmp(datas->cureltname, "controlURL") )
|
else if( !strcmp(datas->cureltname, "controlURL") )
|
||||||
dstmember = datas->controlurl_tmp;
|
dstmember = datas->tmp.controlurl;
|
||||||
else if( !strcmp(datas->cureltname, "eventSubURL") )
|
else if( !strcmp(datas->cureltname, "eventSubURL") )
|
||||||
dstmember = datas->eventsuburl_tmp;
|
dstmember = datas->tmp.eventsuburl;
|
||||||
else if( !strcmp(datas->cureltname, "SCPDURL") )
|
else if( !strcmp(datas->cureltname, "SCPDURL") )
|
||||||
dstmember = datas->scpdurl_tmp;
|
dstmember = datas->tmp.scpdurl;
|
||||||
/* else if( !strcmp(datas->cureltname, "deviceType") )
|
/* else if( !strcmp(datas->cureltname, "deviceType") )
|
||||||
dstmember = datas->devicetype_tmp;*/
|
dstmember = datas->devicetype_tmp;*/
|
||||||
if(dstmember)
|
if(dstmember)
|
||||||
|
@ -95,19 +90,25 @@ void IGDdata(void * d, const char * data, int l)
|
||||||
|
|
||||||
void printIGD(struct IGDdatas * d)
|
void printIGD(struct IGDdatas * d)
|
||||||
{
|
{
|
||||||
printf("urlbase = %s\n", d->urlbase);
|
printf("urlbase = '%s'\n", d->urlbase);
|
||||||
printf("WAN Device (Common interface config) :\n");
|
printf("WAN Device (Common interface config) :\n");
|
||||||
/*printf(" deviceType = %s\n", d->devicetype_CIF);*/
|
/*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/
|
||||||
printf(" serviceType = %s\n", d->servicetype_CIF);
|
printf(" serviceType = '%s'\n", d->CIF.servicetype);
|
||||||
printf(" controlURL = %s\n", d->controlurl_CIF);
|
printf(" controlURL = '%s'\n", d->CIF.controlurl);
|
||||||
printf(" eventSubURL = %s\n", d->eventsuburl_CIF);
|
printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
|
||||||
printf(" SCPDURL = %s\n", d->scpdurl_CIF);
|
printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
|
||||||
printf("WAN Connection Device (IP or PPP Connection):\n");
|
printf("primary WAN Connection Device (IP or PPP Connection):\n");
|
||||||
/*printf(" deviceType = %s\n", d->devicetype);*/
|
/*printf(" deviceType = '%s'\n", d->first.devicetype);*/
|
||||||
printf(" servicetype = %s\n", d->servicetype);
|
printf(" servicetype = '%s'\n", d->first.servicetype);
|
||||||
printf(" controlURL = %s\n", d->controlurl);
|
printf(" controlURL = '%s'\n", d->first.controlurl);
|
||||||
printf(" eventSubURL = %s\n", d->eventsuburl);
|
printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
|
||||||
printf(" SCPDURL = %s\n", d->scpdurl);
|
printf(" SCPDURL = '%s'\n", d->first.scpdurl);
|
||||||
|
printf("secondary WAN Connection Device (IP or PPP Connection):\n");
|
||||||
|
/*printf(" deviceType = '%s'\n", d->second.devicetype);*/
|
||||||
|
printf(" servicetype = '%s'\n", d->second.servicetype);
|
||||||
|
printf(" controlURL = '%s'\n", d->second.controlurl);
|
||||||
|
printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
|
||||||
|
printf(" SCPDURL = '%s'\n", d->second.scpdurl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/* $Id: igd_desc_parse.h,v 1.6 2008/04/23 11:51:07 nanard Exp $ */
|
/* $Id: igd_desc_parse.h,v 1.7 2010/04/05 20:36:59 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* http://miniupnp.free.fr/
|
* http://miniupnp.free.fr/
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2005-2008 Thomas Bernard
|
* Copyright (c) 2005-2010 Thomas Bernard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided in this distribution.
|
* LICENCE file provided in this distribution.
|
||||||
* */
|
* */
|
||||||
|
@ -12,30 +12,28 @@
|
||||||
/* Structure to store the result of the parsing of UPnP
|
/* Structure to store the result of the parsing of UPnP
|
||||||
* descriptions of Internet Gateway Devices */
|
* descriptions of Internet Gateway Devices */
|
||||||
#define MINIUPNPC_URL_MAXSIZE (128)
|
#define MINIUPNPC_URL_MAXSIZE (128)
|
||||||
|
struct IGDdatas_service {
|
||||||
|
char controlurl[MINIUPNPC_URL_MAXSIZE];
|
||||||
|
char eventsuburl[MINIUPNPC_URL_MAXSIZE];
|
||||||
|
char scpdurl[MINIUPNPC_URL_MAXSIZE];
|
||||||
|
char servicetype[MINIUPNPC_URL_MAXSIZE];
|
||||||
|
/*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
|
||||||
|
};
|
||||||
|
|
||||||
struct IGDdatas {
|
struct IGDdatas {
|
||||||
char cureltname[MINIUPNPC_URL_MAXSIZE];
|
char cureltname[MINIUPNPC_URL_MAXSIZE];
|
||||||
char urlbase[MINIUPNPC_URL_MAXSIZE];
|
char urlbase[MINIUPNPC_URL_MAXSIZE];
|
||||||
int level;
|
int level;
|
||||||
/*int state;*/
|
/*int state;*/
|
||||||
/* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
|
/* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
|
||||||
char controlurl_CIF[MINIUPNPC_URL_MAXSIZE];
|
struct IGDdatas_service CIF;
|
||||||
char eventsuburl_CIF[MINIUPNPC_URL_MAXSIZE];
|
|
||||||
char scpdurl_CIF[MINIUPNPC_URL_MAXSIZE];
|
|
||||||
char servicetype_CIF[MINIUPNPC_URL_MAXSIZE];
|
|
||||||
/*char devicetype_CIF[MINIUPNPC_URL_MAXSIZE];*/
|
|
||||||
/* "urn:schemas-upnp-org:service:WANIPConnection:1"
|
/* "urn:schemas-upnp-org:service:WANIPConnection:1"
|
||||||
* "urn:schemas-upnp-org:service:WANPPPConnection:1" */
|
* "urn:schemas-upnp-org:service:WANPPPConnection:1" */
|
||||||
char controlurl[MINIUPNPC_URL_MAXSIZE];
|
struct IGDdatas_service first;
|
||||||
char eventsuburl[MINIUPNPC_URL_MAXSIZE];
|
/* if both WANIPConnection and WANPPPConnection are present */
|
||||||
char scpdurl[MINIUPNPC_URL_MAXSIZE];
|
struct IGDdatas_service second;
|
||||||
char servicetype[MINIUPNPC_URL_MAXSIZE];
|
|
||||||
/*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
|
|
||||||
/* tmp */
|
/* tmp */
|
||||||
char controlurl_tmp[MINIUPNPC_URL_MAXSIZE];
|
struct IGDdatas_service tmp;
|
||||||
char eventsuburl_tmp[MINIUPNPC_URL_MAXSIZE];
|
|
||||||
char scpdurl_tmp[MINIUPNPC_URL_MAXSIZE];
|
|
||||||
char servicetype_tmp[MINIUPNPC_URL_MAXSIZE];
|
|
||||||
/*char devicetype_tmp[MINIUPNPC_URL_MAXSIZE];*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void IGDstartelt(void *, const char *, int);
|
void IGDstartelt(void *, const char *, int);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* $Id: minisoap.c,v 1.16 2008/10/11 16:39:29 nanard Exp $ */
|
/* $Id: minisoap.c,v 1.20 2010/12/11 17:56:51 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2005 Thomas Bernard
|
* Copyright (c) 2005-2009 Thomas Bernard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided in this distribution.
|
* LICENCE file provided in this distribution.
|
||||||
*
|
*
|
||||||
|
@ -21,8 +21,6 @@
|
||||||
#include "minisoap.h"
|
#include "minisoap.h"
|
||||||
#include "miniupnpcstrings.h"
|
#include "miniupnpcstrings.h"
|
||||||
|
|
||||||
#undef DEBUG
|
|
||||||
|
|
||||||
/* only for malloc */
|
/* only for malloc */
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
@ -77,7 +75,8 @@ int soapPostSubmit(int fd,
|
||||||
const char * host,
|
const char * host,
|
||||||
unsigned short port,
|
unsigned short port,
|
||||||
const char * action,
|
const char * action,
|
||||||
const char * body)
|
const char * body,
|
||||||
|
const char * httpversion)
|
||||||
{
|
{
|
||||||
int bodysize;
|
int bodysize;
|
||||||
char headerbuf[512];
|
char headerbuf[512];
|
||||||
|
@ -86,14 +85,16 @@ int soapPostSubmit(int fd,
|
||||||
bodysize = (int)strlen(body);
|
bodysize = (int)strlen(body);
|
||||||
/* We are not using keep-alive HTTP connections.
|
/* We are not using keep-alive HTTP connections.
|
||||||
* HTTP/1.1 needs the header Connection: close to do that.
|
* HTTP/1.1 needs the header Connection: close to do that.
|
||||||
* This is the default with HTTP/1.0 */
|
* This is the default with HTTP/1.0
|
||||||
|
* Using HTTP/1.1 means we need to support chunked transfer-encoding :
|
||||||
|
* When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked
|
||||||
|
* transfer encoding. */
|
||||||
/* Connection: Close is normally there only in HTTP/1.1 but who knows */
|
/* Connection: Close is normally there only in HTTP/1.1 but who knows */
|
||||||
portstr[0] = '\0';
|
portstr[0] = '\0';
|
||||||
if(port != 80)
|
if(port != 80)
|
||||||
snprintf(portstr, sizeof(portstr), ":%hu", port);
|
snprintf(portstr, sizeof(portstr), ":%hu", port);
|
||||||
headerssize = snprintf(headerbuf, sizeof(headerbuf),
|
headerssize = snprintf(headerbuf, sizeof(headerbuf),
|
||||||
"POST %s HTTP/1.1\r\n"
|
"POST %s HTTP/%s\r\n"
|
||||||
/* "POST %s HTTP/1.0\r\n"*/
|
|
||||||
"Host: %s%s\r\n"
|
"Host: %s%s\r\n"
|
||||||
"User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
|
"User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
|
||||||
"Content-Length: %d\r\n"
|
"Content-Length: %d\r\n"
|
||||||
|
@ -103,10 +104,14 @@ int soapPostSubmit(int fd,
|
||||||
"Cache-Control: no-cache\r\n" /* ??? */
|
"Cache-Control: no-cache\r\n" /* ??? */
|
||||||
"Pragma: no-cache\r\n"
|
"Pragma: no-cache\r\n"
|
||||||
"\r\n",
|
"\r\n",
|
||||||
url, host, portstr, bodysize, action);
|
url, httpversion, host, portstr, bodysize, action);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf("SOAP request : headersize=%d bodysize=%d\n",
|
/*printf("SOAP request : headersize=%d bodysize=%d\n",
|
||||||
headerssize, bodysize);
|
headerssize, bodysize);
|
||||||
|
*/
|
||||||
|
printf("SOAP request : POST %s HTTP/%s - Host: %s%s\n",
|
||||||
|
url, httpversion, host, portstr);
|
||||||
|
printf("SOAPAction: \"%s\" - Content-Length: %d\n", action, bodysize);
|
||||||
/*printf("%s", headerbuf);*/
|
/*printf("%s", headerbuf);*/
|
||||||
#endif
|
#endif
|
||||||
return httpWrite(fd, body, bodysize, headerbuf, headerssize);
|
return httpWrite(fd, body, bodysize, headerbuf, headerssize);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* $Id: minisoap.h,v 1.3 2006/11/19 22:32:34 nanard Exp $ */
|
/* $Id: minisoap.h,v 1.4 2010/04/12 20:39:41 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2005 Thomas Bernard
|
* Copyright (c) 2005 Thomas Bernard
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
/*int httpWrite(int, const char *, int, const char *);*/
|
/*int httpWrite(int, const char *, int, const char *);*/
|
||||||
int soapPostSubmit(int, const char *, const char *, unsigned short,
|
int soapPostSubmit(int, const char *, const char *, unsigned short,
|
||||||
const char *, const char *);
|
const char *, const char *, const char *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* $Id: minissdpc.c,v 1.10 2009/09/21 12:57:42 nanard Exp $ */
|
/* $Id: minissdpc.c,v 1.14 2010/11/25 09:57:25 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Author : Thomas BERNARD
|
* Author : Thomas BERNARD
|
||||||
* copyright (c) 2005-2009 Thomas Bernard
|
* copyright (c) 2005-2009 Thomas Bernard
|
||||||
|
@ -8,15 +8,22 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#if defined(WIN32) || defined(__amigaos__) || defined(__amigaos4__)
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
// Warzone additions
|
#include <winsock.h>
|
||||||
#include "lib/framework/types.h"
|
#include <stdint.h>
|
||||||
typedef SSIZE_T ssize_t;
|
#endif
|
||||||
// end WZ
|
#if defined(__amigaos__) || defined(__amigaos4__)
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
#if defined(__amigaos__)
|
||||||
|
#define uint16_t unsigned short
|
||||||
|
#endif
|
||||||
/* Hack */
|
/* Hack */
|
||||||
#define UNIX_PATH_LEN 108
|
#define UNIX_PATH_LEN 108
|
||||||
struct sockaddr_un {
|
struct sockaddr_un {
|
||||||
|
@ -26,7 +33,6 @@ struct sockaddr_un {
|
||||||
#else
|
#else
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "minissdpc.h"
|
#include "minissdpc.h"
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
/* $Id: miniupnpc.c,v 1.66 2009/10/10 19:15:34 nanard Exp $ */
|
/* $Id: miniupnpc.c,v 1.85 2010/12/21 16:13:14 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Author : Thomas BERNARD
|
* Author : Thomas BERNARD
|
||||||
* copyright (c) 2005-2009 Thomas Bernard
|
* copyright (c) 2005-2010 Thomas Bernard
|
||||||
* This software is subjet to the conditions detailed in the
|
* This software is subjet to the conditions detailed in the
|
||||||
* provided LICENCE file. */
|
* provided LICENSE file. */
|
||||||
#define __EXTENSIONS__ 1
|
#define __EXTENSIONS__ 1
|
||||||
#if !defined(MACOSX) && !defined(__sun)
|
#if !defined(MACOSX) && !defined(__sun)
|
||||||
#if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
|
#if !defined(_XOPEN_SOURCE) && !defined(__OpenBSD__) && !defined(__NetBSD__)
|
||||||
|
@ -16,8 +16,6 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "lib/framework/wzglobal.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -26,43 +24,52 @@
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
|
#include <iphlpapi.h>
|
||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
|
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
|
||||||
#define strncasecmp _memicmp
|
#define strncasecmp _memicmp
|
||||||
#else
|
#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||||
#define strncasecmp memicmp
|
#define strncasecmp memicmp
|
||||||
#endif
|
#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||||
#define MAXHOSTNAMELEN 64
|
#define MAXHOSTNAMELEN 64
|
||||||
#else
|
#else /* #ifdef WIN32 */
|
||||||
/* Standard POSIX includes */
|
/* Standard POSIX includes */
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#if defined(__amigaos__) && !defined(__amigaos4__)
|
||||||
|
/* Amiga OS 3 specific stuff */
|
||||||
|
#define socklen_t int
|
||||||
|
#else
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
|
#endif
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#ifdef WZ_OS_MAC
|
#include <netinet/in.h>
|
||||||
# undef _POSIX_C_SOURCE
|
|
||||||
# include <netinet/in.h>
|
|
||||||
#else
|
|
||||||
# include <netinet/in.h>
|
|
||||||
#endif
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <poll.h>
|
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#if !defined(__amigaos__) && !defined(__amigaos4__)
|
||||||
|
#include <poll.h>
|
||||||
|
#endif
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#define closesocket close
|
#define closesocket close
|
||||||
#define MINIUPNPC_IGNORE_EINTR
|
#define MINIUPNPC_IGNORE_EINTR
|
||||||
#endif
|
#endif /* #else WIN32 */
|
||||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(__amigaos__) || defined(__amigaos4__)
|
||||||
|
/* Amiga OS specific stuff */
|
||||||
|
#define TIMEVAL struct timeval
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "miniupnpc.h"
|
#include "miniupnpc.h"
|
||||||
#include "minissdpc.h"
|
#include "minissdpc.h"
|
||||||
#include "miniwget.h"
|
#include "miniwget.h"
|
||||||
#include "minisoap.h"
|
#include "minisoap.h"
|
||||||
#include "minixml.h"
|
#include "minixml.h"
|
||||||
#include "upnpcommands.h"
|
#include "upnpcommands.h"
|
||||||
|
#include "connecthostport.h"
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
|
#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
|
||||||
|
@ -92,7 +99,9 @@ LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * d
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Content-length: nnn */
|
#if 0
|
||||||
|
/* getcontentlenfromline() : parse the Content-Length HTTP header line.
|
||||||
|
* Content-length: nnn */
|
||||||
static int getcontentlenfromline(const char * p, int n)
|
static int getcontentlenfromline(const char * p, int n)
|
||||||
{
|
{
|
||||||
static const char contlenstr[] = "content-length";
|
static const char contlenstr[] = "content-length";
|
||||||
|
@ -127,6 +136,10 @@ static int getcontentlenfromline(const char * p, int n)
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* getContentLengthAndHeaderLength()
|
||||||
|
* retrieve header length and content length from an HTTP response
|
||||||
|
* TODO : retrieve Transfer-Encoding: header value, in order to support
|
||||||
|
* HTTP/1.1, chunked transfer encoding must be supported. */
|
||||||
static void
|
static void
|
||||||
getContentLengthAndHeaderLength(char * p, int n,
|
getContentLengthAndHeaderLength(char * p, int n,
|
||||||
int * contentlen, int * headerlen)
|
int * contentlen, int * headerlen)
|
||||||
|
@ -155,29 +168,27 @@ getContentLengthAndHeaderLength(char * p, int n,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* simpleUPnPcommand :
|
/* simpleUPnPcommand2 :
|
||||||
* not so simple !
|
* not so simple !
|
||||||
* return values :
|
* return values :
|
||||||
* 0 - OK
|
* 0 - OK
|
||||||
* -1 - error */
|
* -1 - error */
|
||||||
int simpleUPnPcommand(int s, const char * url, const char * service,
|
static int simpleUPnPcommand2(int s, const char * url, const char * service,
|
||||||
const char * action, struct UPNParg * args,
|
const char * action, struct UPNParg * args,
|
||||||
char * buffer, int * bufsize)
|
char * buffer, int * bufsize, const char * httpversion)
|
||||||
{
|
{
|
||||||
struct sockaddr_in dest;
|
|
||||||
char hostname[MAXHOSTNAMELEN+1];
|
char hostname[MAXHOSTNAMELEN+1];
|
||||||
unsigned short port = 0;
|
unsigned short port = 0;
|
||||||
char * path;
|
char * path;
|
||||||
char soapact[128];
|
char soapact[128];
|
||||||
char soapbody[2048];
|
char soapbody[2048];
|
||||||
char * buf;
|
char * buf;
|
||||||
int buffree;
|
/*int buffree;*/
|
||||||
int n;
|
int n;
|
||||||
int contentlen, headerlen; /* for the response */
|
/*int contentlen, headerlen;*/ /* for the response */
|
||||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
|
||||||
struct timeval timeout;
|
|
||||||
#endif
|
|
||||||
snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
|
snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
|
||||||
if(args==NULL)
|
if(args==NULL)
|
||||||
{
|
{
|
||||||
|
@ -246,68 +257,15 @@ int simpleUPnPcommand(int s, const char * url, const char * service,
|
||||||
if(!parseURL(url, hostname, &port, &path)) return -1;
|
if(!parseURL(url, hostname, &port, &path)) return -1;
|
||||||
if(s<0)
|
if(s<0)
|
||||||
{
|
{
|
||||||
s = socket(PF_INET, SOCK_STREAM, 0);
|
s = connecthostport(hostname, port);
|
||||||
if(s<0)
|
if(s < 0)
|
||||||
{
|
{
|
||||||
PRINT_SOCKET_ERROR("socket");
|
|
||||||
*bufsize = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
|
||||||
/* setting a 3 seconds timeout for the connect() call */
|
|
||||||
timeout.tv_sec = 3;
|
|
||||||
timeout.tv_usec = 0;
|
|
||||||
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
|
||||||
{
|
|
||||||
PRINT_SOCKET_ERROR("setsockopt");
|
|
||||||
}
|
|
||||||
timeout.tv_sec = 3;
|
|
||||||
timeout.tv_usec = 0;
|
|
||||||
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
|
||||||
{
|
|
||||||
PRINT_SOCKET_ERROR("setsockopt");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
dest.sin_family = AF_INET;
|
|
||||||
dest.sin_port = htons(port);
|
|
||||||
dest.sin_addr.s_addr = inet_addr(hostname);
|
|
||||||
n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr));
|
|
||||||
#ifdef MINIUPNPC_IGNORE_EINTR
|
|
||||||
while(n < 0 && errno == EINTR)
|
|
||||||
{
|
|
||||||
socklen_t len;
|
|
||||||
fd_set wset;
|
|
||||||
int err;
|
|
||||||
FD_ZERO(&wset);
|
|
||||||
FD_SET(s, &wset);
|
|
||||||
if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
|
|
||||||
continue;
|
|
||||||
/*len = 0;*/
|
|
||||||
/*n = getpeername(s, NULL, &len);*/
|
|
||||||
len = sizeof(err);
|
|
||||||
if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
|
|
||||||
PRINT_SOCKET_ERROR("getsockopt");
|
|
||||||
closesocket(s);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(err != 0) {
|
|
||||||
errno = err;
|
|
||||||
n = -1;
|
|
||||||
} else {
|
|
||||||
n = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if(n < 0)
|
|
||||||
{
|
|
||||||
PRINT_SOCKET_ERROR("connect");
|
|
||||||
closesocket(s);
|
|
||||||
*bufsize = 0;
|
*bufsize = 0;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
n = soapPostSubmit(s, path, hostname, port, soapact, soapbody);
|
n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
|
||||||
if(n<=0) {
|
if(n<=0) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf("Error sending SOAP request\n");
|
printf("Error sending SOAP request\n");
|
||||||
|
@ -316,6 +274,7 @@ int simpleUPnPcommand(int s, const char * url, const char * service,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
contentlen = -1;
|
contentlen = -1;
|
||||||
headerlen = -1;
|
headerlen = -1;
|
||||||
buf = buffer;
|
buf = buffer;
|
||||||
|
@ -335,11 +294,56 @@ int simpleUPnPcommand(int s, const char * url, const char * service,
|
||||||
if(contentlen > 0 && headerlen > 0 && *bufsize >= contentlen+headerlen)
|
if(contentlen > 0 && headerlen > 0 && *bufsize >= contentlen+headerlen)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
buf = getHTTPResponse(s, &n);
|
||||||
|
if(n > 0 && buf)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("SOAP Response :\n%.*s\n", n, buf);
|
||||||
|
#endif
|
||||||
|
if(*bufsize > n)
|
||||||
|
{
|
||||||
|
memcpy(buffer, buf, n);
|
||||||
|
*bufsize = n;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(buffer, buf, *bufsize);
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
buf = 0;
|
||||||
|
}
|
||||||
closesocket(s);
|
closesocket(s);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* simpleUPnPcommand :
|
||||||
|
* not so simple !
|
||||||
|
* return values :
|
||||||
|
* 0 - OK
|
||||||
|
* -1 - error */
|
||||||
|
int simpleUPnPcommand(int s, const char * url, const char * service,
|
||||||
|
const char * action, struct UPNParg * args,
|
||||||
|
char * buffer, int * bufsize)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
/*int origbufsize = *bufsize;*/
|
||||||
|
|
||||||
|
result = simpleUPnPcommand2(s, url, service, action, args, buffer, bufsize, "1.1");
|
||||||
|
/*
|
||||||
|
result = simpleUPnPcommand2(s, url, service, action, args, buffer, bufsize, "1.0");
|
||||||
|
if (result < 0 || *bufsize == 0)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
|
||||||
|
#endif
|
||||||
|
*bufsize = origbufsize;
|
||||||
|
result = simpleUPnPcommand2(s, url, service, action, args, buffer, bufsize, "1.1");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* parseMSEARCHReply()
|
/* parseMSEARCHReply()
|
||||||
* the last 4 arguments are filled during the parsing :
|
* the last 4 arguments are filled during the parsing :
|
||||||
* - location/locationsize : "location:" field of the SSDP reply packet
|
* - location/locationsize : "location:" field of the SSDP reply packet
|
||||||
|
@ -435,10 +439,19 @@ LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
|
||||||
char bufr[1536]; /* reception and emission buffer */
|
char bufr[1536]; /* reception and emission buffer */
|
||||||
int sudp;
|
int sudp;
|
||||||
int n;
|
int n;
|
||||||
struct sockaddr_in sockudp_r, sockudp_w;
|
struct sockaddr sockudp_r;
|
||||||
unsigned int mx;
|
unsigned int mx;
|
||||||
|
#ifdef NO_GETADDRINFO
|
||||||
|
struct sockaddr_in sockudp_w;
|
||||||
|
#else
|
||||||
|
int rv;
|
||||||
|
struct addrinfo hints, *servinfo, *p;
|
||||||
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
MIB_IPFORWARDROW ip_forward;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef WIN32
|
#if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
|
||||||
/* first try to get infos from minissdpd ! */
|
/* first try to get infos from minissdpd ! */
|
||||||
if(!minissdpdsock)
|
if(!minissdpdsock)
|
||||||
minissdpdsock = "/var/run/minissdpd.sock";
|
minissdpdsock = "/var/run/minissdpd.sock";
|
||||||
|
@ -464,16 +477,78 @@ LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* reception */
|
/* reception */
|
||||||
memset(&sockudp_r, 0, sizeof(struct sockaddr_in));
|
memset(&sockudp_r, 0, sizeof(struct sockaddr));
|
||||||
sockudp_r.sin_family = AF_INET;
|
if(0/*ipv6*/) {
|
||||||
|
struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
|
||||||
|
p->sin6_family = AF_INET6;
|
||||||
if(sameport)
|
if(sameport)
|
||||||
sockudp_r.sin_port = htons(PORT);
|
p->sin6_port = htons(PORT);
|
||||||
sockudp_r.sin_addr.s_addr = INADDR_ANY;
|
p->sin6_addr = in6addr_any;//IN6ADDR_ANY_INIT;/*INADDR_ANY;*/
|
||||||
/* emission */
|
} else {
|
||||||
memset(&sockudp_w, 0, sizeof(struct sockaddr_in));
|
struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
|
||||||
sockudp_w.sin_family = AF_INET;
|
p->sin_family = AF_INET;
|
||||||
sockudp_w.sin_port = htons(PORT);
|
if(sameport)
|
||||||
sockudp_w.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
|
p->sin_port = htons(PORT);
|
||||||
|
p->sin_addr.s_addr = INADDR_ANY;
|
||||||
|
}
|
||||||
|
#ifdef WIN32
|
||||||
|
/* This code could help us to use the right Network interface for
|
||||||
|
* SSDP multicast traffic */
|
||||||
|
/* Get IP associated with the index given in the ip_forward struct
|
||||||
|
* in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
|
||||||
|
if(GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR) {
|
||||||
|
DWORD dwRetVal = 0;
|
||||||
|
PMIB_IPADDRTABLE pIPAddrTable;
|
||||||
|
DWORD dwSize = 0;
|
||||||
|
#ifdef DEBUG
|
||||||
|
IN_ADDR IPAddr;
|
||||||
|
#endif
|
||||||
|
int i;
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
|
||||||
|
#endif
|
||||||
|
pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
|
||||||
|
if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
|
||||||
|
free(pIPAddrTable);
|
||||||
|
pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
|
||||||
|
}
|
||||||
|
if(pIPAddrTable) {
|
||||||
|
dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
|
||||||
|
#endif
|
||||||
|
for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
|
||||||
|
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
|
||||||
|
printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
|
||||||
|
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
|
||||||
|
printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
|
||||||
|
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
|
||||||
|
printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
|
||||||
|
printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
|
||||||
|
printf("\tType and State[%d]:", i);
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
|
||||||
|
/* Set the address of this interface to be used */
|
||||||
|
struct in_addr mc_if;
|
||||||
|
memset(&mc_if, 0, sizeof(mc_if));
|
||||||
|
mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
|
||||||
|
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
|
||||||
|
PRINT_SOCKET_ERROR("setsockopt");
|
||||||
|
}
|
||||||
|
((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
|
||||||
|
#ifndef DEBUG
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(pIPAddrTable);
|
||||||
|
pIPAddrTable = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
|
if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
|
||||||
|
@ -489,7 +564,10 @@ LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
|
||||||
{
|
{
|
||||||
struct in_addr mc_if;
|
struct in_addr mc_if;
|
||||||
mc_if.s_addr = inet_addr(multicastif);
|
mc_if.s_addr = inet_addr(multicastif);
|
||||||
sockudp_r.sin_addr.s_addr = mc_if.s_addr;
|
if(0/*ipv6*/) {
|
||||||
|
} else {
|
||||||
|
((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
|
||||||
|
}
|
||||||
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
|
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
|
||||||
{
|
{
|
||||||
PRINT_SOCKET_ERROR("setsockopt");
|
PRINT_SOCKET_ERROR("setsockopt");
|
||||||
|
@ -497,7 +575,7 @@ LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Avant d'envoyer le paquet on bind pour recevoir la reponse */
|
/* Avant d'envoyer le paquet on bind pour recevoir la reponse */
|
||||||
if (bind(sudp, (struct sockaddr *)&sockudp_r, sizeof(struct sockaddr_in)) != 0)
|
if (bind(sudp, &sockudp_r, 0/*ipv6*/?sizeof(struct sockaddr_in6):sizeof(struct sockaddr_in)) != 0)
|
||||||
{
|
{
|
||||||
PRINT_SOCKET_ERROR("bind");
|
PRINT_SOCKET_ERROR("bind");
|
||||||
closesocket(sudp);
|
closesocket(sudp);
|
||||||
|
@ -515,6 +593,13 @@ LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
|
||||||
n = snprintf(bufr, sizeof(bufr),
|
n = snprintf(bufr, sizeof(bufr),
|
||||||
MSearchMsgFmt, deviceList[deviceIndex++], mx);
|
MSearchMsgFmt, deviceList[deviceIndex++], mx);
|
||||||
/*printf("Sending %s", bufr);*/
|
/*printf("Sending %s", bufr);*/
|
||||||
|
#ifdef NO_GETADDRINFO
|
||||||
|
/* the following code is not using getaddrinfo */
|
||||||
|
/* emission */
|
||||||
|
memset(&sockudp_w, 0, sizeof(struct sockaddr_in));
|
||||||
|
sockudp_w.sin_family = AF_INET;
|
||||||
|
sockudp_w.sin_port = htons(PORT);
|
||||||
|
sockudp_w.sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
|
||||||
n = sendto(sudp, bufr, n, 0,
|
n = sendto(sudp, bufr, n, 0,
|
||||||
(struct sockaddr *)&sockudp_w, sizeof(struct sockaddr_in));
|
(struct sockaddr *)&sockudp_w, sizeof(struct sockaddr_in));
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
|
@ -522,6 +607,32 @@ LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
|
||||||
closesocket(sudp);
|
closesocket(sudp);
|
||||||
return devlist;
|
return devlist;
|
||||||
}
|
}
|
||||||
|
#else /* #ifdef NO_GETADDRINFO */
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = AF_UNSPEC; // AF_INET6 or AF_INET
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
/*hints.ai_flags = */
|
||||||
|
if ((rv = getaddrinfo(UPNP_MCAST_ADDR, XSTR(PORT), &hints, &servinfo)) != 0) {
|
||||||
|
#ifdef WIN32
|
||||||
|
fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
|
||||||
|
#endif
|
||||||
|
return devlist;
|
||||||
|
}
|
||||||
|
for(p = servinfo; p; p = p->ai_next) {
|
||||||
|
n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
|
||||||
|
if (n < 0) {
|
||||||
|
PRINT_SOCKET_ERROR("sendto");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeaddrinfo(servinfo);
|
||||||
|
if(n < 0) {
|
||||||
|
closesocket(sudp);
|
||||||
|
return devlist;
|
||||||
|
}
|
||||||
|
#endif /* #ifdef NO_GETADDRINFO */
|
||||||
}
|
}
|
||||||
/* Waiting for SSDP REPLY packet to M-SEARCH */
|
/* Waiting for SSDP REPLY packet to M-SEARCH */
|
||||||
n = ReceiveData(sudp, bufr, sizeof(bufr), delay);
|
n = ReceiveData(sudp, bufr, sizeof(bufr), delay);
|
||||||
|
@ -545,8 +656,21 @@ LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
|
||||||
parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
|
parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize);
|
||||||
if(st&&descURL)
|
if(st&&descURL)
|
||||||
{
|
{
|
||||||
/*printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
|
#ifdef DEBUG
|
||||||
stsize, st, urlsize, descURL); */
|
printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
|
||||||
|
stsize, st, urlsize, descURL);
|
||||||
|
#endif
|
||||||
|
for(tmp=devlist; tmp; tmp = tmp->pNext) {
|
||||||
|
if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
|
||||||
|
tmp->descURL[urlsize] == '\0' &&
|
||||||
|
memcmp(tmp->st, st, stsize) == 0 &&
|
||||||
|
tmp->st[stsize] == '\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);
|
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize);
|
||||||
tmp->pNext = devlist;
|
tmp->pNext = devlist;
|
||||||
tmp->descURL = tmp->buffer;
|
tmp->descURL = tmp->buffer;
|
||||||
|
@ -609,9 +733,9 @@ LIBSPEC void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
|
||||||
n1 = strlen(descURL);
|
n1 = strlen(descURL);
|
||||||
n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
|
n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
|
||||||
n2 = n1; n3 = n1;
|
n2 = n1; n3 = n1;
|
||||||
n1 += strlen(data->scpdurl);
|
n1 += strlen(data->first.scpdurl);
|
||||||
n2 += strlen(data->controlurl);
|
n2 += strlen(data->first.controlurl);
|
||||||
n3 += strlen(data->controlurl_CIF);
|
n3 += strlen(data->CIF.controlurl);
|
||||||
|
|
||||||
urls->ipcondescURL = (char *)malloc(n1);
|
urls->ipcondescURL = (char *)malloc(n1);
|
||||||
urls->controlURL = (char *)malloc(n2);
|
urls->controlURL = (char *)malloc(n2);
|
||||||
|
@ -626,19 +750,19 @@ LIBSPEC void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
|
||||||
strncpy(urls->controlURL, urls->ipcondescURL, n2);
|
strncpy(urls->controlURL, urls->ipcondescURL, n2);
|
||||||
strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
|
strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3);
|
||||||
|
|
||||||
url_cpy_or_cat(urls->ipcondescURL, data->scpdurl, n1);
|
url_cpy_or_cat(urls->ipcondescURL, data->first.scpdurl, n1);
|
||||||
|
|
||||||
url_cpy_or_cat(urls->controlURL, data->controlurl, n2);
|
url_cpy_or_cat(urls->controlURL, data->first.controlurl, n2);
|
||||||
|
|
||||||
url_cpy_or_cat(urls->controlURL_CIF, data->controlurl_CIF, n3);
|
url_cpy_or_cat(urls->controlURL_CIF, data->CIF.controlurl, n3);
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf("urls->ipcondescURL='%s' %d n1=%d\n", urls->ipcondescURL,
|
printf("urls->ipcondescURL='%s' %u n1=%d\n", urls->ipcondescURL,
|
||||||
strlen(urls->ipcondescURL), n1);
|
(unsigned)strlen(urls->ipcondescURL), n1);
|
||||||
printf("urls->controlURL='%s' %d n2=%d\n", urls->controlURL,
|
printf("urls->controlURL='%s' %u n2=%d\n", urls->controlURL,
|
||||||
strlen(urls->controlURL), n2);
|
(unsigned)strlen(urls->controlURL), n2);
|
||||||
printf("urls->controlURL_CIF='%s' %d n3=%d\n", urls->controlURL_CIF,
|
printf("urls->controlURL_CIF='%s' %u n3=%d\n", urls->controlURL_CIF,
|
||||||
strlen(urls->controlURL_CIF), n3);
|
(unsigned)strlen(urls->controlURL_CIF), n3);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -659,7 +783,7 @@ FreeUPNPUrls(struct UPNPUrls * urls)
|
||||||
int ReceiveData(int socket, char * data, int length, int timeout)
|
int ReceiveData(int socket, char * data, int length, int timeout)
|
||||||
{
|
{
|
||||||
int n;
|
int n;
|
||||||
#ifndef WIN32
|
#if !defined(WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
|
||||||
struct pollfd fds[1]; /* for the poll */
|
struct pollfd fds[1]; /* for the poll */
|
||||||
#ifdef MINIUPNPC_IGNORE_EINTR
|
#ifdef MINIUPNPC_IGNORE_EINTR
|
||||||
do {
|
do {
|
||||||
|
@ -711,7 +835,7 @@ UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
|
||||||
char status[64];
|
char status[64];
|
||||||
unsigned int uptime;
|
unsigned int uptime;
|
||||||
status[0] = '\0';
|
status[0] = '\0';
|
||||||
UPNP_GetStatusInfo(urls->controlURL, data->servicetype,
|
UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
|
||||||
status, &uptime, NULL);
|
status, &uptime, NULL);
|
||||||
if(0 == strcmp("Connected", status))
|
if(0 == strcmp("Connected", status))
|
||||||
{
|
{
|
||||||
|
@ -768,7 +892,7 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
|
||||||
parserootdesc(descXML, descXMLsize, data);
|
parserootdesc(descXML, descXMLsize, data);
|
||||||
free(descXML);
|
free(descXML);
|
||||||
descXML = NULL;
|
descXML = NULL;
|
||||||
if(0==strcmp(data->servicetype_CIF,
|
if(0==strcmp(data->CIF.servicetype,
|
||||||
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
|
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
|
||||||
|| state >= 3 )
|
|| state >= 3 )
|
||||||
{
|
{
|
||||||
|
@ -782,6 +906,25 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
|
||||||
if((state >= 2) || UPNPIGD_IsConnected(urls, data))
|
if((state >= 2) || UPNPIGD_IsConnected(urls, data))
|
||||||
return state;
|
return state;
|
||||||
FreeUPNPUrls(urls);
|
FreeUPNPUrls(urls);
|
||||||
|
if(data->second.servicetype[0] != '\0') {
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("We tried %s, now we try %s !\n",
|
||||||
|
data->first.servicetype, data->second.servicetype);
|
||||||
|
#endif
|
||||||
|
/* swaping WANPPPConnection and WANIPConnection ! */
|
||||||
|
memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
|
||||||
|
memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
|
||||||
|
memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
|
||||||
|
GetUPNPUrls(urls, data, dev->descURL);
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("UPNPIGD_IsConnected(%s) = %d\n",
|
||||||
|
urls->controlURL,
|
||||||
|
UPNPIGD_IsConnected(urls, data));
|
||||||
|
#endif
|
||||||
|
if((state >= 2) || UPNPIGD_IsConnected(urls, data))
|
||||||
|
return state;
|
||||||
|
FreeUPNPUrls(urls);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
memset(data, 0, sizeof(struct IGDdatas));
|
memset(data, 0, sizeof(struct IGDdatas));
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
#include "declspec.h"
|
#include "declspec.h"
|
||||||
#include "igd_desc_parse.h"
|
#include "igd_desc_parse.h"
|
||||||
|
|
||||||
#undef DEBUG
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#define OS_STRING "Other/unknown"
|
#define OS_STRING "Other/unknown"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MINIUPNPC_VERSION_STRING "1.4"
|
#define MINIUPNPC_VERSION_STRING "1.5"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1,140 +1,308 @@
|
||||||
/* $Id: miniwget.c,v 1.28 2009/10/10 19:15:35 nanard Exp $ */
|
/* $Id: miniwget.c,v 1.41 2010/12/12 23:52:02 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2005-2009 Thomas Bernard
|
* Copyright (c) 2005-2010 Thomas Bernard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided in this distribution.
|
* LICENCE file provided in this distribution. */
|
||||||
* */
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include "miniupnpc.h"
|
#include "miniupnpc.h"
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
#define MAXHOSTNAMELEN 64
|
#define MAXHOSTNAMELEN 64
|
||||||
#define MIN(x,y) (((x)<(y))?(x):(y))
|
#define MIN(x,y) (((x)<(y))?(x):(y))
|
||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
#define herror
|
|
||||||
#define socklen_t int
|
#define socklen_t int
|
||||||
#else
|
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
|
||||||
|
#define strncasecmp _memicmp
|
||||||
|
#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||||
|
#define strncasecmp memicmp
|
||||||
|
#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||||
|
#else /* #ifdef WIN32 */
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
#if defined(__amigaos__) && !defined(__amigaos4__)
|
||||||
|
#define socklen_t int
|
||||||
|
#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
|
#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netdb.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <errno.h>
|
#include <netdb.h>
|
||||||
#include <time.h>
|
|
||||||
#define closesocket close
|
#define closesocket close
|
||||||
|
/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
|
||||||
|
* during the connect() call */
|
||||||
#define MINIUPNPC_IGNORE_EINTR
|
#define MINIUPNPC_IGNORE_EINTR
|
||||||
#endif
|
#endif /* #else WIN32 */
|
||||||
#if defined(__sun) || defined(sun)
|
#if defined(__sun) || defined(sun)
|
||||||
#define MIN(x,y) (((x)<(y))?(x):(y))
|
#define MIN(x,y) (((x)<(y))?(x):(y))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "miniupnpcstrings.h"
|
#include "miniupnpcstrings.h"
|
||||||
#include "miniwget.h"
|
#include "miniwget.h"
|
||||||
|
#include "connecthostport.h"
|
||||||
|
|
||||||
/* miniwget2() :
|
/*
|
||||||
* */
|
* Read a HTTP response from a socket.
|
||||||
|
* Process Content-Length and Transfer-encoding headers.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
getHTTPResponse(int s, int * size)
|
||||||
|
{
|
||||||
|
char buf[2048];
|
||||||
|
int n;
|
||||||
|
int headers = 1;
|
||||||
|
int chunked = 0;
|
||||||
|
int content_length = -1;
|
||||||
|
unsigned int chunksize = 0;
|
||||||
|
unsigned int bytestocopy = 0;
|
||||||
|
/* buffers : */
|
||||||
|
char * header_buf;
|
||||||
|
int header_buf_len = 2048;
|
||||||
|
int header_buf_used = 0;
|
||||||
|
char * content_buf;
|
||||||
|
int content_buf_len = 2048;
|
||||||
|
int content_buf_used = 0;
|
||||||
|
|
||||||
|
header_buf = malloc(header_buf_len);
|
||||||
|
content_buf = malloc(content_buf_len);
|
||||||
|
|
||||||
|
while((n = ReceiveData(s, buf, 2048, 5000)) > 0)
|
||||||
|
{
|
||||||
|
if(headers)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int linestart=0;
|
||||||
|
int colon=0;
|
||||||
|
int valuestart=0;
|
||||||
|
if(header_buf_used + n > header_buf_len) {
|
||||||
|
header_buf = realloc(header_buf, header_buf_used + n);
|
||||||
|
header_buf_len = header_buf_used + n;
|
||||||
|
}
|
||||||
|
memcpy(header_buf + header_buf_used, buf, n);
|
||||||
|
header_buf_used += n;
|
||||||
|
for(i = 0; i < (header_buf_used-3); i++) {
|
||||||
|
if(colon <= linestart && header_buf[i]==':')
|
||||||
|
{
|
||||||
|
colon = i;
|
||||||
|
while(i < (n-3)
|
||||||
|
&& (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
|
||||||
|
i++;
|
||||||
|
valuestart = i + 1;
|
||||||
|
}
|
||||||
|
/* detecting end of line */
|
||||||
|
else if(header_buf[i]=='\r' && header_buf[i+1]=='\n')
|
||||||
|
{
|
||||||
|
if(colon > linestart && valuestart > colon)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("header='%.*s', value='%.*s'\n",
|
||||||
|
colon-linestart, header_buf+linestart,
|
||||||
|
i-valuestart, header_buf+valuestart);
|
||||||
|
#endif
|
||||||
|
if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
|
||||||
|
{
|
||||||
|
content_length = atoi(header_buf+valuestart);
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("Content-Length: %d\n", content_length);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
|
||||||
|
&& 0==strncasecmp(buf+valuestart, "chunked", 7))
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("chunked transfer-encoding!\n");
|
||||||
|
#endif
|
||||||
|
chunked = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
linestart = i+2;
|
||||||
|
colon = linestart;
|
||||||
|
valuestart = 0;
|
||||||
|
}
|
||||||
|
/* searching for the end of the HTTP headers */
|
||||||
|
if(header_buf[i]=='\r' && header_buf[i+1]=='\n'
|
||||||
|
&& header_buf[i+2]=='\r' && header_buf[i+3]=='\n')
|
||||||
|
{
|
||||||
|
headers = 0; /* end */
|
||||||
|
i += 4;
|
||||||
|
if(i < header_buf_used)
|
||||||
|
{
|
||||||
|
if(chunked)
|
||||||
|
{
|
||||||
|
while(i<header_buf_used)
|
||||||
|
{
|
||||||
|
while(i<header_buf_used && isxdigit(header_buf[i]))
|
||||||
|
{
|
||||||
|
if(header_buf[i] >= '0' && header_buf[i] <= '9')
|
||||||
|
chunksize = (chunksize << 4) + (header_buf[i] - '0');
|
||||||
|
else
|
||||||
|
chunksize = (chunksize << 4) + ((header_buf[i] | 32) - 'a' + 10);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
/* discarding chunk-extension */
|
||||||
|
while(i < header_buf_used && header_buf[i] != '\r') i++;
|
||||||
|
if(i < header_buf_used && header_buf[i] == '\r') i++;
|
||||||
|
if(i < header_buf_used && header_buf[i] == '\n') i++;
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("chunksize = %u (%x)\n", chunksize, chunksize);
|
||||||
|
#endif
|
||||||
|
if(chunksize == 0)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("end of HTTP content !\n");
|
||||||
|
#endif
|
||||||
|
goto end_of_stream;
|
||||||
|
}
|
||||||
|
bytestocopy = ((int)chunksize < header_buf_used - i)?chunksize:(header_buf_used - i);
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("chunksize=%u bytestocopy=%u (i=%d header_buf_used=%d)\n",
|
||||||
|
chunksize, bytestocopy, i, header_buf_used);
|
||||||
|
#endif
|
||||||
|
if(content_buf_len < (int)(content_buf_used + bytestocopy))
|
||||||
|
{
|
||||||
|
content_buf = realloc(content_buf, content_buf_used + bytestocopy);
|
||||||
|
content_buf_len = content_buf_used + bytestocopy;
|
||||||
|
}
|
||||||
|
memcpy(content_buf + content_buf_used, header_buf + i, bytestocopy);
|
||||||
|
content_buf_used += bytestocopy;
|
||||||
|
chunksize -= bytestocopy;
|
||||||
|
i += bytestocopy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(content_buf_len < header_buf_used - i)
|
||||||
|
{
|
||||||
|
content_buf = realloc(content_buf, header_buf_used - i);
|
||||||
|
content_buf_len = header_buf_used - i;
|
||||||
|
}
|
||||||
|
memcpy(content_buf, header_buf + i, header_buf_used - i);
|
||||||
|
content_buf_used = header_buf_used - i;
|
||||||
|
i = header_buf_used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* content */
|
||||||
|
if(chunked)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
while(i < n)
|
||||||
|
{
|
||||||
|
if(chunksize == 0)
|
||||||
|
{
|
||||||
|
/* reading chunk size */
|
||||||
|
if(i<n && buf[i] == '\r') i++;
|
||||||
|
if(i<n && buf[i] == '\n') i++;
|
||||||
|
while(i<n && isxdigit(buf[i]))
|
||||||
|
{
|
||||||
|
if(buf[i] >= '0' && buf[i] <= '9')
|
||||||
|
chunksize = (chunksize << 4) + (buf[i] - '0');
|
||||||
|
else
|
||||||
|
chunksize = (chunksize << 4) + ((buf[i] | 32) - 'a' + 10);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
while(i<n && buf[i] != '\r') i++; /* discarding chunk-extension */
|
||||||
|
if(i<n && buf[i] == '\r') i++;
|
||||||
|
if(i<n && buf[i] == '\n') i++;
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("chunksize = %u (%x)\n", chunksize, chunksize);
|
||||||
|
#endif
|
||||||
|
if(chunksize == 0)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("end of HTTP content - %d %d\n", i, n);
|
||||||
|
/*printf("'%.*s'\n", n-i, buf+i);*/
|
||||||
|
#endif
|
||||||
|
goto end_of_stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bytestocopy = ((int)chunksize < n - i)?chunksize:(n - i);
|
||||||
|
if((int)(content_buf_used + bytestocopy) > content_buf_len)
|
||||||
|
{
|
||||||
|
content_buf = (char *)realloc((void *)content_buf,
|
||||||
|
content_buf_used + bytestocopy);
|
||||||
|
content_buf_len = content_buf_used + bytestocopy;
|
||||||
|
}
|
||||||
|
memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
|
||||||
|
content_buf_used += bytestocopy;
|
||||||
|
i += bytestocopy;
|
||||||
|
chunksize -= bytestocopy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(content_buf_used + n > content_buf_len)
|
||||||
|
{
|
||||||
|
content_buf = (char *)realloc((void *)content_buf,
|
||||||
|
content_buf_used + n);
|
||||||
|
content_buf_len = content_buf_used + n;
|
||||||
|
}
|
||||||
|
memcpy(content_buf + content_buf_used, buf, n);
|
||||||
|
content_buf_used += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(content_length > 0 && content_buf_used >= content_length)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("End of HTTP content\n");
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end_of_stream:
|
||||||
|
free(header_buf); header_buf = NULL;
|
||||||
|
*size = content_buf_used;
|
||||||
|
if(content_buf_used == 0)
|
||||||
|
{
|
||||||
|
free(content_buf);
|
||||||
|
content_buf = NULL;
|
||||||
|
}
|
||||||
|
return content_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* miniwget3() :
|
||||||
|
* do all the work.
|
||||||
|
* Return NULL if something failed. */
|
||||||
static void *
|
static void *
|
||||||
miniwget2(const char * url, const char * host,
|
miniwget3(const char * url, const char * host,
|
||||||
unsigned short port, const char * path,
|
unsigned short port, const char * path,
|
||||||
int * size, char * addr_str, int addr_str_len)
|
int * size, char * addr_str, int addr_str_len, const char * httpversion)
|
||||||
{
|
{
|
||||||
char buf[2048];
|
char buf[2048];
|
||||||
int s;
|
int s;
|
||||||
struct sockaddr_in dest;
|
|
||||||
struct hostent *hp;
|
|
||||||
int n;
|
int n;
|
||||||
int len;
|
int len;
|
||||||
int sent;
|
int sent;
|
||||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
|
||||||
struct timeval timeout;
|
|
||||||
#endif
|
|
||||||
*size = 0;
|
*size = 0;
|
||||||
hp = gethostbyname(host);
|
s = connecthostport(host, port);
|
||||||
if(hp==NULL)
|
|
||||||
{
|
|
||||||
herror(host);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* memcpy((char *)&dest.sin_addr, hp->h_addr, hp->h_length); */
|
|
||||||
memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
|
|
||||||
memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
|
|
||||||
s = socket(PF_INET, SOCK_STREAM, 0);
|
|
||||||
if(s < 0)
|
if(s < 0)
|
||||||
{
|
|
||||||
perror("socket");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
|
||||||
/* setting a 3 seconds timeout for the connect() call */
|
|
||||||
timeout.tv_sec = 3;
|
|
||||||
timeout.tv_usec = 0;
|
|
||||||
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
|
||||||
{
|
|
||||||
perror("setsockopt");
|
|
||||||
}
|
|
||||||
timeout.tv_sec = 3;
|
|
||||||
timeout.tv_usec = 0;
|
|
||||||
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
|
||||||
{
|
|
||||||
perror("setsockopt");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
dest.sin_family = AF_INET;
|
|
||||||
dest.sin_port = htons(port);
|
|
||||||
n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
|
|
||||||
#ifdef MINIUPNPC_IGNORE_EINTR
|
|
||||||
while(n < 0 && errno == EINTR)
|
|
||||||
{
|
|
||||||
socklen_t len;
|
|
||||||
fd_set wset;
|
|
||||||
int err;
|
|
||||||
FD_ZERO(&wset);
|
|
||||||
FD_SET(s, &wset);
|
|
||||||
if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
|
|
||||||
continue;
|
|
||||||
/*len = 0;*/
|
|
||||||
/*n = getpeername(s, NULL, &len);*/
|
|
||||||
len = sizeof(err);
|
|
||||||
if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
|
|
||||||
perror("getsockopt");
|
|
||||||
closesocket(s);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if(err != 0) {
|
|
||||||
errno = err;
|
|
||||||
n = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if(n<0)
|
|
||||||
{
|
|
||||||
perror("connect");
|
|
||||||
closesocket(s);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get address for caller ! */
|
/* get address for caller ! */
|
||||||
if(addr_str)
|
if(addr_str)
|
||||||
{
|
{
|
||||||
struct sockaddr_in saddr;
|
struct sockaddr saddr;
|
||||||
socklen_t saddrlen;
|
socklen_t saddrlen;
|
||||||
|
|
||||||
saddrlen = sizeof(saddr);
|
saddrlen = sizeof(saddr);
|
||||||
if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
|
if(getsockname(s, &saddr, &saddrlen) < 0)
|
||||||
{
|
{
|
||||||
perror("getsockname");
|
perror("getsockname");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
#ifndef WIN32
|
#if defined(__amigaos__) && !defined(__amigaos4__)
|
||||||
inet_ntop(AF_INET, &saddr.sin_addr, addr_str, addr_str_len);
|
|
||||||
#else
|
|
||||||
/* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
|
/* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
|
||||||
* But his function make a string with the port : nn.nn.nn.nn:port */
|
* But his function make a string with the port : nn.nn.nn.nn:port */
|
||||||
/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
|
/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
|
||||||
|
@ -142,7 +310,20 @@ miniwget2(const char * url, const char * host,
|
||||||
{
|
{
|
||||||
printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
|
printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
|
||||||
}*/
|
}*/
|
||||||
strncpy(addr_str, inet_ntoa(saddr.sin_addr), addr_str_len);
|
strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
|
||||||
|
#else
|
||||||
|
/*inet_ntop(AF_INET, &saddr.sin_addr, addr_str, addr_str_len);*/
|
||||||
|
n = getnameinfo(&saddr, saddrlen,
|
||||||
|
addr_str, addr_str_len,
|
||||||
|
NULL, 0,
|
||||||
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||||
|
if(n != 0) {
|
||||||
|
#ifdef WIN32
|
||||||
|
fprintf(stderr, "getnameinfo() failed : %d\n", n);
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -151,13 +332,13 @@ miniwget2(const char * url, const char * host,
|
||||||
}
|
}
|
||||||
|
|
||||||
len = snprintf(buf, sizeof(buf),
|
len = snprintf(buf, sizeof(buf),
|
||||||
"GET %s HTTP/1.1\r\n"
|
"GET %s HTTP/%s\r\n"
|
||||||
"Host: %s:%d\r\n"
|
"Host: %s:%d\r\n"
|
||||||
"Connection: Close\r\n"
|
"Connection: Close\r\n"
|
||||||
"User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
|
"User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
|
||||||
|
|
||||||
"\r\n",
|
"\r\n",
|
||||||
path, host, port);
|
path, httpversion, host, port);
|
||||||
sent = 0;
|
sent = 0;
|
||||||
/* sending the HTTP request */
|
/* sending the HTTP request */
|
||||||
while(sent < len)
|
while(sent < len)
|
||||||
|
@ -174,53 +355,36 @@ miniwget2(const char * url, const char * host,
|
||||||
sent += n;
|
sent += n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
return getHTTPResponse(s, size);
|
||||||
int headers=1;
|
|
||||||
char * respbuffer = NULL;
|
|
||||||
int allreadyread = 0;
|
|
||||||
/*while((n = recv(s, buf, 2048, 0)) > 0)*/
|
|
||||||
while((n = ReceiveData(s, buf, 2048, 5000)) > 0)
|
|
||||||
{
|
|
||||||
if(headers)
|
|
||||||
{
|
|
||||||
int i=0;
|
|
||||||
while(i<n-3)
|
|
||||||
{
|
|
||||||
/* searching for the end of the HTTP headers */
|
|
||||||
if(buf[i]=='\r' && buf[i+1]=='\n'
|
|
||||||
&& buf[i+2]=='\r' && buf[i+3]=='\n')
|
|
||||||
{
|
|
||||||
headers = 0; /* end */
|
|
||||||
if(i<n-4)
|
|
||||||
{
|
|
||||||
/* Copy the content into respbuffet */
|
|
||||||
respbuffer = (char *)realloc((void *)respbuffer,
|
|
||||||
allreadyread+(n-i-4));
|
|
||||||
memcpy(respbuffer+allreadyread, buf + i + 4, n-i-4);
|
|
||||||
allreadyread += (n-i-4);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
respbuffer = (char *)realloc((void *)respbuffer,
|
|
||||||
allreadyread+n);
|
|
||||||
memcpy(respbuffer+allreadyread, buf, n);
|
|
||||||
allreadyread += n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*size = allreadyread;
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf("%d bytes read\n", *size);
|
|
||||||
#endif
|
|
||||||
closesocket(s);
|
|
||||||
return respbuffer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* miniwget2() :
|
||||||
|
* Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
|
||||||
|
static void *
|
||||||
|
miniwget2(const char * url, const char * host,
|
||||||
|
unsigned short port, const char * path,
|
||||||
|
int * size, char * addr_str, int addr_str_len)
|
||||||
|
{
|
||||||
|
char * respbuffer;
|
||||||
|
|
||||||
|
respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1");
|
||||||
|
/*
|
||||||
|
respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.0");
|
||||||
|
if (*size == 0)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("Retrying with HTTP/1.1\n");
|
||||||
|
#endif
|
||||||
|
free(respbuffer);
|
||||||
|
respbuffer = miniwget3(url, host, port, path, size, addr_str, addr_str_len, "1.1");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return respbuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* parseURL()
|
/* parseURL()
|
||||||
* arguments :
|
* arguments :
|
||||||
* url : source string not modified
|
* url : source string not modified
|
||||||
|
@ -234,9 +398,6 @@ miniwget2(const char * url, const char * host,
|
||||||
int parseURL(const char * url, char * hostname, unsigned short * port, char * * path)
|
int parseURL(const char * url, char * hostname, unsigned short * port, char * * path)
|
||||||
{
|
{
|
||||||
char * p1, *p2, *p3;
|
char * p1, *p2, *p3;
|
||||||
|
|
||||||
if (!url)
|
|
||||||
return 0;
|
|
||||||
p1 = strstr(url, "://");
|
p1 = strstr(url, "://");
|
||||||
if(!p1)
|
if(!p1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* $Id: miniwget.h,v 1.5 2007/01/29 20:27:23 nanard Exp $ */
|
/* $Id: miniwget.h,v 1.6 2010/12/09 16:11:33 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2005 Thomas Bernard
|
* Copyright (c) 2005 Thomas Bernard
|
||||||
|
@ -14,6 +14,8 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
LIBSPEC void * getHTTPResponse(int s, int * size);
|
||||||
|
|
||||||
LIBSPEC void * miniwget(const char *, int *);
|
LIBSPEC void * miniwget(const char *, int *);
|
||||||
|
|
||||||
LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int);
|
LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* $Id: upnpcommands.c,v 1.25 2009/07/09 16:00:42 nanard Exp $ */
|
/* $Id: upnpcommands.c,v 1.26 2010/06/09 10:59:09 nanard Exp $ */
|
||||||
/* Project : miniupnp
|
/* Project : miniupnp
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2005-2009 Thomas Bernard
|
* Copyright (c) 2005-2010 Thomas Bernard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided in this distribution.
|
* LICENCE file provided in this distribution.
|
||||||
* */
|
* */
|
||||||
|
@ -28,7 +28,9 @@ UPNP_GetTotalBytesSent(const char * controlURL,
|
||||||
int bufsize = 4096;
|
int bufsize = 4096;
|
||||||
unsigned int r = 0;
|
unsigned int r = 0;
|
||||||
char * p;
|
char * p;
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalBytesSent", 0, buffer, &bufsize);
|
if(simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalBytesSent", 0, buffer, &bufsize) < 0) {
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
ParseNameValue(buffer, bufsize, &pdata);
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
/*DisplayNameValueList(buffer, bufsize);*/
|
/*DisplayNameValueList(buffer, bufsize);*/
|
||||||
p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent");
|
p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent");
|
||||||
|
@ -48,7 +50,9 @@ UPNP_GetTotalBytesReceived(const char * controlURL,
|
||||||
int bufsize = 4096;
|
int bufsize = 4096;
|
||||||
unsigned int r = 0;
|
unsigned int r = 0;
|
||||||
char * p;
|
char * p;
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalBytesReceived", 0, buffer, &bufsize);
|
if(simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalBytesReceived", 0, buffer, &bufsize) < 0) {
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
ParseNameValue(buffer, bufsize, &pdata);
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
/*DisplayNameValueList(buffer, bufsize);*/
|
/*DisplayNameValueList(buffer, bufsize);*/
|
||||||
p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived");
|
p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived");
|
||||||
|
@ -68,7 +72,9 @@ UPNP_GetTotalPacketsSent(const char * controlURL,
|
||||||
int bufsize = 4096;
|
int bufsize = 4096;
|
||||||
unsigned int r = 0;
|
unsigned int r = 0;
|
||||||
char * p;
|
char * p;
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalPacketsSent", 0, buffer, &bufsize);
|
if(simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalPacketsSent", 0, buffer, &bufsize) < 0) {
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
ParseNameValue(buffer, bufsize, &pdata);
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
/*DisplayNameValueList(buffer, bufsize);*/
|
/*DisplayNameValueList(buffer, bufsize);*/
|
||||||
p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent");
|
p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent");
|
||||||
|
@ -88,7 +94,9 @@ UPNP_GetTotalPacketsReceived(const char * controlURL,
|
||||||
int bufsize = 4096;
|
int bufsize = 4096;
|
||||||
unsigned int r = 0;
|
unsigned int r = 0;
|
||||||
char * p;
|
char * p;
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalPacketsReceived", 0, buffer, &bufsize);
|
if(simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalPacketsReceived", 0, buffer, &bufsize) < 0) {
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
ParseNameValue(buffer, bufsize, &pdata);
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
/*DisplayNameValueList(buffer, bufsize);*/
|
/*DisplayNameValueList(buffer, bufsize);*/
|
||||||
p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived");
|
p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived");
|
||||||
|
@ -117,7 +125,9 @@ UPNP_GetStatusInfo(const char * controlURL,
|
||||||
if(!status && !uptime)
|
if(!status && !uptime)
|
||||||
return UPNPCOMMAND_INVALID_ARGS;
|
return UPNPCOMMAND_INVALID_ARGS;
|
||||||
|
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype, "GetStatusInfo", 0, buffer, &bufsize);
|
if(simpleUPnPcommand(-1, controlURL, servicetype, "GetStatusInfo", 0, buffer, &bufsize) < 0) {
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
ParseNameValue(buffer, bufsize, &pdata);
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
/*DisplayNameValueList(buffer, bufsize);*/
|
/*DisplayNameValueList(buffer, bufsize);*/
|
||||||
up = GetValueFromNameValueList(&pdata, "NewUptime");
|
up = GetValueFromNameValueList(&pdata, "NewUptime");
|
||||||
|
@ -174,8 +184,10 @@ UPNP_GetConnectionTypeInfo(const char * controlURL,
|
||||||
if(!connectionType)
|
if(!connectionType)
|
||||||
return UPNPCOMMAND_INVALID_ARGS;
|
return UPNPCOMMAND_INVALID_ARGS;
|
||||||
|
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype,
|
if(simpleUPnPcommand(-1, controlURL, servicetype,
|
||||||
"GetConnectionTypeInfo", 0, buffer, &bufsize);
|
"GetConnectionTypeInfo", 0, buffer, &bufsize) < 0) {
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
ParseNameValue(buffer, bufsize, &pdata);
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
p = GetValueFromNameValueList(&pdata, "NewConnectionType");
|
p = GetValueFromNameValueList(&pdata, "NewConnectionType");
|
||||||
/*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/
|
/*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/
|
||||||
|
@ -218,9 +230,11 @@ UPNP_GetLinkLayerMaxBitRates(const char * controlURL,
|
||||||
return UPNPCOMMAND_INVALID_ARGS;
|
return UPNPCOMMAND_INVALID_ARGS;
|
||||||
|
|
||||||
/* shouldn't we use GetCommonLinkProperties ? */
|
/* shouldn't we use GetCommonLinkProperties ? */
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype,
|
if(simpleUPnPcommand(-1, controlURL, servicetype,
|
||||||
"GetCommonLinkProperties", 0, buffer, &bufsize);
|
"GetCommonLinkProperties", 0, buffer, &bufsize) < 0) {
|
||||||
/*"GetLinkLayerMaxBitRates", 0, buffer, &bufsize);*/
|
/*"GetLinkLayerMaxBitRates", 0, buffer, &bufsize);*/
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
/*DisplayNameValueList(buffer, bufsize);*/
|
/*DisplayNameValueList(buffer, bufsize);*/
|
||||||
ParseNameValue(buffer, bufsize, &pdata);
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
/*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/
|
/*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/
|
||||||
|
@ -280,7 +294,9 @@ UPNP_GetExternalIPAddress(const char * controlURL,
|
||||||
if(!extIpAdd || !controlURL || !servicetype)
|
if(!extIpAdd || !controlURL || !servicetype)
|
||||||
return UPNPCOMMAND_INVALID_ARGS;
|
return UPNPCOMMAND_INVALID_ARGS;
|
||||||
|
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype, "GetExternalIPAddress", 0, buffer, &bufsize);
|
if(simpleUPnPcommand(-1, controlURL, servicetype, "GetExternalIPAddress", 0, buffer, &bufsize) < 0) {
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
/*DisplayNameValueList(buffer, bufsize);*/
|
/*DisplayNameValueList(buffer, bufsize);*/
|
||||||
ParseNameValue(buffer, bufsize, &pdata);
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
/*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/
|
/*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/
|
||||||
|
@ -338,7 +354,10 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
|
||||||
AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
|
AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
|
||||||
AddPortMappingArgs[7].elt = "NewLeaseDuration";
|
AddPortMappingArgs[7].elt = "NewLeaseDuration";
|
||||||
AddPortMappingArgs[7].val = "0";
|
AddPortMappingArgs[7].val = "0";
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype, "AddPortMapping", AddPortMappingArgs, buffer, &bufsize);
|
if(simpleUPnPcommand(-1, controlURL, servicetype, "AddPortMapping", AddPortMappingArgs, buffer, &bufsize) < 0) {
|
||||||
|
free(AddPortMappingArgs);
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
/*DisplayNameValueList(buffer, bufsize);*/
|
/*DisplayNameValueList(buffer, bufsize);*/
|
||||||
/*buffer[bufsize] = '\0';*/
|
/*buffer[bufsize] = '\0';*/
|
||||||
/*puts(buffer);*/
|
/*puts(buffer);*/
|
||||||
|
@ -379,9 +398,12 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
|
||||||
DeletePortMappingArgs[1].val = extPort;
|
DeletePortMappingArgs[1].val = extPort;
|
||||||
DeletePortMappingArgs[2].elt = "NewProtocol";
|
DeletePortMappingArgs[2].elt = "NewProtocol";
|
||||||
DeletePortMappingArgs[2].val = proto;
|
DeletePortMappingArgs[2].val = proto;
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype,
|
if(simpleUPnPcommand(-1, controlURL, servicetype,
|
||||||
"DeletePortMapping",
|
"DeletePortMapping",
|
||||||
DeletePortMappingArgs, buffer, &bufsize);
|
DeletePortMappingArgs, buffer, &bufsize) < 0 ) {
|
||||||
|
free(DeletePortMappingArgs);
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
/*DisplayNameValueList(buffer, bufsize);*/
|
/*DisplayNameValueList(buffer, bufsize);*/
|
||||||
ParseNameValue(buffer, bufsize, &pdata);
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
resVal = GetValueFromNameValueList(&pdata, "errorCode");
|
resVal = GetValueFromNameValueList(&pdata, "errorCode");
|
||||||
|
@ -422,9 +444,12 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
|
||||||
GetPortMappingArgs = calloc(2, sizeof(struct UPNParg));
|
GetPortMappingArgs = calloc(2, sizeof(struct UPNParg));
|
||||||
GetPortMappingArgs[0].elt = "NewPortMappingIndex";
|
GetPortMappingArgs[0].elt = "NewPortMappingIndex";
|
||||||
GetPortMappingArgs[0].val = index;
|
GetPortMappingArgs[0].val = index;
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype,
|
if(simpleUPnPcommand(-1, controlURL, servicetype,
|
||||||
"GetGenericPortMappingEntry",
|
"GetGenericPortMappingEntry",
|
||||||
GetPortMappingArgs, buffer, &bufsize);
|
GetPortMappingArgs, buffer, &bufsize) < 0) {
|
||||||
|
free(GetPortMappingArgs);
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
ParseNameValue(buffer, bufsize, &pdata);
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
p = GetValueFromNameValueList(&pdata, "NewRemoteHost");
|
p = GetValueFromNameValueList(&pdata, "NewRemoteHost");
|
||||||
if(p && rHost)
|
if(p && rHost)
|
||||||
|
@ -496,7 +521,9 @@ UPNP_GetPortMappingNumberOfEntries(const char * controlURL,
|
||||||
int bufsize = 4096;
|
int bufsize = 4096;
|
||||||
char* p;
|
char* p;
|
||||||
int ret = UPNPCOMMAND_UNKNOWN_ERROR;
|
int ret = UPNPCOMMAND_UNKNOWN_ERROR;
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype, "GetPortMappingNumberOfEntries", 0, buffer, &bufsize);
|
if(simpleUPnPcommand(-1, controlURL, servicetype, "GetPortMappingNumberOfEntries", 0, buffer, &bufsize) < 0) {
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
DisplayNameValueList(buffer, bufsize);
|
DisplayNameValueList(buffer, bufsize);
|
||||||
#endif
|
#endif
|
||||||
|
@ -546,10 +573,12 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL,
|
||||||
GetPortMappingArgs[1].val = extPort;
|
GetPortMappingArgs[1].val = extPort;
|
||||||
GetPortMappingArgs[2].elt = "NewProtocol";
|
GetPortMappingArgs[2].elt = "NewProtocol";
|
||||||
GetPortMappingArgs[2].val = proto;
|
GetPortMappingArgs[2].val = proto;
|
||||||
simpleUPnPcommand(-1, controlURL, servicetype,
|
if(simpleUPnPcommand(-1, controlURL, servicetype,
|
||||||
"GetSpecificPortMappingEntry",
|
"GetSpecificPortMappingEntry",
|
||||||
GetPortMappingArgs, buffer, &bufsize);
|
GetPortMappingArgs, buffer, &bufsize) < 0) {
|
||||||
/*fd = simpleUPnPcommand(fd, controlURL, data.servicetype, "GetSpecificPortMappingEntry", AddPortMappingArgs, buffer, &bufsize); */
|
free(GetPortMappingArgs);
|
||||||
|
return UPNPCOMMAND_HTTP_ERROR;
|
||||||
|
}
|
||||||
/*DisplayNameValueList(buffer, bufsize);*/
|
/*DisplayNameValueList(buffer, bufsize);*/
|
||||||
ParseNameValue(buffer, bufsize, &pdata);
|
ParseNameValue(buffer, bufsize, &pdata);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/* $Id: upnpcommands.h,v 1.17 2009/04/17 21:21:19 nanard Exp $ */
|
/* $Id: upnpcommands.h,v 1.18 2010/06/09 10:59:09 nanard Exp $ */
|
||||||
/* Miniupnp project : http://miniupnp.free.fr/
|
/* Miniupnp project : http://miniupnp.free.fr/
|
||||||
* Author : Thomas Bernard
|
* Author : Thomas Bernard
|
||||||
* Copyright (c) 2005-2008 Thomas Bernard
|
* Copyright (c) 2005-2010 Thomas Bernard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided within this distribution */
|
* LICENCE file provided within this distribution */
|
||||||
#ifndef __UPNPCOMMANDS_H__
|
#ifndef __UPNPCOMMANDS_H__
|
||||||
|
@ -14,6 +14,7 @@
|
||||||
#define UPNPCOMMAND_SUCCESS (0)
|
#define UPNPCOMMAND_SUCCESS (0)
|
||||||
#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
|
#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
|
||||||
#define UPNPCOMMAND_INVALID_ARGS (-2)
|
#define UPNPCOMMAND_INVALID_ARGS (-2)
|
||||||
|
#define UPNPCOMMAND_HTTP_ERROR (-3)
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
|
@ -41,9 +41,9 @@
|
||||||
#include "netlog.h"
|
#include "netlog.h"
|
||||||
#include "netsocket.h"
|
#include "netsocket.h"
|
||||||
|
|
||||||
#include "miniupnpc/miniwget.h"
|
#include <miniupnpc/miniwget.h>
|
||||||
#include "miniupnpc/miniupnpc.h"
|
#include <miniupnpc/miniupnpc.h>
|
||||||
#include "miniupnpc/upnpcommands.h"
|
#include <miniupnpc/upnpcommands.h>
|
||||||
#include "lib/exceptionhandler/dumpinfo.h"
|
#include "lib/exceptionhandler/dumpinfo.h"
|
||||||
|
|
||||||
#include "src/multistat.h"
|
#include "src/multistat.h"
|
||||||
|
@ -964,9 +964,9 @@ static bool upnp_add_redirect(int port)
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
debug(LOG_NET, "upnp_add_redir(%d)\n", port);
|
debug(LOG_NET, "upnp_add_redir(%d)\n", port);
|
||||||
UPNP_GetExternalIPAddress(urls.controlURL, data.servicetype, externalIP);
|
UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, externalIP);
|
||||||
sprintf(port_str, "%d", port);
|
sprintf(port_str, "%d", port);
|
||||||
r = UPNP_AddPortMapping(urls.controlURL, data.servicetype,
|
r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
|
||||||
port_str, port_str, lanaddr, "Warzone 2100", "TCP", 0);
|
port_str, port_str, lanaddr, "Warzone 2100", "TCP", 0);
|
||||||
if (r != UPNPCOMMAND_SUCCESS)
|
if (r != UPNPCOMMAND_SUCCESS)
|
||||||
{
|
{
|
||||||
|
@ -982,7 +982,7 @@ static void upnp_rem_redirect(int port)
|
||||||
char port_str[16];
|
char port_str[16];
|
||||||
debug(LOG_NET, "upnp_rem_redir(%d)", port);
|
debug(LOG_NET, "upnp_rem_redir(%d)", port);
|
||||||
sprintf(port_str, "%d", port);
|
sprintf(port_str, "%d", port);
|
||||||
UPNP_DeletePortMapping(urls.controlURL, data.servicetype, port_str, "TCP", 0);
|
UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port_str, "TCP", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NETaddRedirects(void)
|
void NETaddRedirects(void)
|
||||||
|
|
Loading…
Reference in New Issue