version 1.1, 2012/02/21 23:16:02
|
version 1.1.1.3, 2013/07/22 00:32:35
|
Line 1
|
Line 1
|
/* $Id$ */ |
/* $Id$ */ |
/* MiniUPnP project |
/* MiniUPnP project |
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ |
* (c) 2008-2009 Thomas Bernard | * (c) 2008-2012 Thomas Bernard |
* This software is subject to the conditions detailed |
* This software is subject to the conditions detailed |
* in the LICENCE file provided within the distribution */ |
* in the LICENCE file provided within the distribution */ |
|
|
Line 16
|
Line 16
|
#include <sys/socket.h> |
#include <sys/socket.h> |
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <arpa/inet.h> |
#include <arpa/inet.h> |
#include <fcntl.h> |
|
#include <errno.h> |
#include <errno.h> |
#include "config.h" |
#include "config.h" |
#include "upnpevents.h" |
#include "upnpevents.h" |
#include "miniupnpdpath.h" |
#include "miniupnpdpath.h" |
#include "upnpglobalvars.h" |
#include "upnpglobalvars.h" |
#include "upnpdescgen.h" |
#include "upnpdescgen.h" |
|
#include "upnputils.h" |
|
|
#ifdef ENABLE_EVENTS |
#ifdef ENABLE_EVENTS |
/*enum subscriber_service_enum { |
/*enum subscriber_service_enum { |
Line 37 struct subscriber {
|
Line 37 struct subscriber {
|
struct upnp_event_notify * notify; |
struct upnp_event_notify * notify; |
time_t timeout; |
time_t timeout; |
uint32_t seq; |
uint32_t seq; |
/*enum { EWanCFG = 1, EWanIPC, EL3F } service;*/ |
|
enum subscriber_service_enum service; |
enum subscriber_service_enum service; |
char uuid[42]; |
char uuid[42]; |
char callback[]; |
char callback[]; |
Line 58 struct upnp_event_notify {
|
Line 57 struct upnp_event_notify {
|
int tosend; |
int tosend; |
int sent; |
int sent; |
const char * path; |
const char * path; |
|
#ifdef ENABLE_IPV6 |
|
int ipv6; |
|
char addrstr[48]; |
|
#else |
char addrstr[16]; |
char addrstr[16]; |
|
#endif |
char portstr[8]; |
char portstr[8]; |
}; |
}; |
|
|
Line 80 newSubscriber(const char * eventurl, const char * call
|
Line 84 newSubscriber(const char * eventurl, const char * call
|
if(!eventurl || !callback || !callbacklen) |
if(!eventurl || !callback || !callbacklen) |
return NULL; |
return NULL; |
tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1); |
tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1); |
|
if(!tmp) |
|
return NULL; |
if(strcmp(eventurl, WANCFG_EVENTURL)==0) |
if(strcmp(eventurl, WANCFG_EVENTURL)==0) |
tmp->service = EWanCFG; |
tmp->service = EWanCFG; |
else if(strcmp(eventurl, WANIPC_EVENTURL)==0) |
else if(strcmp(eventurl, WANIPC_EVENTURL)==0) |
Line 88 newSubscriber(const char * eventurl, const char * call
|
Line 94 newSubscriber(const char * eventurl, const char * call
|
else if(strcmp(eventurl, L3F_EVENTURL)==0) |
else if(strcmp(eventurl, L3F_EVENTURL)==0) |
tmp->service = EL3F; |
tmp->service = EL3F; |
#endif |
#endif |
|
#ifdef ENABLE_6FC_SERVICE |
|
else if(strcmp(eventurl, WANIP6FC_EVENTURL)==0) |
|
tmp->service = E6FC; |
|
#endif |
|
#ifdef ENABLE_DP_SERVICE |
|
else if(strcmp(eventurl, DP_EVENTURL)==0) |
|
tmp->service = EDP; |
|
#endif |
else { |
else { |
free(tmp); |
free(tmp); |
return NULL; |
return NULL; |
Line 103 newSubscriber(const char * eventurl, const char * call
|
Line 117 newSubscriber(const char * eventurl, const char * call
|
} |
} |
|
|
/* creates a new subscriber and adds it to the subscriber list |
/* creates a new subscriber and adds it to the subscriber list |
* also initiate 1st notify */ | * also initiate 1st notify |
| * TODO : add a check on the number of subscriber in order to |
| * prevent memory overflow... */ |
const char * |
const char * |
upnpevents_addSubscriber(const char * eventurl, |
upnpevents_addSubscriber(const char * eventurl, |
const char * callback, int callbacklen, |
const char * callback, int callbacklen, |
Line 132 renewSubscription(const char * sid, int sidlen, int ti
|
Line 148 renewSubscription(const char * sid, int sidlen, int ti
|
{ |
{ |
struct subscriber * sub; |
struct subscriber * sub; |
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) { |
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) { |
if(memcmp(sid, sub->uuid, 41) == 0) { | if((sidlen == 41) && (memcmp(sid, sub->uuid, 41) == 0)) { |
sub->timeout = (timeout ? time(NULL) + timeout : 0); |
sub->timeout = (timeout ? time(NULL) + timeout : 0); |
return 0; |
return 0; |
} |
} |
Line 147 upnpevents_removeSubscriber(const char * sid, int sidl
|
Line 163 upnpevents_removeSubscriber(const char * sid, int sidl
|
if(!sid) |
if(!sid) |
return -1; |
return -1; |
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) { |
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) { |
if(memcmp(sid, sub->uuid, 41) == 0) { | if((sidlen == 41) && (memcmp(sid, sub->uuid, 41) == 0)) { |
if(sub->notify) { |
if(sub->notify) { |
sub->notify->sub = NULL; |
sub->notify->sub = NULL; |
} |
} |
Line 176 static void
|
Line 192 static void
|
upnp_event_create_notify(struct subscriber * sub) |
upnp_event_create_notify(struct subscriber * sub) |
{ |
{ |
struct upnp_event_notify * obj; |
struct upnp_event_notify * obj; |
int flags; | |
obj = calloc(1, sizeof(struct upnp_event_notify)); |
obj = calloc(1, sizeof(struct upnp_event_notify)); |
if(!obj) { |
if(!obj) { |
syslog(LOG_ERR, "%s: calloc(): %m", "upnp_event_create_notify"); |
syslog(LOG_ERR, "%s: calloc(): %m", "upnp_event_create_notify"); |
Line 184 upnp_event_create_notify(struct subscriber * sub)
|
Line 200 upnp_event_create_notify(struct subscriber * sub)
|
} |
} |
obj->sub = sub; |
obj->sub = sub; |
obj->state = ECreated; |
obj->state = ECreated; |
|
#ifdef ENABLE_IPV6 |
|
obj->s = socket((obj->sub->callback[7] == '[') ? PF_INET6 : PF_INET, |
|
SOCK_STREAM, 0); |
|
#else |
obj->s = socket(PF_INET, SOCK_STREAM, 0); |
obj->s = socket(PF_INET, SOCK_STREAM, 0); |
|
#endif |
if(obj->s<0) { |
if(obj->s<0) { |
syslog(LOG_ERR, "%s: socket(): %m", "upnp_event_create_notify"); |
syslog(LOG_ERR, "%s: socket(): %m", "upnp_event_create_notify"); |
goto error; |
goto error; |
} |
} |
if((flags = fcntl(obj->s, F_GETFL, 0)) < 0) { | if(!set_non_blocking(obj->s)) { |
syslog(LOG_ERR, "%s: fcntl(..F_GETFL..): %m", | syslog(LOG_ERR, "%s: set_non_blocking(): %m", |
"upnp_event_create_notify"); |
"upnp_event_create_notify"); |
goto error; |
goto error; |
} |
} |
if(fcntl(obj->s, F_SETFL, flags | O_NONBLOCK) < 0) { |
|
syslog(LOG_ERR, "%s: fcntl(..F_SETFL..): %m", |
|
"upnp_event_create_notify"); |
|
goto error; |
|
} |
|
if(sub) |
if(sub) |
sub->notify = obj; |
sub->notify = obj; |
LIST_INSERT_HEAD(¬ifylist, obj, entries); |
LIST_INSERT_HEAD(¬ifylist, obj, entries); |
Line 212 error:
|
Line 228 error:
|
static void |
static void |
upnp_event_notify_connect(struct upnp_event_notify * obj) |
upnp_event_notify_connect(struct upnp_event_notify * obj) |
{ |
{ |
int i; | unsigned int i; |
const char * p; |
const char * p; |
unsigned short port; |
unsigned short port; |
|
#ifdef ENABLE_IPV6 |
|
struct sockaddr_storage addr; |
|
#else |
struct sockaddr_in addr; |
struct sockaddr_in addr; |
|
#endif |
if(!obj) |
if(!obj) |
return; |
return; |
memset(&addr, 0, sizeof(addr)); |
memset(&addr, 0, sizeof(addr)); |
Line 226 upnp_event_notify_connect(struct upnp_event_notify * o
|
Line 246 upnp_event_notify_connect(struct upnp_event_notify * o
|
} |
} |
p = obj->sub->callback; |
p = obj->sub->callback; |
p += 7; /* http:// */ |
p += 7; /* http:// */ |
while(*p != '/' && *p != ':') | #ifdef ENABLE_IPV6 |
obj->addrstr[i++] = *(p++); | if(*p == '[') { /* ip v6 */ |
| p++; |
| obj->ipv6 = 1; |
| while(*p != ']' && i < (sizeof(obj->addrstr)-1)) |
| obj->addrstr[i++] = *(p++); |
| if(*p == ']') |
| p++; |
| } else { |
| #endif |
| while(*p != '/' && *p != ':' && i < (sizeof(obj->addrstr)-1)) |
| obj->addrstr[i++] = *(p++); |
| #ifdef ENABLE_IPV6 |
| } |
| #endif |
obj->addrstr[i] = '\0'; |
obj->addrstr[i] = '\0'; |
if(*p == ':') { |
if(*p == ':') { |
obj->portstr[0] = *p; |
obj->portstr[0] = *p; |
Line 244 upnp_event_notify_connect(struct upnp_event_notify * o
|
Line 277 upnp_event_notify_connect(struct upnp_event_notify * o
|
obj->portstr[0] = '\0'; |
obj->portstr[0] = '\0'; |
} |
} |
obj->path = p; |
obj->path = p; |
|
#ifdef ENABLE_IPV6 |
|
if(obj->ipv6) { |
|
struct sockaddr_in6 * sa = (struct sockaddr_in6 *)&addr; |
|
sa->sin6_family = AF_INET6; |
|
inet_pton(AF_INET6, obj->addrstr, &(sa->sin6_addr)); |
|
sa->sin6_port = htons(port); |
|
} else { |
|
struct sockaddr_in * sa = (struct sockaddr_in *)&addr; |
|
sa->sin_family = AF_INET; |
|
inet_pton(AF_INET, obj->addrstr, &(sa->sin_addr)); |
|
sa->sin_port = htons(port); |
|
} |
|
#else |
addr.sin_family = AF_INET; |
addr.sin_family = AF_INET; |
inet_aton(obj->addrstr, &addr.sin_addr); |
inet_aton(obj->addrstr, &addr.sin_addr); |
addr.sin_port = htons(port); |
addr.sin_port = htons(port); |
|
#endif |
syslog(LOG_DEBUG, "%s: '%s' %hu '%s'", "upnp_event_notify_connect", |
syslog(LOG_DEBUG, "%s: '%s' %hu '%s'", "upnp_event_notify_connect", |
obj->addrstr, port, obj->path); |
obj->addrstr, port, obj->path); |
obj->state = EConnecting; |
obj->state = EConnecting; |
Line 260 upnp_event_notify_connect(struct upnp_event_notify * o
|
Line 307 upnp_event_notify_connect(struct upnp_event_notify * o
|
|
|
static void upnp_event_prepare(struct upnp_event_notify * obj) |
static void upnp_event_prepare(struct upnp_event_notify * obj) |
{ |
{ |
static const char notifymsg[] = | static const char notifymsg[] = |
"NOTIFY %s HTTP/1.1\r\n" |
"NOTIFY %s HTTP/1.1\r\n" |
"Host: %s%s\r\n" |
"Host: %s%s\r\n" |
"Content-Type: text/xml\r\n" |
"Content-Type: text/xml\r\n" |
Line 291 static void upnp_event_prepare(struct upnp_event_notif
|
Line 338 static void upnp_event_prepare(struct upnp_event_notif
|
xml = getVarsL3F(&l); |
xml = getVarsL3F(&l); |
break; |
break; |
#endif |
#endif |
|
#ifdef ENABLE_6FC_SERVICE |
|
case E6FC: |
|
xml = getVars6FC(&l); |
|
break; |
|
#endif |
|
#ifdef ENABLE_DP_SERVICE |
|
case EDP: |
|
xml = getVarsDP(&l); |
|
break; |
|
#endif |
default: |
default: |
xml = NULL; |
xml = NULL; |
l = 0; |
l = 0; |
} |
} |
obj->buffersize = 1024; |
obj->buffersize = 1024; |
obj->buffer = malloc(obj->buffersize); |
obj->buffer = malloc(obj->buffersize); |
/*if(!obj->buffer) { | if(!obj->buffer) { |
}*/ | syslog(LOG_ERR, "%s: malloc returned NULL", "upnp_event_prepare"); |
| if(xml) { |
| free(xml); |
| } |
| obj->state = EError; |
| return; |
| } |
obj->tosend = snprintf(obj->buffer, obj->buffersize, notifymsg, |
obj->tosend = snprintf(obj->buffer, obj->buffersize, notifymsg, |
obj->path, obj->addrstr, obj->portstr, l+2, |
obj->path, obj->addrstr, obj->portstr, l+2, |
obj->sub->uuid, obj->sub->seq, |
obj->sub->uuid, obj->sub->seq, |
Line 313 static void upnp_event_prepare(struct upnp_event_notif
|
Line 376 static void upnp_event_prepare(struct upnp_event_notif
|
static void upnp_event_send(struct upnp_event_notify * obj) |
static void upnp_event_send(struct upnp_event_notify * obj) |
{ |
{ |
int i; |
int i; |
|
|
syslog(LOG_DEBUG, "%s: sending event notify message to %s:%s", |
syslog(LOG_DEBUG, "%s: sending event notify message to %s:%s", |
"upnp_event_send", obj->addrstr, obj->portstr); |
"upnp_event_send", obj->addrstr, obj->portstr); |
syslog(LOG_DEBUG, "%s: msg: %s", |
syslog(LOG_DEBUG, "%s: msg: %s", |
"upnp_event_send", obj->buffer + obj->sent); |
"upnp_event_send", obj->buffer + obj->sent); |
i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0); |
i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0); |
if(i<0) { |
if(i<0) { |
syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send"); | if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { |
obj->state = EError; | syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send"); |
return; | obj->state = EError; |
| return; |
| } else { |
| /* EAGAIN or EWOULDBLOCK or EINTR : no data sent */ |
| i = 0; |
| } |
} |
} |
else if(i != (obj->tosend - obj->sent)) | if(i != (obj->tosend - obj->sent)) |
syslog(LOG_NOTICE, "%s: %d bytes send out of %d", |
syslog(LOG_NOTICE, "%s: %d bytes send out of %d", |
"upnp_event_send", i, obj->tosend - obj->sent); |
"upnp_event_send", i, obj->tosend - obj->sent); |
obj->sent += i; |
obj->sent += i; |
Line 336 static void upnp_event_recv(struct upnp_event_notify *
|
Line 405 static void upnp_event_recv(struct upnp_event_notify *
|
int n; |
int n; |
n = recv(obj->s, obj->buffer, obj->buffersize, 0); |
n = recv(obj->s, obj->buffer, obj->buffersize, 0); |
if(n<0) { |
if(n<0) { |
syslog(LOG_ERR, "%s: recv(): %m", "upnp_event_recv"); | if(errno != EAGAIN && |
obj->state = EError; | errno != EWOULDBLOCK && |
| errno != EINTR) { |
| syslog(LOG_ERR, "%s: recv(): %m", "upnp_event_recv"); |
| obj->state = EError; |
| } |
return; |
return; |
} |
} |
syslog(LOG_DEBUG, "%s: (%dbytes) %.*s", "upnp_event_recv", |
syslog(LOG_DEBUG, "%s: (%dbytes) %.*s", "upnp_event_recv", |
n, n, obj->buffer); |
n, n, obj->buffer); |
|
/* TODO : do something with the data recevied ? |
|
* right now, n (number of bytes received) is ignored |
|
* We may need to recv() more bytes. */ |
obj->state = EFinished; |
obj->state = EFinished; |
if(obj->sub) |
if(obj->sub) |
obj->sub->seq++; |
obj->sub->seq++; |
Line 350 static void upnp_event_recv(struct upnp_event_notify *
|
Line 426 static void upnp_event_recv(struct upnp_event_notify *
|
static void |
static void |
upnp_event_process_notify(struct upnp_event_notify * obj) |
upnp_event_process_notify(struct upnp_event_notify * obj) |
{ |
{ |
|
int err; |
|
socklen_t len; |
switch(obj->state) { |
switch(obj->state) { |
case EConnecting: |
case EConnecting: |
/* now connected or failed to connect */ |
/* now connected or failed to connect */ |
|
len = sizeof(err); |
|
if(getsockopt(obj->s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { |
|
syslog(LOG_ERR, "%s: getsockopt: %m", "upnp_event_process_notify"); |
|
obj->state = EError; |
|
break; |
|
} |
|
if(err != 0) { |
|
errno = err; |
|
syslog(LOG_WARNING, "%s: connect failed: %m", "upnp_event_process_notify"); |
|
obj->state = EError; |
|
break; |
|
} |
upnp_event_prepare(obj); |
upnp_event_prepare(obj); |
upnp_event_send(obj); | if(obj->state == ESending) |
| upnp_event_send(obj); |
break; |
break; |
case ESending: |
case ESending: |
upnp_event_send(obj); |
upnp_event_send(obj); |
Line 367 upnp_event_process_notify(struct upnp_event_notify * o
|
Line 458 upnp_event_process_notify(struct upnp_event_notify * o
|
obj->s = -1; |
obj->s = -1; |
break; |
break; |
default: |
default: |
syslog(LOG_ERR, "upnp_event_process_notify: unknown state"); | syslog(LOG_ERR, "%s: unknown state", "upnp_event_process_notify"); |
} |
} |
} |
} |
|
|
Line 394 void upnpevents_selectfds(fd_set *readset, fd_set *wri
|
Line 485 void upnpevents_selectfds(fd_set *readset, fd_set *wri
|
if(obj->s > *max_fd) |
if(obj->s > *max_fd) |
*max_fd = obj->s; |
*max_fd = obj->s; |
break; |
break; |
|
default: |
|
; |
} |
} |
} |
} |
} |
} |
Line 464 void write_events_details(int s) {
|
Line 557 void write_events_details(int s) {
|
write(s, "Subscribers :\n", 14); |
write(s, "Subscribers :\n", 14); |
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) { |
for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) { |
n = snprintf(buff, sizeof(buff), " %p timeout=%d seq=%u service=%d\n", |
n = snprintf(buff, sizeof(buff), " %p timeout=%d seq=%u service=%d\n", |
sub, sub->timeout, sub->seq, sub->service); | sub, (int)sub->timeout, sub->seq, sub->service); |
write(s, buff, n); |
write(s, buff, n); |
n = snprintf(buff, sizeof(buff), " notify=%p %s\n", |
n = snprintf(buff, sizeof(buff), " notify=%p %s\n", |
sub->notify, sub->uuid); |
sub->notify, sub->uuid); |