Add PPTP runtime and GUI
[tomato.git] / release / src / router / miniupnpd / upnpevents.c
blob4a5e05a755a394972aa7ba1d0006a6a86645e716
1 /* $Id: upnpevents.c,v 1.17 2011/06/27 11:24:00 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 subscriber_service_enum service;
41 char uuid[42];
42 char callback[];
45 struct upnp_event_notify {
46 LIST_ENTRY(upnp_event_notify) entries;
47 int s; /* socket */
48 enum { ECreated=1,
49 EConnecting,
50 ESending,
51 EWaitingForResponse,
52 EFinished,
53 EError } state;
54 struct subscriber * sub;
55 char * buffer;
56 int buffersize;
57 int tosend;
58 int sent;
59 const char * path;
60 #ifdef ENABLE_IPV6
61 int ipv6;
62 char addrstr[48];
63 #else
64 char addrstr[16];
65 #endif
66 char portstr[8];
69 /* prototypes */
70 static void
71 upnp_event_create_notify(struct subscriber * sub);
73 /* Subscriber list */
74 LIST_HEAD(listhead, subscriber) subscriberlist = { NULL };
76 /* notify list */
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)
85 return NULL;
86 tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1);
87 if(!tmp)
88 return NULL;
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)
95 tmp->service = EL3F;
96 #endif
97 else {
98 free(tmp);
99 return NULL;
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);
108 return tmp;
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... */
115 const char *
116 upnpevents_addSubscriber(const char * eventurl,
117 const char * callback, int callbacklen,
118 int timeout)
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);
128 if(!tmp)
129 return NULL;
130 if(timeout)
131 tmp->timeout = time(NULL) + timeout;
132 LIST_INSERT_HEAD(&subscriberlist, tmp, entries);
133 upnp_event_create_notify(tmp);
134 return tmp->uuid;
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);
145 return 0;
148 return -1;
152 upnpevents_removeSubscriber(const char * sid, int sidlen)
154 struct subscriber * sub;
155 if(!sid)
156 return -1;
157 for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
158 if(memcmp(sid, sub->uuid, 41) == 0) {
159 if(sub->notify) {
160 sub->notify->sub = NULL;
162 LIST_REMOVE(sub, entries);
163 free(sub);
164 return 0;
167 return -1;
170 /* notifies all subscriber of a number of port mapping change
171 * or external ip address change */
172 void
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 */
183 static void
184 upnp_event_create_notify(struct subscriber * sub)
186 struct upnp_event_notify * obj;
187 int flags;
188 obj = calloc(1, sizeof(struct upnp_event_notify));
189 if(!obj) {
190 syslog(LOG_ERR, "%s: calloc(): %m", "upnp_event_create_notify");
191 return;
193 obj->sub = sub;
194 obj->state = ECreated;
195 #ifdef ENABLE_IPV6
196 obj->s = socket((obj->sub->callback[7] == '[') ? PF_INET6 : PF_INET,
197 SOCK_STREAM, 0);
198 #else
199 obj->s = socket(PF_INET, SOCK_STREAM, 0);
200 #endif
201 if(obj->s<0) {
202 syslog(LOG_ERR, "%s: socket(): %m", "upnp_event_create_notify");
203 goto error;
205 if((flags = fcntl(obj->s, F_GETFL, 0)) < 0) {
206 syslog(LOG_ERR, "%s: fcntl(..F_GETFL..): %m",
207 "upnp_event_create_notify");
208 goto error;
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");
213 goto error;
215 if(sub)
216 sub->notify = obj;
217 LIST_INSERT_HEAD(&notifylist, obj, entries);
218 return;
219 error:
220 if(obj->s >= 0)
221 close(obj->s);
222 free(obj);
225 static void
226 upnp_event_notify_connect(struct upnp_event_notify * obj)
228 int i;
229 const char * p;
230 unsigned short port;
231 #ifdef ENABLE_IPV6
232 struct sockaddr_storage addr;
233 #else
234 struct sockaddr_in addr;
235 #endif
236 if(!obj)
237 return;
238 memset(&addr, 0, sizeof(addr));
239 i = 0;
240 if(obj->sub == NULL) {
241 obj->state = EError;
242 return;
244 p = obj->sub->callback;
245 p += 7; /* http:// */
246 #ifdef ENABLE_IPV6
247 if(*p == '[') { /* ip v6 */
248 p++;
249 obj->ipv6 = 1;
250 while(*p != ']' && i < (sizeof(obj->addrstr)-1))
251 obj->addrstr[i++] = *(p++);
252 if(*p == ']')
253 p++;
254 } else {
255 #endif
256 while(*p != '/' && *p != ':' && i < (sizeof(obj->addrstr)-1))
257 obj->addrstr[i++] = *(p++);
258 #ifdef ENABLE_IPV6
260 #endif
261 obj->addrstr[i] = '\0';
262 if(*p == ':') {
263 obj->portstr[0] = *p;
264 i = 1;
265 p++;
266 port = (unsigned short)atoi(p);
267 while(*p != '/') {
268 if(i<7) obj->portstr[i++] = *p;
269 p++;
271 obj->portstr[i] = 0;
272 } else {
273 port = 80;
274 obj->portstr[0] = '\0';
276 obj->path = p;
277 #ifdef ENABLE_IPV6
278 if(obj->ipv6) {
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);
283 } else {
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);
289 #else
290 addr.sin_family = AF_INET;
291 inet_aton(obj->addrstr, &addr.sin_addr);
292 addr.sin_port = htons(port);
293 #endif
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");
300 obj->state = EError;
305 static void upnp_event_prepare(struct upnp_event_notify * obj)
307 static const char notifymsg[] =
308 "NOTIFY %s HTTP/1.1\r\n"
309 "Host: %s%s\r\n"
310 "Content-Type: text/xml\r\n"
311 "Content-Length: %d\r\n"
312 "NT: upnp:event\r\n"
313 "NTS: upnp:propchange\r\n"
314 "SID: %s\r\n"
315 "SEQ: %u\r\n"
316 "Connection: close\r\n"
317 "Cache-Control: no-cache\r\n"
318 "\r\n"
319 "%.*s\r\n";
320 char * xml;
321 int l;
322 if(obj->sub == NULL) {
323 obj->state = EError;
324 return;
326 switch(obj->sub->service) {
327 case EWanCFG:
328 xml = getVarsWANCfg(&l);
329 break;
330 case EWanIPC:
331 xml = getVarsWANIPCn(&l);
332 break;
333 #ifdef ENABLE_L3F_SERVICE
334 case EL3F:
335 xml = getVarsL3F(&l);
336 break;
337 #endif
338 #ifdef ENABLE_6FC_SERVICE
339 case E6FC:
340 xml = getVars6FC(&l);
341 break;
342 #endif
343 #ifdef ENABLE_DP_SERVICE
344 case EDP:
345 xml = getVarsDP(&l);
346 break;
347 #endif
348 default:
349 xml = NULL;
350 l = 0;
352 obj->buffersize = 1024;
353 obj->buffer = malloc(obj->buffersize);
354 /*if(!obj->buffer) {
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,
359 l, xml);
360 if(xml) {
361 free(xml);
362 xml = NULL;
364 obj->state = ESending;
367 static void upnp_event_send(struct upnp_event_notify * obj)
369 int i;
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);
375 if(i<0) {
376 syslog(LOG_DEBUG, "%s: send(): %m", "upnp_event_send");
377 obj->state = EError;
378 return;
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);
383 obj->sent += i;
384 if(obj->sent == obj->tosend)
385 obj->state = EWaitingForResponse;
388 static void upnp_event_recv(struct upnp_event_notify * obj)
390 int n;
391 n = recv(obj->s, obj->buffer, obj->buffersize, 0);
392 if(n<0) {
393 syslog(LOG_DEBUG, "%s: recv(): %m", "upnp_event_recv");
394 obj->state = EError;
395 return;
397 syslog(LOG_DEBUG, "%s: (%dbytes) %.*s", "upnp_event_recv",
398 n, n, obj->buffer);
399 obj->state = EFinished;
400 if(obj->sub)
401 obj->sub->seq++;
404 static void
405 upnp_event_process_notify(struct upnp_event_notify * obj)
407 switch(obj->state) {
408 case EConnecting:
409 /* now connected or failed to connect */
410 upnp_event_prepare(obj);
411 upnp_event_send(obj);
412 break;
413 case ESending:
414 upnp_event_send(obj);
415 break;
416 case EWaitingForResponse:
417 upnp_event_recv(obj);
418 break;
419 case EFinished:
420 close(obj->s);
421 obj->s = -1;
422 break;
423 default:
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);
434 if(obj->s >= 0) {
435 switch(obj->state) {
436 case ECreated:
437 upnp_event_notify_connect(obj);
438 if(obj->state != EConnecting)
439 break;
440 case EConnecting:
441 case ESending:
442 FD_SET(obj->s, writeset);
443 if(obj->s > *max_fd)
444 *max_fd = obj->s;
445 break;
446 case EWaitingForResponse:
447 FD_SET(obj->s, readset);
448 if(obj->s > *max_fd)
449 *max_fd = obj->s;
450 break;
451 default:
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;
464 time_t curtime;
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));
469 if(obj->s >= 0) {
470 if(FD_ISSET(obj->s, readset) || FD_ISSET(obj->s, writeset))
471 upnp_event_process_notify(obj);
474 obj = notifylist.lh_first;
475 while(obj != NULL) {
476 next = obj->entries.le_next;
477 if(obj->state == EError || obj->state == EFinished) {
478 if(obj->s >= 0) {
479 close(obj->s);
481 if(obj->sub)
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);
486 free(obj->sub);
488 if(obj->buffer) {
489 free(obj->buffer);
491 LIST_REMOVE(obj, entries);
492 free(obj);
494 obj = next;
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);
502 free(sub);
504 sub = subnext;
508 #ifdef USE_MINIUPNPDCTL
509 void write_events_details(int s) {
510 int n;
511 char buff[80];
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);
518 write(s, buff, n);
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);
524 write(s, buff, n);
525 n = snprintf(buff, sizeof(buff), " notify=%p %s\n",
526 sub->notify, sub->uuid);
527 write(s, buff, n);
528 n = snprintf(buff, sizeof(buff), " %s\n",
529 sub->callback);
530 write(s, buff, n);
533 #endif
535 #endif