Adding miniupnpc-async

This commit is contained in:
Thomas Bernard 2014-11-07 12:32:20 +01:00
parent ecb382533c
commit 8271eb91b3
18 changed files with 2227 additions and 0 deletions

View File

@ -0,0 +1,4 @@
$Id: Changelog.txt,v 1.1 2009/11/12 14:04:33 nanard Exp $
initial version...

104
miniupnpc-async/Makefile Normal file
View File

@ -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

10
miniupnpc-async/README Normal file
View File

@ -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/)

5
miniupnpc-async/config.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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 */

229
miniupnpc-async/minixml.c Normal file
View File

@ -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);
}

37
miniupnpc-async/minixml.h Normal file
View File

@ -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

View File

@ -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++;
}
}

View File

@ -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

128
miniupnpc-async/testasync.c Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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