Miniupnpd v. 1.5 (20110618)
[tomato.git] / release / src / router / miniupnpd / upnpevents.c
blobe2534df1292fa23d7b746a055b041a5e0aa8402c
1 /* $Id: upnpevents.c,v 1.15 2011/06/04 08:25:27 nanard Exp $ */
2 /* MiniUPnP project
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 */
8 #include <stdio.h>
9 #include <string.h>
10 #include <syslog.h>
11 #include <sys/queue.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <time.h>
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 #include <fcntl.h>
20 #include <errno.h>
21 #include "config.h"
22 #include "upnpevents.h"
23 #include "miniupnpdpath.h"
24 #include "upnpglobalvars.h"
25 #include "upnpdescgen.h"
27 #ifdef ENABLE_EVENTS
28 /*enum subscriber_service_enum {
29 EWanCFG = 1,
30 EWanIPC,
31 EL3F
32 };*/
34 /* stuctures definitions */
35 struct subscriber {
36 LIST_ENTRY(subscriber) entries;
37 struct upnp_event_notify * notify;
38 time_t timeout;
39 uint32_t seq;
40 /*enum { EWanCFG = 1, EWanIPC, EL3F } service;*/
41 enum subscriber_service_enum service;
42 char uuid[42];
43 char callback[];
46 struct upnp_event_notify {
47 LIST_ENTRY(upnp_event_notify) entries;
48 int s; /* socket */
49 enum { ECreated=1,
50 EConnecting,
51 ESending,
52 EWaitingForResponse,
53 EFinished,
54 EError } state;
55 struct subscriber * sub;
56 char * buffer;
57 int buffersize;
58 int tosend;
59 int sent;
60 const char * path;
61 char addrstr[16];
62 char portstr[8];
65 /* prototypes */
66 static void
67 upnp_event_create_notify(struct subscriber * sub);
69 /* Subscriber list */
70 LIST_HEAD(listhead, subscriber) subscriberlist = { NULL };
72 /* notify list */
73 LIST_HEAD(listheadnotif, upnp_event_notify) notifylist = { NULL };
75 /* create a new subscriber */
76 static struct subscriber *
77 newSubscriber(const char * eventurl, const char * callback, int callbacklen)
79 struct subscriber * tmp;
80 if(!eventurl || !callback || !callbacklen)
81 return NULL;
82 tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1);
83 if(strcmp(eventurl, WANCFG_EVENTURL)==0)
84 tmp->service = EWanCFG;
85 else if(strcmp(eventurl, WANIPC_EVENTURL)==0)
86 tmp->service = EWanIPC;
87 #ifdef ENABLE_L3F_SERVICE
88 else if(strcmp(eventurl, L3F_EVENTURL)==0)
89 tmp->service = EL3F;
90 #endif
91 else {
92 free(tmp);
93 return NULL;
95 memcpy(tmp->callback, callback, callbacklen);
96 tmp->callback[callbacklen] = '\0';
97 /* make a dummy uuid */
98 /* TODO: improve that */
99 strncpy(tmp->uuid, uuidvalue, sizeof(tmp->uuid));
100 tmp->uuid[sizeof(tmp->uuid)-1] = '\0';
101 snprintf(tmp->uuid+37, 5, "%04lx", random() & 0xffff);
102 return tmp;
105 /* creates a new subscriber and adds it to the subscriber list
106 * also initiate 1st notify */
107 const char *
108 upnpevents_addSubscriber(const char * eventurl,
109 const char * callback, int callbacklen,
110 int timeout)
112 struct subscriber * tmp;
113 /*static char uuid[42];*/
114 /* "uuid:00000000-0000-0000-0000-000000000000"; 5+36+1=42bytes */
115 syslog(LOG_DEBUG, "addSubscriber(%s, %.*s, %d)",
116 eventurl, callbacklen, callback, timeout);
117 /*strncpy(uuid, uuidvalue, sizeof(uuid));
118 uuid[sizeof(uuid)-1] = '\0';*/
119 tmp = newSubscriber(eventurl, callback, callbacklen);
120 if(!tmp)
121 return NULL;
122 if(timeout)
123 tmp->timeout = time(NULL) + timeout;
124 LIST_INSERT_HEAD(&subscriberlist, tmp, entries);
125 upnp_event_create_notify(tmp);
126 return tmp->uuid;
129 /* renew a subscription (update the timeout) */
131 renewSubscription(const char * sid, int sidlen, int timeout)
133 struct subscriber * sub;
134 for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
135 if(memcmp(sid, sub->uuid, 41) == 0) {
136 sub->timeout = (timeout ? time(NULL) + timeout : 0);
137 return 0;
140 return -1;
144 upnpevents_removeSubscriber(const char * sid, int sidlen)
146 struct subscriber * sub;
147 if(!sid)
148 return -1;
149 for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
150 if(memcmp(sid, sub->uuid, 41) == 0) {
151 if(sub->notify) {
152 sub->notify->sub = NULL;
154 LIST_REMOVE(sub, entries);
155 free(sub);
156 return 0;
159 return -1;
162 /* notifies all subscriber of a number of port mapping change
163 * or external ip address change */
164 void
165 upnp_event_var_change_notify(enum subscriber_service_enum service)
167 struct subscriber * sub;
168 for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
169 if(sub->service == service && sub->notify == NULL)
170 upnp_event_create_notify(sub);
174 /* create and add the notify object to the list */
175 static void
176 upnp_event_create_notify(struct subscriber * sub)
178 struct upnp_event_notify * obj;
179 int flags;
180 obj = calloc(1, sizeof(struct upnp_event_notify));
181 if(!obj) {
182 syslog(LOG_ERR, "%s: calloc(): %m", "upnp_event_create_notify");
183 return;
185 obj->sub = sub;
186 obj->state = ECreated;
187 obj->s = socket(PF_INET, SOCK_STREAM, 0);
188 if(obj->s<0) {
189 syslog(LOG_ERR, "%s: socket(): %m", "upnp_event_create_notify");
190 goto error;
192 if((flags = fcntl(obj->s, F_GETFL, 0)) < 0) {
193 syslog(LOG_ERR, "%s: fcntl(..F_GETFL..): %m",
194 "upnp_event_create_notify");
195 goto error;
197 if(fcntl(obj->s, F_SETFL, flags | O_NONBLOCK) < 0) {
198 syslog(LOG_ERR, "%s: fcntl(..F_SETFL..): %m",
199 "upnp_event_create_notify");
200 goto error;
202 if(sub)
203 sub->notify = obj;
204 LIST_INSERT_HEAD(&notifylist, obj, entries);
205 return;
206 error:
207 if(obj->s >= 0)
208 close(obj->s);
209 free(obj);
212 static void
213 upnp_event_notify_connect(struct upnp_event_notify * obj)
215 int i;
216 const char * p;
217 unsigned short port;
218 struct sockaddr_in addr;
219 if(!obj)
220 return;
221 memset(&addr, 0, sizeof(addr));
222 i = 0;
223 if(obj->sub == NULL) {
224 obj->state = EError;
225 return;
227 p = obj->sub->callback;
228 p += 7; /* http:// */
229 while(*p != '/' && *p != ':')
230 obj->addrstr[i++] = *(p++);
231 obj->addrstr[i] = '\0';
232 if(*p == ':') {
233 obj->portstr[0] = *p;
234 i = 1;
235 p++;
236 port = (unsigned short)atoi(p);
237 while(*p != '/') {
238 if(i<7) obj->portstr[i++] = *p;
239 p++;
241 obj->portstr[i] = 0;
242 } else {
243 port = 80;
244 obj->portstr[0] = '\0';
246 obj->path = p;
247 addr.sin_family = AF_INET;
248 inet_aton(obj->addrstr, &addr.sin_addr);
249 addr.sin_port = htons(port);
250 syslog(LOG_DEBUG, "%s: '%s' %hu '%s'", "upnp_event_notify_connect",
251 obj->addrstr, port, obj->path);
252 obj->state = EConnecting;
253 if(connect(obj->s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
254 if(errno != EINPROGRESS && errno != EWOULDBLOCK) {
255 syslog(LOG_ERR, "%s: connect(): %m", "upnp_event_notify_connect");
256 obj->state = EError;
261 static void upnp_event_prepare(struct upnp_event_notify * obj)
263 static const char notifymsg[] =
264 "NOTIFY %s HTTP/1.1\r\n"
265 "Host: %s%s\r\n"
266 "Content-Type: text/xml\r\n"
267 "Content-Length: %d\r\n"
268 "NT: upnp:event\r\n"
269 "NTS: upnp:propchange\r\n"
270 "SID: %s\r\n"
271 "SEQ: %u\r\n"
272 "Connection: close\r\n"
273 "Cache-Control: no-cache\r\n"
274 "\r\n"
275 "%.*s\r\n";
276 char * xml;
277 int l;
278 if(obj->sub == NULL) {
279 obj->state = EError;
280 return;
282 switch(obj->sub->service) {
283 case EWanCFG:
284 xml = getVarsWANCfg(&l);
285 break;
286 case EWanIPC:
287 xml = getVarsWANIPCn(&l);
288 break;
289 #ifdef ENABLE_L3F_SERVICE
290 case EL3F:
291 xml = getVarsL3F(&l);
292 break;
293 #endif
294 #ifdef ENABLE_6FC_SERVICE
295 case E6FC:
296 xml = getVars6FC(&l);
297 break;
298 #endif
299 #ifdef ENABLE_DP_SERVICE
300 case EDP:
301 xml = getVarsDP(&l);
302 break;
303 #endif
304 default:
305 xml = NULL;
306 l = 0;
308 obj->buffersize = 1024;
309 obj->buffer = malloc(obj->buffersize);
310 /*if(!obj->buffer) {
312 obj->tosend = snprintf(obj->buffer, obj->buffersize, notifymsg,
313 obj->path, obj->addrstr, obj->portstr, l+2,
314 obj->sub->uuid, obj->sub->seq,
315 l, xml);
316 if(xml) {
317 free(xml);
318 xml = NULL;
320 obj->state = ESending;
323 static void upnp_event_send(struct upnp_event_notify * obj)
325 int i;
326 syslog(LOG_DEBUG, "%s: sending event notify message to %s:%s",
327 "upnp_event_send", obj->addrstr, obj->portstr);
328 syslog(LOG_DEBUG, "%s: msg: %s",
329 "upnp_event_send", obj->buffer + obj->sent);
330 i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0);
331 if(i<0) {
332 syslog(LOG_DEBUG, "%s: send(): %m", "upnp_event_send");
333 obj->state = EError;
334 return;
336 else if(i != (obj->tosend - obj->sent))
337 syslog(LOG_NOTICE, "%s: %d bytes send out of %d",
338 "upnp_event_send", i, obj->tosend - obj->sent);
339 obj->sent += i;
340 if(obj->sent == obj->tosend)
341 obj->state = EWaitingForResponse;
344 static void upnp_event_recv(struct upnp_event_notify * obj)
346 int n;
347 n = recv(obj->s, obj->buffer, obj->buffersize, 0);
348 if(n<0) {
349 syslog(LOG_DEBUG, "%s: recv(): %m", "upnp_event_recv");
350 obj->state = EError;
351 return;
353 syslog(LOG_DEBUG, "%s: (%dbytes) %.*s", "upnp_event_recv",
354 n, n, obj->buffer);
355 obj->state = EFinished;
356 if(obj->sub)
357 obj->sub->seq++;
360 static void
361 upnp_event_process_notify(struct upnp_event_notify * obj)
363 switch(obj->state) {
364 case EConnecting:
365 /* now connected or failed to connect */
366 upnp_event_prepare(obj);
367 upnp_event_send(obj);
368 break;
369 case ESending:
370 upnp_event_send(obj);
371 break;
372 case EWaitingForResponse:
373 upnp_event_recv(obj);
374 break;
375 case EFinished:
376 close(obj->s);
377 obj->s = -1;
378 break;
379 default:
380 syslog(LOG_ERR, "upnp_event_process_notify: unknown state");
384 void upnpevents_selectfds(fd_set *readset, fd_set *writeset, int * max_fd)
386 struct upnp_event_notify * obj;
387 for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
388 syslog(LOG_DEBUG, "upnpevents_selectfds: %p %d %d",
389 obj, obj->state, obj->s);
390 if(obj->s >= 0) {
391 switch(obj->state) {
392 case ECreated:
393 upnp_event_notify_connect(obj);
394 if(obj->state != EConnecting)
395 break;
396 case EConnecting:
397 case ESending:
398 FD_SET(obj->s, writeset);
399 if(obj->s > *max_fd)
400 *max_fd = obj->s;
401 break;
402 case EWaitingForResponse:
403 FD_SET(obj->s, readset);
404 if(obj->s > *max_fd)
405 *max_fd = obj->s;
406 break;
407 default:
414 void upnpevents_processfds(fd_set *readset, fd_set *writeset)
416 struct upnp_event_notify * obj;
417 struct upnp_event_notify * next;
418 struct subscriber * sub;
419 struct subscriber * subnext;
420 time_t curtime;
421 for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
422 syslog(LOG_DEBUG, "%s: %p %d %d %d %d",
423 "upnpevents_processfds", obj, obj->state, obj->s,
424 FD_ISSET(obj->s, readset), FD_ISSET(obj->s, writeset));
425 if(obj->s >= 0) {
426 if(FD_ISSET(obj->s, readset) || FD_ISSET(obj->s, writeset))
427 upnp_event_process_notify(obj);
430 obj = notifylist.lh_first;
431 while(obj != NULL) {
432 next = obj->entries.le_next;
433 if(obj->state == EError || obj->state == EFinished) {
434 if(obj->s >= 0) {
435 close(obj->s);
437 if(obj->sub)
438 obj->sub->notify = NULL;
439 /* remove also the subscriber from the list if there was an error */
440 if(obj->state == EError && obj->sub) {
441 LIST_REMOVE(obj->sub, entries);
442 free(obj->sub);
444 if(obj->buffer) {
445 free(obj->buffer);
447 LIST_REMOVE(obj, entries);
448 free(obj);
450 obj = next;
452 /* remove timeouted subscribers */
453 curtime = time(NULL);
454 for(sub = subscriberlist.lh_first; sub != NULL; ) {
455 subnext = sub->entries.le_next;
456 if(sub->timeout && curtime > sub->timeout && sub->notify == NULL) {
457 LIST_REMOVE(sub, entries);
458 free(sub);
460 sub = subnext;
464 #ifdef USE_MINIUPNPDCTL
465 void write_events_details(int s) {
466 int n;
467 char buff[80];
468 struct upnp_event_notify * obj;
469 struct subscriber * sub;
470 write(s, "Events details :\n", 17);
471 for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
472 n = snprintf(buff, sizeof(buff), " %p sub=%p state=%d s=%d\n",
473 obj, obj->sub, obj->state, obj->s);
474 write(s, buff, n);
476 write(s, "Subscribers :\n", 14);
477 for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
478 n = snprintf(buff, sizeof(buff), " %p timeout=%d seq=%u service=%d\n",
479 sub, (int)sub->timeout, sub->seq, sub->service);
480 write(s, buff, n);
481 n = snprintf(buff, sizeof(buff), " notify=%p %s\n",
482 sub->notify, sub->uuid);
483 write(s, buff, n);
484 n = snprintf(buff, sizeof(buff), " %s\n",
485 sub->callback);
486 write(s, buff, n);
489 #endif
491 #endif