Adding miniupnpc-async
This commit is contained in:
parent
ecb382533c
commit
8271eb91b3
|
@ -0,0 +1,4 @@
|
|||
$Id: Changelog.txt,v 1.1 2009/11/12 14:04:33 nanard Exp $
|
||||
|
||||
initial version...
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
# $Id: Makefile,v 1.9 2014/11/04 22:25:00 nanard Exp $
|
||||
# MiniUPnP Project
|
||||
# http://miniupnp.free.fr/
|
||||
# (c) 2005-2014 Thomas Bernard
|
||||
# to install use :
|
||||
# $ PREFIX=/tmp/dummylocation make install
|
||||
# or
|
||||
# $ INSTALLPREFIX=/usr/local make install
|
||||
# or
|
||||
# make install (will go to /usr/bin, /usr/lib, etc...)
|
||||
CC = gcc
|
||||
#AR = gar
|
||||
CFLAGS = -O0 -g -DDEBUG
|
||||
#CFLAGS = -O
|
||||
CFLAGS += -fPIC
|
||||
CFLAGS += -ansi
|
||||
CFLAGS += -Wall -W
|
||||
CFLAGS += -D_BSD_SOURCE
|
||||
CFLAGS += -DUPNPC_USE_SELECT
|
||||
INSTALL = install
|
||||
#following libs are needed on Solaris
|
||||
#LDLIBS=-lsocket -lnsl -lresolv
|
||||
|
||||
# APIVERSION is used to build SONAME
|
||||
APIVERSION = 0
|
||||
|
||||
SRCS = miniupnpc-async.c parsessdpreply.c \
|
||||
upnputils.c igd_desc_parse.c minixml.c \
|
||||
upnpreplyparse.c \
|
||||
testasync.c
|
||||
|
||||
LIBOBJS = miniupnpc-async.o parsessdpreply.o \
|
||||
upnputils.o igd_desc_parse.o minixml.o \
|
||||
upnpreplyparse.o
|
||||
|
||||
OBJS = $(patsubst %.c,%.o,$(SRCS))
|
||||
|
||||
# HEADERS to install
|
||||
HEADERS = miniupnpc-async.h
|
||||
LIBRARY = libminiupnpc-async.a
|
||||
SHAREDLIBRARY = libminiupnpc-async.so
|
||||
SONAME = $(SHAREDLIBRARY).$(APIVERSION)
|
||||
EXECUTABLES = testasync
|
||||
|
||||
INSTALLPREFIX ?= $(PREFIX)/usr
|
||||
INSTALLDIRINC = $(INSTALLPREFIX)/include/miniupnpc
|
||||
INSTALLDIRLIB = $(INSTALLPREFIX)/lib
|
||||
INSTALLDIRBIN = $(INSTALLPREFIX)/bin
|
||||
|
||||
.PHONY: install clean depend all installpythonmodule
|
||||
|
||||
all: $(LIBRARY) $(EXECUTABLES)
|
||||
|
||||
pythonmodule: $(LIBRARY) miniupnpcmodule.c setup.py
|
||||
python setup.py build
|
||||
touch $@
|
||||
|
||||
installpythonmodule: pythonmodule
|
||||
python setup.py install
|
||||
|
||||
clean:
|
||||
$(RM) $(LIBRARY) $(SHAREDLIBRARY) $(EXECUTABLES) $(OBJS)
|
||||
# clean python stuff
|
||||
$(RM) pythonmodule
|
||||
$(RM) -r build/ dist/
|
||||
#python setup.py clean
|
||||
|
||||
install: $(LIBRARY) $(SHAREDLIBRARY)
|
||||
$(INSTALL) -d $(INSTALLDIRINC)
|
||||
$(INSTALL) -m 644 $(HEADERS) $(INSTALLDIRINC)
|
||||
$(INSTALL) -d $(INSTALLDIRLIB)
|
||||
$(INSTALL) -m 644 $(LIBRARY) $(INSTALLDIRLIB)
|
||||
$(INSTALL) -m 644 $(SHAREDLIBRARY) $(INSTALLDIRLIB)/$(SONAME)
|
||||
$(INSTALL) -m 755 upnpc-shared $(INSTALLDIRBIN)/upnpc
|
||||
ln -fs $(SONAME) $(INSTALLDIRLIB)/$(SHAREDLIBRARY)
|
||||
|
||||
cleaninstall:
|
||||
$(RM) -r $(INSTALLDIRINC)
|
||||
$(RM) $(INSTALLDIRLIB)/$(LIBRARY)
|
||||
$(RM) $(INSTALLDIRLIB)/$(SHAREDLIBRARY)
|
||||
|
||||
depend:
|
||||
makedepend -Y -- $(CFLAGS) -- $(SRCS) 2>/dev/null
|
||||
|
||||
$(LIBRARY): $(LIBOBJS)
|
||||
$(AR) crs $@ $?
|
||||
|
||||
$(SHAREDLIBRARY): $(LIBOBJS)
|
||||
$(CC) -shared -Wl,-soname,$(SONAME) -o $@ $^
|
||||
|
||||
upnpc-static: upnpc.o $(LIBRARY)
|
||||
$(CC) -o $@ $^
|
||||
|
||||
upnpc-shared: upnpc.o $(SHAREDLIBRARY)
|
||||
$(CC) -o $@ $^
|
||||
|
||||
#testasync: testasync.o libminiupnpc-async.a
|
||||
testasync: testasync.o -lminiupnpc-async
|
||||
|
||||
# DO NOT DELETE THIS LINE -- make depend depends on it.
|
||||
|
||||
miniupnpc-async.o: miniupnpc-async.h declspec.h parsessdpreply.h upnputils.h
|
||||
parsessdpreply.o: parsessdpreply.h
|
||||
testasync.o: miniupnpc-async.h declspec.h
|
|
@ -0,0 +1,10 @@
|
|||
(c) 2014 Thomas BERNARD
|
||||
http://miniupnp.free.fr/
|
||||
https://github.com/miniupnp/miniupnp
|
||||
|
||||
miniupnpc-async :
|
||||
proof of concept of a UPnP IGD client using asynchronous socket calls
|
||||
(ie non blocking sockets)
|
||||
|
||||
To be reimplemented using libevent2 (http://libevent.org/)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/* $Id: config.h,v 1.1 2012/05/20 14:58:50 nanard Exp $ */
|
||||
#ifndef __CONFIG_H__
|
||||
#define __CONFIG_H__
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef __DECLSPEC_H__
|
||||
#define __DECLSPEC_H__
|
||||
|
||||
#if defined(WIN32) && !defined(STATICLIB)
|
||||
#ifdef MINIUPNP_EXPORTS
|
||||
#define LIBSPEC __declspec(dllexport)
|
||||
#else
|
||||
#define LIBSPEC __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define LIBSPEC
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
/* $Id: igd_desc_parse.c,v 1.15 2014/07/01 13:01:17 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* http://miniupnp.free.fr/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2014 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution. */
|
||||
|
||||
#include "igd_desc_parse.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Start element handler :
|
||||
* update nesting level counter and copy element name */
|
||||
void IGDstartelt(void * d, const char * name, int l)
|
||||
{
|
||||
struct IGDdatas * datas = (struct IGDdatas *)d;
|
||||
memcpy( datas->cureltname, name, l);
|
||||
datas->cureltname[l] = '\0';
|
||||
datas->level++;
|
||||
if( (l==7) && !memcmp(name, "service", l) ) {
|
||||
datas->tmp.controlurl[0] = '\0';
|
||||
datas->tmp.eventsuburl[0] = '\0';
|
||||
datas->tmp.scpdurl[0] = '\0';
|
||||
datas->tmp.servicetype[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
|
||||
|
||||
/* End element handler :
|
||||
* update nesting level counter and update parser state if
|
||||
* service element is parsed */
|
||||
void IGDendelt(void * d, const char * name, int l)
|
||||
{
|
||||
struct IGDdatas * datas = (struct IGDdatas *)d;
|
||||
datas->level--;
|
||||
/*printf("endelt %2d %.*s\n", datas->level, l, name);*/
|
||||
if( (l==7) && !memcmp(name, "service", l) )
|
||||
{
|
||||
if(COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) {
|
||||
memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
} else if(COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANIPv6FirewallControl:")) {
|
||||
memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
} else if(COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANIPConnection:")
|
||||
|| COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANPPPConnection:") ) {
|
||||
if(datas->first.servicetype[0] == '\0') {
|
||||
memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
} else {
|
||||
memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Data handler :
|
||||
* copy data depending on the current element name and state */
|
||||
void IGDdata(void * d, const char * data, int l)
|
||||
{
|
||||
struct IGDdatas * datas = (struct IGDdatas *)d;
|
||||
char * dstmember = 0;
|
||||
/*printf("%2d %s : %.*s\n",
|
||||
datas->level, datas->cureltname, l, data); */
|
||||
if( !strcmp(datas->cureltname, "URLBase") )
|
||||
dstmember = datas->urlbase;
|
||||
else if( !strcmp(datas->cureltname, "presentationURL") )
|
||||
dstmember = datas->presentationurl;
|
||||
else if( !strcmp(datas->cureltname, "serviceType") )
|
||||
dstmember = datas->tmp.servicetype;
|
||||
else if( !strcmp(datas->cureltname, "controlURL") )
|
||||
dstmember = datas->tmp.controlurl;
|
||||
else if( !strcmp(datas->cureltname, "eventSubURL") )
|
||||
dstmember = datas->tmp.eventsuburl;
|
||||
else if( !strcmp(datas->cureltname, "SCPDURL") )
|
||||
dstmember = datas->tmp.scpdurl;
|
||||
/* else if( !strcmp(datas->cureltname, "deviceType") )
|
||||
dstmember = datas->devicetype_tmp;*/
|
||||
if(dstmember)
|
||||
{
|
||||
if(l>=MINIUPNPC_URL_MAXSIZE)
|
||||
l = MINIUPNPC_URL_MAXSIZE-1;
|
||||
memcpy(dstmember, data, l);
|
||||
dstmember[l] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void printIGD(struct IGDdatas * d)
|
||||
{
|
||||
printf("urlbase = '%s'\n", d->urlbase);
|
||||
printf("WAN Device (Common interface config) :\n");
|
||||
/*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/
|
||||
printf(" serviceType = '%s'\n", d->CIF.servicetype);
|
||||
printf(" controlURL = '%s'\n", d->CIF.controlurl);
|
||||
printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
|
||||
printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
|
||||
printf("primary WAN Connection Device (IP or PPP Connection):\n");
|
||||
/*printf(" deviceType = '%s'\n", d->first.devicetype);*/
|
||||
printf(" servicetype = '%s'\n", d->first.servicetype);
|
||||
printf(" controlURL = '%s'\n", d->first.controlurl);
|
||||
printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
|
||||
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);
|
||||
printf("WAN IPv6 Firewall Control :\n");
|
||||
/*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
|
||||
printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
|
||||
printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
|
||||
printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
|
||||
printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/* $Id: igd_desc_parse.h,v 1.11 2012/10/16 16:49:02 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* http://miniupnp.free.fr/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2010 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution.
|
||||
* */
|
||||
#ifndef IGD_DESC_PARSE_H_INCLUDED
|
||||
#define IGD_DESC_PARSE_H_INCLUDED
|
||||
|
||||
/* Structure to store the result of the parsing of UPnP
|
||||
* descriptions of Internet Gateway Devices */
|
||||
#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 {
|
||||
char cureltname[MINIUPNPC_URL_MAXSIZE];
|
||||
char urlbase[MINIUPNPC_URL_MAXSIZE];
|
||||
char presentationurl[MINIUPNPC_URL_MAXSIZE];
|
||||
int level;
|
||||
/*int state;*/
|
||||
/* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
|
||||
struct IGDdatas_service CIF;
|
||||
/* "urn:schemas-upnp-org:service:WANIPConnection:1"
|
||||
* "urn:schemas-upnp-org:service:WANPPPConnection:1" */
|
||||
struct IGDdatas_service first;
|
||||
/* if both WANIPConnection and WANPPPConnection are present */
|
||||
struct IGDdatas_service second;
|
||||
/* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
|
||||
struct IGDdatas_service IPv6FC;
|
||||
/* tmp */
|
||||
struct IGDdatas_service tmp;
|
||||
};
|
||||
|
||||
void IGDstartelt(void *, const char *, int);
|
||||
void IGDendelt(void *, const char *, int);
|
||||
void IGDdata(void *, const char *, int);
|
||||
void printIGD(struct IGDdatas *);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,963 @@
|
|||
/* $Id: miniupnpc-async.c,v 1.18 2014/11/07 11:25:52 nanard Exp $ */
|
||||
/* miniupnpc-async
|
||||
* Copyright (c) 2008-2014, Thomas BERNARD <miniupnp@free.fr>
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <stdio.h>
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <io.h>
|
||||
#define PRINT_SOCKET_ERROR printf
|
||||
#define SOCKET_ERROR GetWSALastError()
|
||||
#define WOULDBLOCK(err) (err == WSAEWOULDBLOCK)
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#define closesocket close
|
||||
#define PRINT_SOCKET_ERROR perror
|
||||
#define SOCKET_ERROR errno
|
||||
#define WOULDBLOCK(err) (err == EAGAIN || err == EWOULDBLOCK)
|
||||
#endif
|
||||
#include "miniupnpc-async.h"
|
||||
#include "parsessdpreply.h"
|
||||
#include "upnputils.h"
|
||||
#include "minixml.h"
|
||||
#include "igd_desc_parse.h"
|
||||
#include "upnpreplyparse.h"
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x,y) (((x)<(y))?(x):(y))
|
||||
#endif /* MIN */
|
||||
|
||||
#ifndef MAXHOSTNAMELEN
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#endif /* MAXHOSTNAMELEN */
|
||||
|
||||
#define SSDP_PORT 1900
|
||||
#define SSDP_MCAST_ADDR "239.255.255.250"
|
||||
#define XSTR(s) STR(s)
|
||||
#define STR(s) #s
|
||||
|
||||
#ifdef DEBUG
|
||||
#define debug_printf(...) fprintf(stderr, __VA_ARGS__)
|
||||
#else
|
||||
#define debug_printf(...)
|
||||
#endif
|
||||
|
||||
/* stuctures */
|
||||
|
||||
struct upnp_args {
|
||||
const char * elt;
|
||||
const char * val;
|
||||
};
|
||||
|
||||
/* private functions */
|
||||
|
||||
static int upnpc_connect(upnpc_t * p, const char * url);
|
||||
static int upnpc_send_request(upnpc_t * p);
|
||||
|
||||
|
||||
/* parse_msearch_reply()
|
||||
* the last 4 arguments are filled during the parsing :
|
||||
* - location/locationsize : "location:" field of the SSDP reply packet
|
||||
* - st/stsize : "st:" field of the SSDP reply packet.
|
||||
* The strings are NOT null terminated */
|
||||
static void
|
||||
parse_msearch_reply(const char * reply, int size,
|
||||
const char * * location, int * locationsize,
|
||||
const char * * st, int * stsize)
|
||||
{
|
||||
int a, b, i;
|
||||
i = 0; /* current character index */
|
||||
a = i; /* start of the line */
|
||||
b = 0; /* end of the "header" (position of the colon) */
|
||||
while(i<size) {
|
||||
switch(reply[i]) {
|
||||
case ':':
|
||||
if(b==0) {
|
||||
b = i; /* end of the "header" */
|
||||
}
|
||||
break;
|
||||
case '\x0a':
|
||||
case '\x0d':
|
||||
if(b!=0) {
|
||||
/* skip the colon and white spaces */
|
||||
do { b++; } while(reply[b]==' ' && b<size);
|
||||
if(0==strncasecmp(reply+a, "location", 8)) {
|
||||
*location = reply+b;
|
||||
*locationsize = i-b;
|
||||
} else if(0==strncasecmp(reply+a, "st", 2)) {
|
||||
*st = reply+b;
|
||||
*stsize = i-b;
|
||||
}
|
||||
b = 0;
|
||||
}
|
||||
a = i+1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
static int upnpc_send_ssdp_msearch(upnpc_t * p, const char * device, unsigned int mx)
|
||||
{
|
||||
/* envoyer les packets de M-SEARCH discovery sur le socket ssdp */
|
||||
int n;
|
||||
char bufr[1024];
|
||||
struct sockaddr_in addr;
|
||||
static const char MSearchMsgFmt[] =
|
||||
"M-SEARCH * HTTP/1.1\r\n"
|
||||
"HOST: " SSDP_MCAST_ADDR ":" XSTR(SSDP_PORT) "\r\n"
|
||||
"ST: %s\r\n"
|
||||
"MAN: \"ssdp:discover\"\r\n"
|
||||
"MX: %u\r\n"
|
||||
"\r\n";
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(SSDP_PORT);
|
||||
addr.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
|
||||
n = snprintf(bufr, sizeof(bufr),
|
||||
MSearchMsgFmt, device, mx);
|
||||
debug_printf("upnpc_send_ssdp_msearch: %s", bufr);
|
||||
n = sendto(p->ssdp_socket, bufr, n, 0,
|
||||
(struct sockaddr *)&addr, sizeof(struct sockaddr_in));
|
||||
if (n < 0) {
|
||||
int err = SOCKET_ERROR;
|
||||
if(err == EINTR || WOULDBLOCK(err)) {
|
||||
debug_printf("upnpc_send_ssdp_msearch: should try again");
|
||||
p->state = ESendSSDP;
|
||||
return 0;
|
||||
}
|
||||
PRINT_SOCKET_ERROR("sendto");
|
||||
return -1;
|
||||
}
|
||||
p->state = EReceiveSSDP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int upnpc_set_root_desc_location(upnpc_t * p, const char * location, int locationsize)
|
||||
{
|
||||
char * tmp;
|
||||
tmp = realloc(p->root_desc_location, locationsize + 1);
|
||||
if(tmp == 0) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(tmp, location, locationsize);
|
||||
tmp[locationsize] = '\0';
|
||||
p->root_desc_location = tmp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int upnpc_receive_and_parse_ssdp(upnpc_t * p)
|
||||
{
|
||||
int n;
|
||||
char bufr[1024];
|
||||
n = recv(p->ssdp_socket, bufr, sizeof(bufr), 0);
|
||||
if (n<0) {
|
||||
PRINT_SOCKET_ERROR("recv");
|
||||
} else if (n==0) {
|
||||
debug_printf("empty packet received\n");
|
||||
} else {
|
||||
const char * location = NULL;
|
||||
int locationsize;
|
||||
const char * st = NULL;
|
||||
int stsize;
|
||||
debug_printf("%.*s", n, bufr);
|
||||
parse_msearch_reply(bufr, n, &location, &locationsize, &st, &stsize);
|
||||
debug_printf("location = '%.*s'\n", locationsize, location);
|
||||
debug_printf("st = '%.*s'\n", stsize, st);
|
||||
if(location != NULL) {
|
||||
if(upnpc_set_root_desc_location(p, location, locationsize) < 0) {
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
p->state = EGetDescConnect;
|
||||
upnpc_connect(p, p->root_desc_location);
|
||||
} else {
|
||||
/* or do nothing ? */
|
||||
p->state = EError;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
parseURL(const char * url,
|
||||
char * hostname, unsigned short * port,
|
||||
char * * path, unsigned int * scope_id)
|
||||
{
|
||||
char * p1, *p2, *p3;
|
||||
if(!url)
|
||||
return 0;
|
||||
p1 = strstr(url, "://");
|
||||
if(!p1)
|
||||
return 0;
|
||||
p1 += 3;
|
||||
if( (url[0]!='h') || (url[1]!='t')
|
||||
||(url[2]!='t') || (url[3]!='p'))
|
||||
return 0;
|
||||
memset(hostname, 0, MAXHOSTNAMELEN + 1);
|
||||
if(*p1 == '[') {
|
||||
/* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
|
||||
char * scope;
|
||||
scope = strchr(p1, '%');
|
||||
p2 = strchr(p1, ']');
|
||||
if(p2 && scope && scope < p2 && scope_id) {
|
||||
/* parse scope */
|
||||
#ifdef IF_NAMESIZE
|
||||
char tmp[IF_NAMESIZE];
|
||||
int l;
|
||||
scope++;
|
||||
/* "%25" is just '%' in URL encoding */
|
||||
if(scope[0] == '2' && scope[1] == '5')
|
||||
scope += 2; /* skip "25" */
|
||||
l = p2 - scope;
|
||||
if(l >= IF_NAMESIZE)
|
||||
l = IF_NAMESIZE - 1;
|
||||
memcpy(tmp, scope, l);
|
||||
tmp[l] = '\0';
|
||||
*scope_id = if_nametoindex(tmp);
|
||||
if(*scope_id == 0) {
|
||||
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
|
||||
}
|
||||
#else
|
||||
/* under windows, scope is numerical */
|
||||
char tmp[8];
|
||||
int l;
|
||||
scope++;
|
||||
/* "%25" is just '%' in URL encoding */
|
||||
if(scope[0] == '2' && scope[1] == '5')
|
||||
scope += 2; /* skip "25" */
|
||||
l = p2 - scope;
|
||||
if(l >= (int)sizeof(tmp))
|
||||
l = sizeof(tmp) - 1;
|
||||
memcpy(tmp, scope, l);
|
||||
tmp[l] = '\0';
|
||||
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
|
||||
#endif
|
||||
}
|
||||
p3 = strchr(p1, '/');
|
||||
if(p2 && p3) {
|
||||
p2++;
|
||||
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
|
||||
if(*p2 == ':') {
|
||||
*port = 0;
|
||||
p2++;
|
||||
while( (*p2 >= '0') && (*p2 <= '9')) {
|
||||
*port *= 10;
|
||||
*port += (unsigned short)(*p2 - '0');
|
||||
p2++;
|
||||
}
|
||||
} else {
|
||||
*port = 80;
|
||||
}
|
||||
*path = p3;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
p2 = strchr(p1, ':');
|
||||
p3 = strchr(p1, '/');
|
||||
if(!p3)
|
||||
return 0;
|
||||
if(!p2 || (p2>p3)) {
|
||||
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
|
||||
*port = 80;
|
||||
} else {
|
||||
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
|
||||
*port = 0;
|
||||
p2++;
|
||||
while( (*p2 >= '0') && (*p2 <= '9')) {
|
||||
*port *= 10;
|
||||
*port += (unsigned short)(*p2 - '0');
|
||||
p2++;
|
||||
}
|
||||
}
|
||||
*path = p3;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int upnpc_connect(upnpc_t * p, const char * url)
|
||||
{
|
||||
int r;
|
||||
char hostname[MAXHOSTNAMELEN+1];
|
||||
unsigned short port;
|
||||
char * path;
|
||||
unsigned int scope_id;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen;
|
||||
|
||||
if(p->root_desc_location == 0) {
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
if(!parseURL(url/*p->root_desc_location*/, hostname, &port,
|
||||
&path, &scope_id)) {
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
p->http_socket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if(p->http_socket < 0) {
|
||||
PRINT_SOCKET_ERROR("socket");
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
if(!set_non_blocking(p->http_socket)) {
|
||||
/* TODO : ERROR */
|
||||
}
|
||||
memset(&addr, 0, sizeof(struct sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
inet_pton(AF_INET, hostname, &(addr.sin_addr));
|
||||
addr.sin_port = htons(port);
|
||||
addrlen = sizeof(struct sockaddr_in);
|
||||
do {
|
||||
r = connect(p->http_socket, (struct sockaddr *)&addr, addrlen);
|
||||
if(r < 0) {
|
||||
if(errno == EINPROGRESS) {
|
||||
/*p->state = EGetDescConnect;*/
|
||||
return 0;
|
||||
} else if(errno != EINTR) {
|
||||
PRINT_SOCKET_ERROR("connect");
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} while(r < 0 && errno == EINTR);
|
||||
if(p->state == EGetDescConnect) {
|
||||
p->state = EGetDescRequest;
|
||||
} else {
|
||||
p->state = ESoapRequest;
|
||||
}
|
||||
upnpc_send_request(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int upnpc_complete_connect(upnpc_t * p)
|
||||
{
|
||||
socklen_t len;
|
||||
int err;
|
||||
len = sizeof(err);
|
||||
if(getsockopt(p->http_socket, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
|
||||
PRINT_SOCKET_ERROR("getsockopt");
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
if(err != 0) {
|
||||
debug_printf("connect failed %d\n", err);
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
if(p->state == EGetDescConnect)
|
||||
p->state = EGetDescRequest;
|
||||
else
|
||||
p->state = ESoapRequest;
|
||||
upnpc_send_request(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int upnpc_send_request(upnpc_t * p)
|
||||
{
|
||||
ssize_t n;
|
||||
static const char reqfmt[] = "GET %s HTTP/1.1\r\n"
|
||||
"Host: %s:%hu\r\n"
|
||||
"Connection: Close\r\n"
|
||||
"User-Agent: MiniUPnPc-async\r\n"
|
||||
"\r\n";
|
||||
if(p->http_request == NULL) {
|
||||
char hostname[MAXHOSTNAMELEN+1];
|
||||
unsigned short port;
|
||||
char * path;
|
||||
unsigned int scope_id;
|
||||
int len;
|
||||
if(!parseURL(p->root_desc_location, hostname, &port,
|
||||
&path, &scope_id)) {
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
len = snprintf(NULL, 0, reqfmt, path, hostname, port);
|
||||
p->http_request = malloc(len + 1);
|
||||
if(p->http_request == NULL) {
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
p->http_request_len = snprintf(p->http_request, len + 1,
|
||||
reqfmt, path, hostname, port);
|
||||
p->http_request_sent = 0;
|
||||
}
|
||||
n = send(p->http_socket, p->http_request + p->http_request_sent,
|
||||
p->http_request_len - p->http_request_sent, 0/* flags */);
|
||||
if(n < 0) {
|
||||
PRINT_SOCKET_ERROR("send");
|
||||
p->state = EError;
|
||||
return -1;
|
||||
} else {
|
||||
debug_printf("sent %d bytes\n", (int)n);
|
||||
/*if(n == 0) {
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}*/
|
||||
p->http_request_sent += n;
|
||||
if(p->http_request_sent >= p->http_request_len) {
|
||||
/* all bytes sent */
|
||||
#if 0
|
||||
shutdown(p->http_socket, SHUT_WR); /* some routers don't like that */
|
||||
#endif
|
||||
free(p->http_request);
|
||||
p->http_request = NULL;
|
||||
p->http_request_len = 0;
|
||||
if(p->state == EGetDescRequest)
|
||||
p->state = EGetDescResponse;
|
||||
else
|
||||
p->state = ESoapResponse;
|
||||
free(p->http_response);
|
||||
p->http_response = NULL;
|
||||
p->http_response_received = 0;
|
||||
p->http_response_end_of_headers = 0;
|
||||
/* get response */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int upnpc_parse_headers(upnpc_t * p)
|
||||
{
|
||||
/* search for CR LF CR LF (end of headers)
|
||||
* recognize also LF LF */
|
||||
int i = 0;
|
||||
while(i < (p->http_response_received-1) &&
|
||||
p->http_response_end_of_headers == 0) {
|
||||
if(p->http_response[i] == '\r') {
|
||||
i++;
|
||||
if(p->http_response[i] == '\n') {
|
||||
i++;
|
||||
if(i < p->http_response_received && p->http_response[i] == '\r') {
|
||||
i++;
|
||||
if(i < p->http_response_received && p->http_response[i] == '\n') {
|
||||
p->http_response_end_of_headers = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(p->http_response[i] == '\n') {
|
||||
i++;
|
||||
if(p->http_response[i] == '\n') {
|
||||
p->http_response_end_of_headers = i + 1;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if(p->http_response_end_of_headers != 0) {
|
||||
int colon = 0;
|
||||
int linestart = 0;
|
||||
int valuestart = 0;
|
||||
p->http_response_code = -1;
|
||||
for(i = 0; i < p->http_response_end_of_headers - 1; i++) {
|
||||
if(linestart == 0) {
|
||||
/* reading HTTP response code on the 1st line */
|
||||
if(p->http_response[i] == ' ' && p->http_response_code < 0)
|
||||
p->http_response_code = 0;
|
||||
else if(p->http_response[i] >= '0' && p->http_response[i] <= '9') {
|
||||
p->http_response_code = p->http_response_code * 10 + (p->http_response[i] - '0');
|
||||
} else if(p->http_response[i] == ' ')
|
||||
linestart = 1;
|
||||
}
|
||||
if(colon <= linestart && p->http_response[i] == ':') {
|
||||
colon = i;
|
||||
while(i < p->http_response_end_of_headers - 1 &&
|
||||
(p->http_response[i+1] == ' ' || p->http_response[i+1] == '\t'))
|
||||
i++;
|
||||
valuestart = i + 1;
|
||||
} else if(p->http_response[i + 1] == '\r' ||
|
||||
p->http_response[i + 1] == '\n') {
|
||||
if(colon > linestart && valuestart > colon) {
|
||||
printf("header='%.*s', value='%.*s'\n",
|
||||
colon-linestart, p->http_response+linestart,
|
||||
i+1-valuestart, p->http_response+valuestart);
|
||||
if(0==strncasecmp(p->http_response+linestart, "content-length", colon-linestart)) {
|
||||
p->http_response_content_length = atoi(p->http_response + valuestart);
|
||||
debug_printf("Content-Length: %d\n", p->http_response_content_length);
|
||||
if(p->http_response_content_length < 0) {
|
||||
debug_printf("Content-Length overflow ? setting to 0\n");
|
||||
p->http_response_content_length = 0;
|
||||
}
|
||||
} else if(0==strncasecmp(p->http_response+linestart, "transfer-encoding", colon-linestart)
|
||||
&& 0==strncasecmp(p->http_response+valuestart, "chunked", 7)) {
|
||||
debug_printf("Chunked transfer-encoding !\n");
|
||||
p->http_response_chunked = 1;
|
||||
}
|
||||
}
|
||||
/* find next line */
|
||||
while((i < p->http_response_received) &&
|
||||
(p->http_response[i]=='\r' || p->http_response[i] == '\n'))
|
||||
i++;
|
||||
linestart = i;
|
||||
colon = linestart;
|
||||
valuestart = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char * build_url_string(const char * urlbase, const char * root_desc_url, const char * controlurl)
|
||||
{
|
||||
int l, n;
|
||||
char * s;
|
||||
const char * base;
|
||||
char * p;
|
||||
/* if controlurl is an absolute url, return it */
|
||||
if(0 == memcmp("http://", controlurl, 7))
|
||||
return strdup(controlurl);
|
||||
base = (urlbase[0] == '\0') ? root_desc_url : urlbase;
|
||||
n = strlen(base);
|
||||
if(n > 7) {
|
||||
p = strchr(base + 7, '/');
|
||||
if(p)
|
||||
n = p - base;
|
||||
}
|
||||
l = n + strlen(controlurl) + 1;
|
||||
if(controlurl[0] != '/')
|
||||
l++;
|
||||
s = malloc(l);
|
||||
if(s == NULL) return NULL;
|
||||
memcpy(s, base, n);
|
||||
if(controlurl[0] != '/')
|
||||
s[n++] = '/';
|
||||
memcpy(s + n, controlurl, l - n);
|
||||
return s;
|
||||
}
|
||||
|
||||
static int upnpc_get_response(upnpc_t * p)
|
||||
{
|
||||
ssize_t n;
|
||||
ssize_t count;
|
||||
char buffer[2048];
|
||||
if(p->http_response_content_length > 0) {
|
||||
count = p->http_response_content_length
|
||||
+ p->http_response_end_of_headers
|
||||
- p->http_response_received;
|
||||
if(count > (ssize_t)sizeof(buffer)) count = sizeof(buffer);
|
||||
} else {
|
||||
count = sizeof(buffer);
|
||||
}
|
||||
debug_printf("recv(..., %d)\n", (int)count);
|
||||
n = recv(p->http_socket, buffer, count, 0/* flags */);
|
||||
if(n < 0) {
|
||||
if(errno == EINTR || WOULDBLOCK(errno))
|
||||
return 0; /* try again later */
|
||||
PRINT_SOCKET_ERROR("read");
|
||||
p->state = EError;
|
||||
return -1;
|
||||
} else if(n == 0) {
|
||||
/* receiving finished */
|
||||
debug_printf("%.*s\n", p->http_response_received, p->http_response);
|
||||
close(p->http_socket);
|
||||
p->http_socket = -1;
|
||||
/* parse */
|
||||
if(p->http_response_end_of_headers == 0) {
|
||||
upnpc_parse_headers(p);
|
||||
}
|
||||
/* TODO : decode chunked transfer-encoding */
|
||||
/* parse xml */
|
||||
if(p->state == EGetDescResponse) {
|
||||
struct IGDdatas igd;
|
||||
struct xmlparser parser;
|
||||
memset(&igd, 0, sizeof(struct IGDdatas));
|
||||
memset(&parser, 0, sizeof(struct xmlparser));
|
||||
parser.xmlstart = p->http_response + p->http_response_end_of_headers;
|
||||
parser.xmlsize = p->http_response_received - p->http_response_end_of_headers;
|
||||
parser.data = &igd;
|
||||
parser.starteltfunc = IGDstartelt;
|
||||
parser.endeltfunc = IGDendelt;
|
||||
parser.datafunc = IGDdata;
|
||||
parsexml(&parser);
|
||||
#ifdef DEBUG
|
||||
printIGD(&igd);
|
||||
#endif /* DEBUG */
|
||||
p->control_conn_url = build_url_string(igd.urlbase, p->root_desc_location, igd.first.controlurl);
|
||||
p->control_cif_url = build_url_string(igd.urlbase, p->root_desc_location, igd.CIF.controlurl);
|
||||
debug_printf("control_conn_url='%s'\n", p->control_conn_url);
|
||||
debug_printf("control_cif_url='%s'\n", p->control_cif_url);
|
||||
} else {
|
||||
ClearNameValueList(&p->soap_response_data);
|
||||
ParseNameValue(p->http_response + p->http_response_end_of_headers,
|
||||
p->http_response_received - p->http_response_end_of_headers,
|
||||
&p->soap_response_data);
|
||||
}
|
||||
free(p->http_response);
|
||||
p->http_response = NULL;
|
||||
p->http_response_received = 0;
|
||||
p->http_response_end_of_headers = 0;
|
||||
p->state = EReady;
|
||||
} else {
|
||||
/* receiving in progress */
|
||||
debug_printf("received %d bytes:\n%.*s\n", (int)n, (int)n, buffer);
|
||||
if(p->http_response == NULL) {
|
||||
p->http_response = malloc(n);
|
||||
if(p->http_response == NULL) {
|
||||
debug_printf("failed to malloc %d bytes\n", (int)n);
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
p->http_response_received = n;
|
||||
memcpy(p->http_response, buffer, n);
|
||||
} else {
|
||||
char * tmp = realloc(p->http_response, p->http_response_received + n);
|
||||
if(tmp == NULL) {
|
||||
debug_printf("failed to realloc %d bytes\n", (int)(p->http_response_received + n));
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
p->http_response = tmp;
|
||||
memcpy(p->http_response + p->http_response_received, buffer, n);
|
||||
p->http_response_received += n;
|
||||
}
|
||||
if(p->http_response_end_of_headers == 0) {
|
||||
upnpc_parse_headers(p);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SOAPPREFIX "s"
|
||||
#define SERVICEPREFIX "u"
|
||||
#define SERVICEPREFIX2 'u'
|
||||
|
||||
static int upnpc_build_soap_request(upnpc_t * p, const char * url,
|
||||
const char * service,
|
||||
const char * action,
|
||||
const struct upnp_args * args, int arg_count)
|
||||
{
|
||||
char * body;
|
||||
const char fmt_soap[] =
|
||||
"<?xml version=\"1.0\"?>\r\n"
|
||||
"<" SOAPPREFIX ":Envelope "
|
||||
"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
|
||||
SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||
"<" SOAPPREFIX ":Body>"
|
||||
"<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
|
||||
"%s"
|
||||
"</" SERVICEPREFIX ":%s>"
|
||||
"</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
|
||||
"\r\n";
|
||||
int body_len;
|
||||
const char fmt_http[] =
|
||||
"POST %s HTTP/1.1\r\n"
|
||||
"Host: %s%s\r\n"
|
||||
"User-Agent: MiniUPnPc-async\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/xml\r\n"
|
||||
"SOAPAction: \"%s#%s\"\r\n"
|
||||
"Connection: Close\r\n"
|
||||
"Cache-Control: no-cache\r\n" /* ??? */
|
||||
"Pragma: no-cache\r\n"
|
||||
"\r\n"
|
||||
"%s";
|
||||
char hostname[MAXHOSTNAMELEN+1];
|
||||
unsigned short port;
|
||||
char * path;
|
||||
unsigned int scope_id;
|
||||
char portstr[8];
|
||||
char * args_xml = NULL;
|
||||
|
||||
if(arg_count > 0) {
|
||||
int i;
|
||||
size_t l, n;
|
||||
for(i = 0, l = 0; i < arg_count; i++) {
|
||||
/* <ELT>VAL</ELT> */
|
||||
l += strlen(args[i].elt) * 2 + strlen(args[i].val) + 5;
|
||||
}
|
||||
args_xml = malloc(++l);
|
||||
if(args_xml == NULL) {
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
for(i = 0, n = 0; i < arg_count && n < l; i++) {
|
||||
/* <ELT>VAL</ELT> */
|
||||
n += snprintf(args_xml + n, l - n, "<%s>%s</%s>",
|
||||
args[i].elt, args[i].val, args[i].elt);
|
||||
}
|
||||
}
|
||||
|
||||
body_len = snprintf(NULL, 0, fmt_soap, action, service, args_xml?args_xml:"", action);
|
||||
body = malloc(body_len + 1);
|
||||
if(body == NULL) {
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
if(snprintf(body, body_len + 1, fmt_soap, action, service, args_xml?args_xml:"", action) != body_len) {
|
||||
debug_printf("snprintf() returned strange value...\n");
|
||||
}
|
||||
free(args_xml);
|
||||
args_xml = NULL;
|
||||
if(!parseURL(url, hostname, &port, &path, &scope_id)) {
|
||||
p->state = EError;
|
||||
return -1;
|
||||
}
|
||||
if(port != 80)
|
||||
snprintf(portstr, sizeof(portstr), ":%hu", port);
|
||||
else
|
||||
portstr[0] = '\0';
|
||||
p->http_request_len = snprintf(NULL, 0, fmt_http,
|
||||
path/*url*/, hostname, portstr, body_len, service, action, body);
|
||||
free(p->http_request);
|
||||
p->http_request = malloc(p->http_request_len + 1);
|
||||
if(snprintf(p->http_request, p->http_request_len + 1, fmt_http,
|
||||
path/*url*/, hostname, portstr, body_len, service, action, body) != p->http_request_len) {
|
||||
debug_printf("snprintf() returned strange value...\n");
|
||||
}
|
||||
free(body);
|
||||
debug_printf("%s", p->http_request);
|
||||
p->http_request_sent = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* public functions */
|
||||
int upnpc_init(upnpc_t * p, const char * multicastif)
|
||||
{
|
||||
int opt = 1;
|
||||
struct sockaddr_in addr;
|
||||
if(!p)
|
||||
return UPNPC_ERR_INVALID_ARGS;
|
||||
p->state = EError;
|
||||
memset(p, 0, sizeof(upnpc_t)); /* clean everything */
|
||||
/* open the socket for SSDP */
|
||||
p->ssdp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if(p->ssdp_socket < 0) {
|
||||
return UPNPC_ERR_SOCKET_FAILED;
|
||||
}
|
||||
/* set REUSEADDR */
|
||||
#ifdef WIN32
|
||||
if(setsockopt(p->ssdp_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt)) < 0) {
|
||||
#else
|
||||
if(setsockopt(p->ssdp_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
|
||||
#endif
|
||||
/* non fatal error ! */
|
||||
}
|
||||
if(!set_non_blocking(p->ssdp_socket)) {
|
||||
/* TODO log error */
|
||||
}
|
||||
|
||||
/* receive address */
|
||||
memset(&addr, 0, sizeof(struct sockaddr_in));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
/*addr.sin_port = htons(SSDP_PORT);*/
|
||||
|
||||
if(multicastif) {
|
||||
struct in_addr mc_if;
|
||||
mc_if.s_addr = inet_addr(multicastif);
|
||||
addr.sin_addr.s_addr = mc_if.s_addr;
|
||||
if(setsockopt(p->ssdp_socket, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
|
||||
PRINT_SOCKET_ERROR("setsockopt");
|
||||
/* non fatal error ! */
|
||||
}
|
||||
}
|
||||
|
||||
/* bind the socket to the ssdp address in order to receive responses */
|
||||
if(bind(p->ssdp_socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0) {
|
||||
close(p->ssdp_socket);
|
||||
return UPNPC_ERR_BIND_FAILED;
|
||||
}
|
||||
|
||||
p->state = EInit;
|
||||
return UPNPC_OK;
|
||||
}
|
||||
|
||||
int upnpc_finalize(upnpc_t * p)
|
||||
{
|
||||
if(!p) return UPNPC_ERR_INVALID_ARGS;
|
||||
free(p->root_desc_location);
|
||||
p->root_desc_location = NULL;
|
||||
free(p->http_request);
|
||||
p->http_request = NULL;
|
||||
free(p->http_response);
|
||||
p->http_response = NULL;
|
||||
free(p->control_cif_url);
|
||||
p->control_cif_url = NULL;
|
||||
free(p->control_conn_url);
|
||||
p->control_conn_url = NULL;
|
||||
if(p->ssdp_socket >= 0) {
|
||||
close(p->ssdp_socket);
|
||||
p->ssdp_socket = -1;
|
||||
}
|
||||
if(p->http_socket >= 0) {
|
||||
close(p->http_socket);
|
||||
p->http_socket = -1;
|
||||
}
|
||||
ClearNameValueList(&p->soap_response_data);
|
||||
p->state = EFinalized;
|
||||
return UPNPC_OK;
|
||||
}
|
||||
|
||||
int upnpc_get_external_ip_address(upnpc_t * p)
|
||||
{
|
||||
upnpc_build_soap_request(p, p->control_conn_url,
|
||||
"urn:schemas-upnp-org:service:WANIPConnection:1",
|
||||
"GetExternalIPAddress", NULL, 0);
|
||||
p->state = ESoapConnect;
|
||||
upnpc_connect(p, p->control_conn_url);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upnpc_get_link_layer_max_rate(upnpc_t * p)
|
||||
{
|
||||
upnpc_build_soap_request(p, p->control_cif_url,
|
||||
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
|
||||
"GetCommonLinkProperties", NULL, 0);
|
||||
p->state = ESoapConnect;
|
||||
upnpc_connect(p, p->control_conn_url);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int upnpc_add_port_mapping(upnpc_t * p,
|
||||
const char * remote_host, unsigned short ext_port,
|
||||
unsigned short int_port, const char * int_client,
|
||||
const char * proto, const char * description,
|
||||
unsigned int lease_duration)
|
||||
{
|
||||
struct upnp_args args[8];
|
||||
char lease_duration_str[16];
|
||||
char int_port_str[8];
|
||||
char ext_port_str[8];
|
||||
|
||||
if(int_client == NULL || int_port == 0 || ext_port == 0 || proto == NULL)
|
||||
return UPNPC_ERR_INVALID_ARGS;
|
||||
snprintf(lease_duration_str, sizeof(lease_duration_str), "%u", lease_duration);
|
||||
snprintf(int_port_str, sizeof(int_port_str), "%hu", int_port);
|
||||
snprintf(ext_port_str, sizeof(ext_port_str), "%hu", ext_port);
|
||||
args[0].elt = "NewRemoteHost";
|
||||
args[0].val = remote_host?remote_host:"";
|
||||
args[1].elt = "NewExternalPort";
|
||||
args[1].val = ext_port_str;
|
||||
args[2].elt = "NewProtocol";
|
||||
args[2].val = proto;
|
||||
args[3].elt = "NewInternalPort";
|
||||
args[3].val = int_port_str;
|
||||
args[4].elt = "NewInternalClient";
|
||||
args[4].val = int_client;
|
||||
args[5].elt = "NewEnabled";
|
||||
args[5].val = "1";
|
||||
args[6].elt = "NewPortMappingDescription";
|
||||
args[6].val = description?description:"miniupnpc-async";
|
||||
args[7].elt = "NewLeaseDuration";
|
||||
args[7].val = lease_duration_str;
|
||||
upnpc_build_soap_request(p, p->control_conn_url,
|
||||
"urn:schemas-upnp-org:service:WANIPConnection:1",
|
||||
"AddPortMapping",
|
||||
args, 8);
|
||||
p->state = ESoapConnect;
|
||||
upnpc_connect(p, p->control_conn_url);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef UPNPC_USE_SELECT
|
||||
int upnpc_select_fds(upnpc_t * p, int * nfds, fd_set * readfds, fd_set * writefds)
|
||||
{
|
||||
int n = 0;
|
||||
if(!p) return UPNPC_ERR_INVALID_ARGS;
|
||||
switch(p->state) {
|
||||
case ESendSSDP:
|
||||
FD_SET(p->ssdp_socket, writefds);
|
||||
if(*nfds < p->ssdp_socket)
|
||||
*nfds = p->ssdp_socket;
|
||||
n++;
|
||||
break;
|
||||
case EReceiveSSDP:
|
||||
FD_SET(p->ssdp_socket, readfds);
|
||||
if(*nfds < p->ssdp_socket)
|
||||
*nfds = p->ssdp_socket;
|
||||
n++;
|
||||
break;
|
||||
case EGetDescConnect:
|
||||
case EGetDescRequest:
|
||||
case ESoapConnect:
|
||||
case ESoapRequest:
|
||||
FD_SET(p->http_socket, writefds);
|
||||
if(*nfds < p->http_socket)
|
||||
*nfds = p->http_socket;
|
||||
n++;
|
||||
break;
|
||||
case EGetDescResponse:
|
||||
case ESoapResponse:
|
||||
FD_SET(p->http_socket, readfds);
|
||||
if(*nfds < p->http_socket)
|
||||
*nfds = p->http_socket;
|
||||
n++;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char * devices_to_search[] = {
|
||||
"urn:schemas-upnp-org:device:InternetGatewayDevice:1",
|
||||
"urn:schemas-upnp-org:service:WANIPConnection:1",
|
||||
"urn:schemas-upnp-org:service:WANPPPConnection:1",
|
||||
"upnp:rootdevice",
|
||||
0
|
||||
};
|
||||
|
||||
int upnpc_process(upnpc_t * p)
|
||||
{
|
||||
/*
|
||||
1) Envoyer les paquets de discovery SSDP
|
||||
2) Recevoir et traiter les reponses
|
||||
3) recup les descriptions
|
||||
4) tester les etats
|
||||
*/
|
||||
if(!p) return UPNPC_ERR_INVALID_ARGS;
|
||||
debug_printf("state=%d\n", (int)p->state);
|
||||
switch(p->state) {
|
||||
case EInit:
|
||||
upnpc_send_ssdp_msearch(p, devices_to_search[0], 2);
|
||||
break;
|
||||
case ESendSSDP:
|
||||
upnpc_send_ssdp_msearch(p, devices_to_search[0], 2);
|
||||
break;
|
||||
case EReceiveSSDP:
|
||||
upnpc_receive_and_parse_ssdp(p);
|
||||
break;
|
||||
/*case EGetDesc:
|
||||
upnpc_connect(p);
|
||||
break;*/
|
||||
case EGetDescConnect:
|
||||
case ESoapConnect:
|
||||
upnpc_complete_connect(p);
|
||||
break;
|
||||
case EGetDescRequest:
|
||||
case ESoapRequest:
|
||||
upnpc_send_request(p);
|
||||
break;
|
||||
case EGetDescResponse:
|
||||
case ESoapResponse:
|
||||
upnpc_get_response(p);
|
||||
break;
|
||||
default:
|
||||
return UPNPC_ERR_UNKNOWN_STATE;
|
||||
}
|
||||
return UPNPC_OK;
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/* $Id: miniupnpc-async.h,v 1.13 2014/11/07 11:25:52 nanard Exp $ */
|
||||
/* miniupnpc-async
|
||||
* Copyright (c) 2008-2014, Thomas BERNARD <miniupnp@free.fr>
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
#ifndef MINIUPNPC_ASYNC_H_INCLUDED
|
||||
#define MINIUPNPC_ASYNC_H_INCLUDED
|
||||
|
||||
#include "declspec.h"
|
||||
#include "upnpreplyparse.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define UPNPC_OK 0
|
||||
#define UPNPC_ERR_INVALID_ARGS (-1)
|
||||
#define UPNPC_ERR_SOCKET_FAILED (-2)
|
||||
#define UPNPC_ERR_BIND_FAILED (-3)
|
||||
#define UPNPC_ERR_UNKNOWN_STATE (-4)
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
EInit = 1,
|
||||
ESendSSDP,
|
||||
EReceiveSSDP,
|
||||
/*EGetDesc,*/
|
||||
EGetDescConnect,
|
||||
EGetDescRequest,
|
||||
EGetDescResponse,
|
||||
EReady,
|
||||
ESoapConnect,
|
||||
ESoapRequest,
|
||||
ESoapResponse,
|
||||
EFinalized = 99,
|
||||
EError = 1000
|
||||
} state;
|
||||
int ssdp_socket;
|
||||
char * root_desc_location;
|
||||
int http_socket;
|
||||
char * http_request;
|
||||
int http_request_len;
|
||||
int http_request_sent;
|
||||
char * http_response;
|
||||
int http_response_received;
|
||||
int http_response_end_of_headers;
|
||||
int http_response_content_length;
|
||||
int http_response_chunked;
|
||||
int http_response_code;
|
||||
char * control_cif_url;
|
||||
char * control_conn_url;
|
||||
struct NameValueParserData soap_response_data;
|
||||
} upnpc_t;
|
||||
|
||||
int upnpc_init(upnpc_t * p, const char * multicastif);
|
||||
|
||||
int upnpc_finalize(upnpc_t * p);
|
||||
|
||||
int upnpc_get_external_ip_address(upnpc_t * p);
|
||||
|
||||
int upnpc_get_link_layer_max_rate(upnpc_t * p);
|
||||
|
||||
int upnpc_add_port_mapping(upnpc_t * p,
|
||||
const char * remote_host, unsigned short ext_port,
|
||||
unsigned short int_port, const char * int_client,
|
||||
const char * proto, const char * description,
|
||||
unsigned int lease_duration);
|
||||
|
||||
#ifdef UPNPC_USE_SELECT
|
||||
int upnpc_select_fds(upnpc_t * p, int * nfds, fd_set * readfds, fd_set * writefds);
|
||||
#endif /* UPNPC_USE_SELECT */
|
||||
|
||||
int upnpc_process(upnpc_t * p);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MINIUPNPC_ASYNC_H_INCLUDED */
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
/* $Id: minixml.c,v 1.11 2014/02/03 15:54:12 nanard Exp $ */
|
||||
/* minixml.c : the minimum size a xml parser can be ! */
|
||||
/* Project : miniupnp
|
||||
* webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* Author : Thomas Bernard
|
||||
|
||||
Copyright (c) 2005-2014, Thomas BERNARD
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
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.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "minixml.h"
|
||||
|
||||
/* parseatt : used to parse the argument list
|
||||
* return 0 (false) in case of success and -1 (true) if the end
|
||||
* of the xmlbuffer is reached. */
|
||||
static int parseatt(struct xmlparser * p)
|
||||
{
|
||||
const char * attname;
|
||||
int attnamelen;
|
||||
const char * attvalue;
|
||||
int attvaluelen;
|
||||
while(p->xml < p->xmlend)
|
||||
{
|
||||
if(*p->xml=='/' || *p->xml=='>')
|
||||
return 0;
|
||||
if( !IS_WHITE_SPACE(*p->xml) )
|
||||
{
|
||||
char sep;
|
||||
attname = p->xml;
|
||||
attnamelen = 0;
|
||||
while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
|
||||
{
|
||||
attnamelen++; p->xml++;
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
}
|
||||
while(*(p->xml++) != '=')
|
||||
{
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
}
|
||||
while(IS_WHITE_SPACE(*p->xml))
|
||||
{
|
||||
p->xml++;
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
}
|
||||
sep = *p->xml;
|
||||
if(sep=='\'' || sep=='\"')
|
||||
{
|
||||
p->xml++;
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
attvalue = p->xml;
|
||||
attvaluelen = 0;
|
||||
while(*p->xml != sep)
|
||||
{
|
||||
attvaluelen++; p->xml++;
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attvalue = p->xml;
|
||||
attvaluelen = 0;
|
||||
while( !IS_WHITE_SPACE(*p->xml)
|
||||
&& *p->xml != '>' && *p->xml != '/')
|
||||
{
|
||||
attvaluelen++; p->xml++;
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/*printf("%.*s='%.*s'\n",
|
||||
attnamelen, attname, attvaluelen, attvalue);*/
|
||||
if(p->attfunc)
|
||||
p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
|
||||
}
|
||||
p->xml++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* parseelt parse the xml stream and
|
||||
* call the callback functions when needed... */
|
||||
static void parseelt(struct xmlparser * p)
|
||||
{
|
||||
int i;
|
||||
const char * elementname;
|
||||
while(p->xml < (p->xmlend - 1))
|
||||
{
|
||||
if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "<!--", 4)))
|
||||
{
|
||||
p->xml += 3;
|
||||
/* ignore comments */
|
||||
do
|
||||
{
|
||||
p->xml++;
|
||||
if ((p->xml + 3) >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
while(memcmp(p->xml, "-->", 3) != 0);
|
||||
p->xml += 3;
|
||||
}
|
||||
else if((p->xml)[0]=='<' && (p->xml)[1]!='?')
|
||||
{
|
||||
i = 0; elementname = ++p->xml;
|
||||
while( !IS_WHITE_SPACE(*p->xml)
|
||||
&& (*p->xml!='>') && (*p->xml!='/')
|
||||
)
|
||||
{
|
||||
i++; p->xml++;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
/* to ignore namespace : */
|
||||
if(*p->xml==':')
|
||||
{
|
||||
i = 0;
|
||||
elementname = ++p->xml;
|
||||
}
|
||||
}
|
||||
if(i>0)
|
||||
{
|
||||
if(p->starteltfunc)
|
||||
p->starteltfunc(p->data, elementname, i);
|
||||
if(parseatt(p))
|
||||
return;
|
||||
if(*p->xml!='/')
|
||||
{
|
||||
const char * data;
|
||||
i = 0; data = ++p->xml;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
while( IS_WHITE_SPACE(*p->xml) )
|
||||
{
|
||||
i++; p->xml++;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
if(memcmp(p->xml, "<![CDATA[", 9) == 0)
|
||||
{
|
||||
/* CDATA handling */
|
||||
p->xml += 9;
|
||||
data = p->xml;
|
||||
i = 0;
|
||||
while(memcmp(p->xml, "]]>", 3) != 0)
|
||||
{
|
||||
i++; p->xml++;
|
||||
if ((p->xml + 3) >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
if(i>0 && p->datafunc)
|
||||
p->datafunc(p->data, data, i);
|
||||
while(*p->xml!='<')
|
||||
{
|
||||
p->xml++;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(*p->xml!='<')
|
||||
{
|
||||
i++; p->xml++;
|
||||
if ((p->xml + 1) >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
if(i>0 && p->datafunc && *(p->xml + 1) == '/')
|
||||
p->datafunc(p->data, data, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(*p->xml == '/')
|
||||
{
|
||||
i = 0; elementname = ++p->xml;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
while((*p->xml != '>'))
|
||||
{
|
||||
i++; p->xml++;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
if(p->endeltfunc)
|
||||
p->endeltfunc(p->data, elementname, i);
|
||||
p->xml++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p->xml++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* the parser must be initialized before calling this function */
|
||||
void parsexml(struct xmlparser * parser)
|
||||
{
|
||||
parser->xml = parser->xmlstart;
|
||||
parser->xmlend = parser->xmlstart + parser->xmlsize;
|
||||
parseelt(parser);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/* $Id: minixml.h,v 1.7 2012/09/27 15:42:10 nanard Exp $ */
|
||||
/* minimal xml parser
|
||||
*
|
||||
* Project : miniupnp
|
||||
* Website : http://miniupnp.free.fr/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution.
|
||||
* */
|
||||
#ifndef MINIXML_H_INCLUDED
|
||||
#define MINIXML_H_INCLUDED
|
||||
#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
|
||||
|
||||
/* if a callback function pointer is set to NULL,
|
||||
* the function is not called */
|
||||
struct xmlparser {
|
||||
const char *xmlstart;
|
||||
const char *xmlend;
|
||||
const char *xml; /* pointer to current character */
|
||||
int xmlsize;
|
||||
void * data;
|
||||
void (*starteltfunc) (void *, const char *, int);
|
||||
void (*endeltfunc) (void *, const char *, int);
|
||||
void (*datafunc) (void *, const char *, int);
|
||||
void (*attfunc) (void *, const char *, int, const char *, int);
|
||||
};
|
||||
|
||||
/* parsexml()
|
||||
* the xmlparser structure must be initialized before the call
|
||||
* the following structure members have to be initialized :
|
||||
* xmlstart, xmlsize, data, *func
|
||||
* xml is for internal usage, xmlend is computed automatically */
|
||||
void parsexml(struct xmlparser *);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/* $Id: parsessdpreply.c,v 1.2 2009/11/14 10:37:55 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* Author : Thomas Bernard
|
||||
* copyright (c) 2005-2009 Thomas Bernard
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
#include <strings.h>
|
||||
#include "parsessdpreply.h"
|
||||
|
||||
/* parseMSEARCHReply()
|
||||
* the last 4 arguments are filled during the parsing :
|
||||
* - location/locationsize : "location:" field of the SSDP reply packet
|
||||
* - st/stsize : "st:" field of the SSDP reply packet.
|
||||
* The strings are NOT null terminated */
|
||||
void
|
||||
parseMSEARCHReply(const char * reply, int size,
|
||||
const char * * location, int * locationsize,
|
||||
const char * * st, int * stsize)
|
||||
{
|
||||
int a, b, i;
|
||||
i = 0;
|
||||
a = i; /* start of the line */
|
||||
b = 0;
|
||||
while(i<size)
|
||||
{
|
||||
switch(reply[i])
|
||||
{
|
||||
case ':':
|
||||
if(b==0)
|
||||
{
|
||||
b = i; /* end of the "header" */
|
||||
/*for(j=a; j<b; j++)
|
||||
{
|
||||
putchar(reply[j]);
|
||||
}
|
||||
*/
|
||||
}
|
||||
break;
|
||||
case '\x0a':
|
||||
case '\x0d':
|
||||
if(b!=0)
|
||||
{
|
||||
/*for(j=b+1; j<i; j++)
|
||||
{
|
||||
putchar(reply[j]);
|
||||
}
|
||||
putchar('\n');*/
|
||||
do { b++; } while(reply[b]==' ');
|
||||
if(0==strncasecmp(reply+a, "location", 8))
|
||||
{
|
||||
*location = reply+b;
|
||||
*locationsize = i-b;
|
||||
}
|
||||
else if(0==strncasecmp(reply+a, "st", 2))
|
||||
{
|
||||
*st = reply+b;
|
||||
*stsize = i-b;
|
||||
}
|
||||
b = 0;
|
||||
}
|
||||
a = i+1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/* $Id: parsessdpreply.h,v 1.1 2009/11/14 10:37:55 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* website : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* Author : Thomas Bernard
|
||||
* copyright (c) 2005-2009 Thomas Bernard
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
#ifndef __PARSESSDPREPLY_H__
|
||||
#define __PARSESSDPREPLY_H__
|
||||
|
||||
/* parseMSEARCHReply()
|
||||
* the last 4 arguments are filled during the parsing :
|
||||
* - location/locationsize : "location:" field of the SSDP reply packet
|
||||
* - st/stsize : "st:" field of the SSDP reply packet.
|
||||
* The strings are NOT null terminated */
|
||||
void
|
||||
parseMSEARCHReply(const char * reply, int size,
|
||||
const char * * location, int * locationsize,
|
||||
const char * * st, int * stsize);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
/* $Id: testasync.c,v 1.13 2014/11/07 11:25:52 nanard Exp $ */
|
||||
/* miniupnpc-async
|
||||
* Copyright (c) 2008-2014, Thomas BERNARD <miniupnp@free.fr>
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
/* compile with -DUPNPC_USE_SELECT to enable upnpc_select_fds() function */
|
||||
#include "miniupnpc-async.h"
|
||||
#include "upnpreplyparse.h"
|
||||
|
||||
enum methods {
|
||||
EGetExternalIP,
|
||||
EGetRates,
|
||||
EAddPortMapping,
|
||||
ENothing
|
||||
};
|
||||
|
||||
int main(int argc, char * * argv)
|
||||
{
|
||||
int r, n;
|
||||
upnpc_t upnp;
|
||||
const char * multicastif = NULL;
|
||||
enum methods next_method_to_call = EGetExternalIP;
|
||||
enum methods last_method = ENothing;
|
||||
if(argc>1)
|
||||
multicastif = argv[1];
|
||||
if((r = upnpc_init(&upnp, multicastif)) < 0) {
|
||||
fprintf(stderr, "upnpc_init failed : %d", r);
|
||||
return 1;
|
||||
}
|
||||
r = upnpc_process(&upnp);
|
||||
printf("upnpc_process returned %d\n", r);
|
||||
while((upnp.state != EReady) && (upnp.state != EError)) {
|
||||
int nfds;
|
||||
fd_set readfds;
|
||||
fd_set writefds;
|
||||
/*struct timeval timeout;*/
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
nfds = 0;
|
||||
n = upnpc_select_fds(&upnp, &nfds, &readfds, &writefds);
|
||||
if(n <= 0) {
|
||||
printf("nothing to select()...\n");
|
||||
break;
|
||||
}
|
||||
#if 0
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
#endif
|
||||
printf("select(%d, ...);\n", nfds+1);
|
||||
if(select(nfds+1, &readfds, &writefds, NULL, NULL/*&timeout*/) < 0) {
|
||||
perror("select");
|
||||
return 1;
|
||||
}
|
||||
r = upnpc_process(&upnp);
|
||||
printf("upnpc_process returned %d\n", r);
|
||||
if(upnp.state == EReady) {
|
||||
char * p;
|
||||
printf("Process UPnP IGD Method results : HTTP %d\n", upnp.http_response_code);
|
||||
if(upnp.http_response_code == 200) {
|
||||
switch(last_method) {
|
||||
case EGetExternalIP:
|
||||
p = GetValueFromNameValueList(&upnp.soap_response_data, "NewExternalIPAddress");
|
||||
printf("ExternalIPAddress = %s\n", p);
|
||||
/* p = GetValueFromNameValueList(&pdata, "errorCode");*/
|
||||
break;
|
||||
case EGetRates:
|
||||
p = GetValueFromNameValueList(&upnp.soap_response_data, "NewLayer1DownstreamMaxBitRate");
|
||||
printf("DownStream MaxBitRate = %s\t", p);
|
||||
p = GetValueFromNameValueList(&upnp.soap_response_data, "NewLayer1UpstreamMaxBitRate");
|
||||
printf("UpStream MaxBitRate = %s\n", p);
|
||||
break;
|
||||
case EAddPortMapping:
|
||||
case ENothing:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
printf("SOAP error :\n");
|
||||
printf(" faultcode='%s'\n", GetValueFromNameValueList(&upnp.soap_response_data, "faultcode"));
|
||||
printf(" faultstring='%s'\n", GetValueFromNameValueList(&upnp.soap_response_data, "faultstring"));
|
||||
printf(" errorCode=%s\n", GetValueFromNameValueList(&upnp.soap_response_data, "errorCode"));
|
||||
printf(" errorDescription='%s'\n", GetValueFromNameValueList(&upnp.soap_response_data, "errorDescription"));
|
||||
}
|
||||
if(next_method_to_call == ENothing)
|
||||
break;
|
||||
printf("Ready to call UPnP IGD methods\n");
|
||||
last_method = next_method_to_call;
|
||||
switch(next_method_to_call) {
|
||||
case EGetExternalIP:
|
||||
printf("GetExternalIPAddress\n");
|
||||
upnpc_get_external_ip_address(&upnp);
|
||||
next_method_to_call = EGetRates;
|
||||
break;
|
||||
case EGetRates:
|
||||
printf("GetCommonLinkProperties\n");
|
||||
upnpc_get_link_layer_max_rate(&upnp);
|
||||
next_method_to_call = EAddPortMapping;
|
||||
case EAddPortMapping:
|
||||
printf("AddPortMapping\n");
|
||||
upnpc_add_port_mapping(&upnp,
|
||||
NULL /* remote_host */, 40002 /* ext_port */,
|
||||
42042 /* int_port */, "192.168.1.202" /* int_client */,
|
||||
"TCP" /* proto */, "this is a test" /* description */,
|
||||
0 /* lease duration */);
|
||||
next_method_to_call = ENothing;
|
||||
case ENothing:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
upnpc_finalize(&upnp);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
/* $Id: upnpreplyparse.c,v 1.18 2014/11/05 05:36:08 nanard Exp $ */
|
||||
/* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2006-2014 Thomas Bernard
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "upnpreplyparse.h"
|
||||
#include "minixml.h"
|
||||
|
||||
static void
|
||||
NameValueParserStartElt(void * d, const char * name, int l)
|
||||
{
|
||||
struct NameValueParserData * data = (struct NameValueParserData *)d;
|
||||
data->topelt = 1;
|
||||
if(l>63)
|
||||
l = 63;
|
||||
memcpy(data->curelt, name, l);
|
||||
data->curelt[l] = '\0';
|
||||
data->cdata = NULL;
|
||||
data->cdatalen = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
NameValueParserEndElt(void * d, const char * name, int l)
|
||||
{
|
||||
struct NameValueParserData * data = (struct NameValueParserData *)d;
|
||||
struct NameValue * nv;
|
||||
(void)name;
|
||||
(void)l;
|
||||
if(!data->topelt)
|
||||
return;
|
||||
if(strcmp(data->curelt, "NewPortListing") != 0)
|
||||
{
|
||||
int l;
|
||||
/* standard case. Limited to n chars strings */
|
||||
l = data->cdatalen;
|
||||
nv = malloc(sizeof(struct NameValue));
|
||||
if(l>=(int)sizeof(nv->value))
|
||||
l = sizeof(nv->value) - 1;
|
||||
strncpy(nv->name, data->curelt, 64);
|
||||
nv->name[63] = '\0';
|
||||
if(data->cdata != NULL)
|
||||
{
|
||||
memcpy(nv->value, data->cdata, l);
|
||||
nv->value[l] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
nv->value[0] = '\0';
|
||||
}
|
||||
nv->l_next = data->l_head; /* insert in list */
|
||||
data->l_head = nv;
|
||||
}
|
||||
data->cdata = NULL;
|
||||
data->cdatalen = 0;
|
||||
data->topelt = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
NameValueParserGetData(void * d, const char * datas, int l)
|
||||
{
|
||||
struct NameValueParserData * data = (struct NameValueParserData *)d;
|
||||
if(strcmp(data->curelt, "NewPortListing") == 0)
|
||||
{
|
||||
/* specific case for NewPortListing which is a XML Document */
|
||||
data->portListing = malloc(l + 1);
|
||||
if(!data->portListing)
|
||||
{
|
||||
/* malloc error */
|
||||
return;
|
||||
}
|
||||
memcpy(data->portListing, datas, l);
|
||||
data->portListing[l] = '\0';
|
||||
data->portListingLength = l;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* standard case. */
|
||||
data->cdata = datas;
|
||||
data->cdatalen = l;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ParseNameValue(const char * buffer, int bufsize,
|
||||
struct NameValueParserData * data)
|
||||
{
|
||||
struct xmlparser parser;
|
||||
data->l_head = NULL;
|
||||
data->portListing = NULL;
|
||||
data->portListingLength = 0;
|
||||
/* init xmlparser object */
|
||||
parser.xmlstart = buffer;
|
||||
parser.xmlsize = bufsize;
|
||||
parser.data = data;
|
||||
parser.starteltfunc = NameValueParserStartElt;
|
||||
parser.endeltfunc = NameValueParserEndElt;
|
||||
parser.datafunc = NameValueParserGetData;
|
||||
parser.attfunc = 0;
|
||||
parsexml(&parser);
|
||||
}
|
||||
|
||||
void
|
||||
ClearNameValueList(struct NameValueParserData * pdata)
|
||||
{
|
||||
struct NameValue * nv;
|
||||
if(pdata->portListing)
|
||||
{
|
||||
free(pdata->portListing);
|
||||
pdata->portListing = NULL;
|
||||
pdata->portListingLength = 0;
|
||||
}
|
||||
while((nv = pdata->l_head) != NULL)
|
||||
{
|
||||
pdata->l_head = nv->l_next;
|
||||
free(nv);
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
GetValueFromNameValueList(struct NameValueParserData * pdata,
|
||||
const char * Name)
|
||||
{
|
||||
struct NameValue * nv;
|
||||
char * p = NULL;
|
||||
for(nv = pdata->l_head;
|
||||
(nv != NULL) && (p == NULL);
|
||||
nv = nv->l_next)
|
||||
{
|
||||
if(strcmp(nv->name, Name) == 0)
|
||||
p = nv->value;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* useless now that minixml ignores namespaces by itself */
|
||||
char *
|
||||
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
|
||||
const char * Name)
|
||||
{
|
||||
struct NameValue * nv;
|
||||
char * p = NULL;
|
||||
char * pname;
|
||||
for(nv = pdata->head.lh_first;
|
||||
(nv != NULL) && (p == NULL);
|
||||
nv = nv->entries.le_next)
|
||||
{
|
||||
pname = strrchr(nv->name, ':');
|
||||
if(pname)
|
||||
pname++;
|
||||
else
|
||||
pname = nv->name;
|
||||
if(strcmp(pname, Name)==0)
|
||||
p = nv->value;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* debug all-in-one function
|
||||
* do parsing then display to stdout */
|
||||
#ifdef DEBUG
|
||||
void
|
||||
DisplayNameValueList(char * buffer, int bufsize)
|
||||
{
|
||||
struct NameValueParserData pdata;
|
||||
struct NameValue * nv;
|
||||
ParseNameValue(buffer, bufsize, &pdata);
|
||||
for(nv = pdata.l_head;
|
||||
nv != NULL;
|
||||
nv = nv->l_next)
|
||||
{
|
||||
printf("%s = %s\n", nv->name, nv->value);
|
||||
}
|
||||
ClearNameValueList(&pdata);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/* $Id: upnpreplyparse.h,v 1.19 2014/10/27 16:33:19 nanard Exp $ */
|
||||
/* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2006-2013 Thomas Bernard
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
|
||||
#ifndef UPNPREPLYPARSE_H_INCLUDED
|
||||
#define UPNPREPLYPARSE_H_INCLUDED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct NameValue {
|
||||
struct NameValue * l_next;
|
||||
char name[64];
|
||||
char value[128];
|
||||
};
|
||||
|
||||
struct NameValueParserData {
|
||||
struct NameValue * l_head;
|
||||
char curelt[64];
|
||||
char * portListing;
|
||||
int portListingLength;
|
||||
int topelt;
|
||||
const char * cdata;
|
||||
int cdatalen;
|
||||
};
|
||||
|
||||
/* ParseNameValue() */
|
||||
void
|
||||
ParseNameValue(const char * buffer, int bufsize,
|
||||
struct NameValueParserData * data);
|
||||
|
||||
/* ClearNameValueList() */
|
||||
void
|
||||
ClearNameValueList(struct NameValueParserData * pdata);
|
||||
|
||||
/* GetValueFromNameValueList() */
|
||||
char *
|
||||
GetValueFromNameValueList(struct NameValueParserData * pdata,
|
||||
const char * Name);
|
||||
|
||||
#if 0
|
||||
/* GetValueFromNameValueListIgnoreNS() */
|
||||
char *
|
||||
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
|
||||
const char * Name);
|
||||
#endif
|
||||
|
||||
/* DisplayNameValueList() */
|
||||
#ifdef DEBUG
|
||||
void
|
||||
DisplayNameValueList(char * buffer, int bufsize);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
/* $Id: upnputils.c,v 1.1 2013/09/07 06:45:39 nanard Exp $ */
|
||||
/* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2006-2013 Thomas Bernard
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#ifdef AF_LINK
|
||||
#include <net/if_dl.h>
|
||||
#endif
|
||||
|
||||
#include "upnputils.h"
|
||||
|
||||
int
|
||||
sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size)
|
||||
{
|
||||
char buffer[64];
|
||||
unsigned short port = 0;
|
||||
int n = -1;
|
||||
|
||||
switch(addr->sa_family)
|
||||
{
|
||||
case AF_INET6:
|
||||
inet_ntop(addr->sa_family,
|
||||
&((struct sockaddr_in6 *)addr)->sin6_addr,
|
||||
buffer, sizeof(buffer));
|
||||
port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
|
||||
n = snprintf(str, size, "[%s]:%hu", buffer, port);
|
||||
break;
|
||||
case AF_INET:
|
||||
inet_ntop(addr->sa_family,
|
||||
&((struct sockaddr_in *)addr)->sin_addr,
|
||||
buffer, sizeof(buffer));
|
||||
port = ntohs(((struct sockaddr_in *)addr)->sin_port);
|
||||
n = snprintf(str, size, "%s:%hu", buffer, port);
|
||||
break;
|
||||
#ifdef AF_LINK
|
||||
#if defined(__sun)
|
||||
/* solaris does not seem to have link_ntoa */
|
||||
/* #define link_ntoa _link_ntoa */
|
||||
#define link_ntoa(x) "dummy-link_ntoa"
|
||||
#endif
|
||||
case AF_LINK:
|
||||
{
|
||||
struct sockaddr_dl * sdl = (struct sockaddr_dl *)addr;
|
||||
n = snprintf(str, size, "index=%hu type=%d %s",
|
||||
sdl->sdl_index, sdl->sdl_type,
|
||||
link_ntoa(sdl));
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
n = snprintf(str, size, "unknown address family %d", addr->sa_family);
|
||||
#if 0
|
||||
n = snprintf(str, size, "unknown address family %d "
|
||||
"%02x %02x %02x %02x %02x %02x %02x %02x",
|
||||
addr->sa_family,
|
||||
addr->sa_data[0], addr->sa_data[1], (unsigned)addr->sa_data[2], addr->sa_data[3],
|
||||
addr->sa_data[4], addr->sa_data[5], (unsigned)addr->sa_data[6], addr->sa_data[7]);
|
||||
#endif
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
set_non_blocking(int fd)
|
||||
{
|
||||
int flags = fcntl(fd, F_GETFL);
|
||||
if(flags < 0)
|
||||
return 0;
|
||||
if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/* $Id: upnputils.h,v 1.1 2013/09/07 06:45:39 nanard Exp $ */
|
||||
/* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2011-2013 Thomas Bernard
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
|
||||
#ifndef UPNPUTILS_H_INCLUDED
|
||||
#define UPNPUTILS_H_INCLUDED
|
||||
|
||||
/**
|
||||
* convert a struct sockaddr to a human readable string.
|
||||
* [ipv6]:port or ipv4:port
|
||||
* return the number of characters used (as snprintf)
|
||||
*/
|
||||
int
|
||||
sockaddr_to_string(const struct sockaddr * addr, char * str, size_t size);
|
||||
|
||||
/**
|
||||
* set the file description as non blocking
|
||||
* return 0 in case of failure, 1 in case of success
|
||||
*/
|
||||
int
|
||||
set_non_blocking(int fd);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue