1 /* $Id: upnpevents.c,v 1.17 2011/06/27 11:24:00 nanard Exp $ */
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2008-2011 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
11 #include <sys/queue.h>
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
22 #include "upnpevents.h"
23 #include "miniupnpdpath.h"
24 #include "upnpglobalvars.h"
25 #include "upnpdescgen.h"
28 /*enum subscriber_service_enum {
34 /* stuctures definitions */
36 LIST_ENTRY(subscriber
) entries
;
37 struct upnp_event_notify
* notify
;
40 enum subscriber_service_enum service
;
45 struct upnp_event_notify
{
46 LIST_ENTRY(upnp_event_notify
) entries
;
54 struct subscriber
* sub
;
71 upnp_event_create_notify(struct subscriber
* sub
);
74 LIST_HEAD(listhead
, subscriber
) subscriberlist
= { NULL
};
77 LIST_HEAD(listheadnotif
, upnp_event_notify
) notifylist
= { NULL
};
79 /* create a new subscriber */
80 static struct subscriber
*
81 newSubscriber(const char * eventurl
, const char * callback
, int callbacklen
)
83 struct subscriber
* tmp
;
84 if(!eventurl
|| !callback
|| !callbacklen
)
86 tmp
= calloc(1, sizeof(struct subscriber
)+callbacklen
+1);
89 if(strcmp(eventurl
, WANCFG_EVENTURL
)==0)
90 tmp
->service
= EWanCFG
;
91 else if(strcmp(eventurl
, WANIPC_EVENTURL
)==0)
92 tmp
->service
= EWanIPC
;
93 #ifdef ENABLE_L3F_SERVICE
94 else if(strcmp(eventurl
, L3F_EVENTURL
)==0)
101 memcpy(tmp
->callback
, callback
, callbacklen
);
102 tmp
->callback
[callbacklen
] = '\0';
103 /* make a dummy uuid */
104 /* TODO: improve that */
105 strncpy(tmp
->uuid
, uuidvalue
, sizeof(tmp
->uuid
));
106 tmp
->uuid
[sizeof(tmp
->uuid
)-1] = '\0';
107 snprintf(tmp
->uuid
+37, 5, "%04lx", random() & 0xffff);
111 /* creates a new subscriber and adds it to the subscriber list
112 * also initiate 1st notify
113 * TODO : add a check on the number of subscriber in order to
114 * prevent memory overflow... */
116 upnpevents_addSubscriber(const char * eventurl
,
117 const char * callback
, int callbacklen
,
120 struct subscriber
* tmp
;
121 /*static char uuid[42];*/
122 /* "uuid:00000000-0000-0000-0000-000000000000"; 5+36+1=42bytes */
123 syslog(LOG_DEBUG
, "addSubscriber(%s, %.*s, %d)",
124 eventurl
, callbacklen
, callback
, timeout
);
125 /*strncpy(uuid, uuidvalue, sizeof(uuid));
126 uuid[sizeof(uuid)-1] = '\0';*/
127 tmp
= newSubscriber(eventurl
, callback
, callbacklen
);
131 tmp
->timeout
= time(NULL
) + timeout
;
132 LIST_INSERT_HEAD(&subscriberlist
, tmp
, entries
);
133 upnp_event_create_notify(tmp
);
137 /* renew a subscription (update the timeout) */
139 renewSubscription(const char * sid
, int sidlen
, int timeout
)
141 struct subscriber
* sub
;
142 for(sub
= subscriberlist
.lh_first
; sub
!= NULL
; sub
= sub
->entries
.le_next
) {
143 if(memcmp(sid
, sub
->uuid
, 41) == 0) {
144 sub
->timeout
= (timeout
? time(NULL
) + timeout
: 0);
152 upnpevents_removeSubscriber(const char * sid
, int sidlen
)
154 struct subscriber
* sub
;
157 for(sub
= subscriberlist
.lh_first
; sub
!= NULL
; sub
= sub
->entries
.le_next
) {
158 if(memcmp(sid
, sub
->uuid
, 41) == 0) {
160 sub
->notify
->sub
= NULL
;
162 LIST_REMOVE(sub
, entries
);
170 /* notifies all subscriber of a number of port mapping change
171 * or external ip address change */
173 upnp_event_var_change_notify(enum subscriber_service_enum service
)
175 struct subscriber
* sub
;
176 for(sub
= subscriberlist
.lh_first
; sub
!= NULL
; sub
= sub
->entries
.le_next
) {
177 if(sub
->service
== service
&& sub
->notify
== NULL
)
178 upnp_event_create_notify(sub
);
182 /* create and add the notify object to the list */
184 upnp_event_create_notify(struct subscriber
* sub
)
186 struct upnp_event_notify
* obj
;
188 obj
= calloc(1, sizeof(struct upnp_event_notify
));
190 syslog(LOG_ERR
, "%s: calloc(): %m", "upnp_event_create_notify");
194 obj
->state
= ECreated
;
196 obj
->s
= socket((obj
->sub
->callback
[7] == '[') ? PF_INET6
: PF_INET
,
199 obj
->s
= socket(PF_INET
, SOCK_STREAM
, 0);
202 syslog(LOG_ERR
, "%s: socket(): %m", "upnp_event_create_notify");
205 if((flags
= fcntl(obj
->s
, F_GETFL
, 0)) < 0) {
206 syslog(LOG_ERR
, "%s: fcntl(..F_GETFL..): %m",
207 "upnp_event_create_notify");
210 if(fcntl(obj
->s
, F_SETFL
, flags
| O_NONBLOCK
) < 0) {
211 syslog(LOG_ERR
, "%s: fcntl(..F_SETFL..): %m",
212 "upnp_event_create_notify");
217 LIST_INSERT_HEAD(¬ifylist
, obj
, entries
);
226 upnp_event_notify_connect(struct upnp_event_notify
* obj
)
232 struct sockaddr_storage addr
;
234 struct sockaddr_in addr
;
238 memset(&addr
, 0, sizeof(addr
));
240 if(obj
->sub
== NULL
) {
244 p
= obj
->sub
->callback
;
245 p
+= 7; /* http:// */
247 if(*p
== '[') { /* ip v6 */
250 while(*p
!= ']' && i
< (sizeof(obj
->addrstr
)-1))
251 obj
->addrstr
[i
++] = *(p
++);
256 while(*p
!= '/' && *p
!= ':' && i
< (sizeof(obj
->addrstr
)-1))
257 obj
->addrstr
[i
++] = *(p
++);
261 obj
->addrstr
[i
] = '\0';
263 obj
->portstr
[0] = *p
;
266 port
= (unsigned short)atoi(p
);
268 if(i
<7) obj
->portstr
[i
++] = *p
;
274 obj
->portstr
[0] = '\0';
279 struct sockaddr_in6
* sa
= (struct sockaddr_in6
*)&addr
;
280 sa
->sin6_family
= AF_INET6
;
281 inet_pton(AF_INET6
, obj
->addrstr
, &(sa
->sin6_addr
));
282 sa
->sin6_port
= htons(port
);
284 struct sockaddr_in
* sa
= (struct sockaddr_in
*)&addr
;
285 sa
->sin_family
= AF_INET
;
286 inet_pton(AF_INET
, obj
->addrstr
, &(sa
->sin_addr
));
287 sa
->sin_port
= htons(port
);
290 addr
.sin_family
= AF_INET
;
291 inet_aton(obj
->addrstr
, &addr
.sin_addr
);
292 addr
.sin_port
= htons(port
);
294 syslog(LOG_DEBUG
, "%s: '%s' %hu '%s'", "upnp_event_notify_connect",
295 obj
->addrstr
, port
, obj
->path
);
296 obj
->state
= EConnecting
;
297 if(connect(obj
->s
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
298 if(errno
!= EINPROGRESS
&& errno
!= EWOULDBLOCK
) {
299 syslog(LOG_ERR
, "%s: connect(): %m", "upnp_event_notify_connect");
305 static void upnp_event_prepare(struct upnp_event_notify
* obj
)
307 static const char notifymsg
[] =
308 "NOTIFY %s HTTP/1.1\r\n"
310 "Content-Type: text/xml\r\n"
311 "Content-Length: %d\r\n"
313 "NTS: upnp:propchange\r\n"
316 "Connection: close\r\n"
317 "Cache-Control: no-cache\r\n"
322 if(obj
->sub
== NULL
) {
326 switch(obj
->sub
->service
) {
328 xml
= getVarsWANCfg(&l
);
331 xml
= getVarsWANIPCn(&l
);
333 #ifdef ENABLE_L3F_SERVICE
335 xml
= getVarsL3F(&l
);
338 #ifdef ENABLE_6FC_SERVICE
340 xml
= getVars6FC(&l
);
343 #ifdef ENABLE_DP_SERVICE
352 obj
->buffersize
= 1024;
353 obj
->buffer
= malloc(obj
->buffersize
);
356 obj
->tosend
= snprintf(obj
->buffer
, obj
->buffersize
, notifymsg
,
357 obj
->path
, obj
->addrstr
, obj
->portstr
, l
+2,
358 obj
->sub
->uuid
, obj
->sub
->seq
,
364 obj
->state
= ESending
;
367 static void upnp_event_send(struct upnp_event_notify
* obj
)
370 syslog(LOG_DEBUG
, "%s: sending event notify message to %s:%s",
371 "upnp_event_send", obj
->addrstr
, obj
->portstr
);
372 syslog(LOG_DEBUG
, "%s: msg: %s",
373 "upnp_event_send", obj
->buffer
+ obj
->sent
);
374 i
= send(obj
->s
, obj
->buffer
+ obj
->sent
, obj
->tosend
- obj
->sent
, 0);
376 syslog(LOG_DEBUG
, "%s: send(): %m", "upnp_event_send");
380 else if(i
!= (obj
->tosend
- obj
->sent
))
381 syslog(LOG_NOTICE
, "%s: %d bytes send out of %d",
382 "upnp_event_send", i
, obj
->tosend
- obj
->sent
);
384 if(obj
->sent
== obj
->tosend
)
385 obj
->state
= EWaitingForResponse
;
388 static void upnp_event_recv(struct upnp_event_notify
* obj
)
391 n
= recv(obj
->s
, obj
->buffer
, obj
->buffersize
, 0);
393 syslog(LOG_DEBUG
, "%s: recv(): %m", "upnp_event_recv");
397 syslog(LOG_DEBUG
, "%s: (%dbytes) %.*s", "upnp_event_recv",
399 obj
->state
= EFinished
;
405 upnp_event_process_notify(struct upnp_event_notify
* obj
)
409 /* now connected or failed to connect */
410 upnp_event_prepare(obj
);
411 upnp_event_send(obj
);
414 upnp_event_send(obj
);
416 case EWaitingForResponse
:
417 upnp_event_recv(obj
);
424 syslog(LOG_ERR
, "upnp_event_process_notify: unknown state");
428 void upnpevents_selectfds(fd_set
*readset
, fd_set
*writeset
, int * max_fd
)
430 struct upnp_event_notify
* obj
;
431 for(obj
= notifylist
.lh_first
; obj
!= NULL
; obj
= obj
->entries
.le_next
) {
432 syslog(LOG_DEBUG
, "upnpevents_selectfds: %p %d %d",
433 obj
, obj
->state
, obj
->s
);
437 upnp_event_notify_connect(obj
);
438 if(obj
->state
!= EConnecting
)
442 FD_SET(obj
->s
, writeset
);
446 case EWaitingForResponse
:
447 FD_SET(obj
->s
, readset
);
458 void upnpevents_processfds(fd_set
*readset
, fd_set
*writeset
)
460 struct upnp_event_notify
* obj
;
461 struct upnp_event_notify
* next
;
462 struct subscriber
* sub
;
463 struct subscriber
* subnext
;
465 for(obj
= notifylist
.lh_first
; obj
!= NULL
; obj
= obj
->entries
.le_next
) {
466 syslog(LOG_DEBUG
, "%s: %p %d %d %d %d",
467 "upnpevents_processfds", obj
, obj
->state
, obj
->s
,
468 FD_ISSET(obj
->s
, readset
), FD_ISSET(obj
->s
, writeset
));
470 if(FD_ISSET(obj
->s
, readset
) || FD_ISSET(obj
->s
, writeset
))
471 upnp_event_process_notify(obj
);
474 obj
= notifylist
.lh_first
;
476 next
= obj
->entries
.le_next
;
477 if(obj
->state
== EError
|| obj
->state
== EFinished
) {
482 obj
->sub
->notify
= NULL
;
483 /* remove also the subscriber from the list if there was an error */
484 if(obj
->state
== EError
&& obj
->sub
) {
485 LIST_REMOVE(obj
->sub
, entries
);
491 LIST_REMOVE(obj
, entries
);
496 /* remove timeouted subscribers */
497 curtime
= time(NULL
);
498 for(sub
= subscriberlist
.lh_first
; sub
!= NULL
; ) {
499 subnext
= sub
->entries
.le_next
;
500 if(sub
->timeout
&& curtime
> sub
->timeout
&& sub
->notify
== NULL
) {
501 LIST_REMOVE(sub
, entries
);
508 #ifdef USE_MINIUPNPDCTL
509 void write_events_details(int s
) {
512 struct upnp_event_notify
* obj
;
513 struct subscriber
* sub
;
514 write(s
, "Events details :\n", 17);
515 for(obj
= notifylist
.lh_first
; obj
!= NULL
; obj
= obj
->entries
.le_next
) {
516 n
= snprintf(buff
, sizeof(buff
), " %p sub=%p state=%d s=%d\n",
517 obj
, obj
->sub
, obj
->state
, obj
->s
);
520 write(s
, "Subscribers :\n", 14);
521 for(sub
= subscriberlist
.lh_first
; sub
!= NULL
; sub
= sub
->entries
.le_next
) {
522 n
= snprintf(buff
, sizeof(buff
), " %p timeout=%d seq=%u service=%d\n",
523 sub
, (int)sub
->timeout
, sub
->seq
, sub
->service
);
525 n
= snprintf(buff
, sizeof(buff
), " notify=%p %s\n",
526 sub
->notify
, sub
->uuid
);
528 n
= snprintf(buff
, sizeof(buff
), " %s\n",