Annotation of embedaddon/miniupnpd/upnpevents.c, revision 1.1.1.2
1.1.1.2 ! misho 1: /* $Id: upnpevents.c,v 1.17 2011/06/27 11:24:00 nanard Exp $ */
1.1 misho 2: /* MiniUPnP project
3: * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
1.1.1.2 ! misho 4: * (c) 2008-2011 Thomas Bernard
1.1 misho 5: * This software is subject to the conditions detailed
6: * in the LICENCE file provided within the distribution */
7:
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"
26:
27: #ifdef ENABLE_EVENTS
28: /*enum subscriber_service_enum {
29: EWanCFG = 1,
30: EWanIPC,
31: EL3F
32: };*/
33:
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[];
43: };
44:
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;
1.1.1.2 ! misho 60: #ifdef ENABLE_IPV6
! 61: int ipv6;
! 62: char addrstr[48];
! 63: #else
1.1 misho 64: char addrstr[16];
1.1.1.2 ! misho 65: #endif
1.1 misho 66: char portstr[8];
67: };
68:
69: /* prototypes */
70: static void
71: upnp_event_create_notify(struct subscriber * sub);
72:
73: /* Subscriber list */
74: LIST_HEAD(listhead, subscriber) subscriberlist = { NULL };
75:
76: /* notify list */
77: LIST_HEAD(listheadnotif, upnp_event_notify) notifylist = { NULL };
78:
79: /* create a new subscriber */
80: static struct subscriber *
81: newSubscriber(const char * eventurl, const char * callback, int callbacklen)
82: {
83: struct subscriber * tmp;
84: if(!eventurl || !callback || !callbacklen)
85: return NULL;
86: tmp = calloc(1, sizeof(struct subscriber)+callbacklen+1);
1.1.1.2 ! misho 87: if(!tmp)
! 88: return NULL;
1.1 misho 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;
100: }
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;
109: }
110:
111: /* creates a new subscriber and adds it to the subscriber list
1.1.1.2 ! misho 112: * also initiate 1st notify
! 113: * TODO : add a check on the number of subscriber in order to
! 114: * prevent memory overflow... */
1.1 misho 115: const char *
116: upnpevents_addSubscriber(const char * eventurl,
117: const char * callback, int callbacklen,
118: int timeout)
119: {
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;
135: }
136:
137: /* renew a subscription (update the timeout) */
138: int
139: renewSubscription(const char * sid, int sidlen, int timeout)
140: {
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;
146: }
147: }
148: return -1;
149: }
150:
151: int
152: upnpevents_removeSubscriber(const char * sid, int sidlen)
153: {
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;
161: }
162: LIST_REMOVE(sub, entries);
163: free(sub);
164: return 0;
165: }
166: }
167: return -1;
168: }
169:
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)
174: {
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);
179: }
180: }
181:
182: /* create and add the notify object to the list */
183: static void
184: upnp_event_create_notify(struct subscriber * sub)
185: {
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;
192: }
193: obj->sub = sub;
194: obj->state = ECreated;
1.1.1.2 ! misho 195: #ifdef ENABLE_IPV6
! 196: obj->s = socket((obj->sub->callback[7] == '[') ? PF_INET6 : PF_INET,
! 197: SOCK_STREAM, 0);
! 198: #else
1.1 misho 199: obj->s = socket(PF_INET, SOCK_STREAM, 0);
1.1.1.2 ! misho 200: #endif
1.1 misho 201: if(obj->s<0) {
202: syslog(LOG_ERR, "%s: socket(): %m", "upnp_event_create_notify");
203: goto error;
204: }
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;
209: }
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;
214: }
215: if(sub)
216: sub->notify = obj;
217: LIST_INSERT_HEAD(¬ifylist, obj, entries);
218: return;
219: error:
220: if(obj->s >= 0)
221: close(obj->s);
222: free(obj);
223: }
224:
225: static void
226: upnp_event_notify_connect(struct upnp_event_notify * obj)
227: {
228: int i;
229: const char * p;
230: unsigned short port;
1.1.1.2 ! misho 231: #ifdef ENABLE_IPV6
! 232: struct sockaddr_storage addr;
! 233: #else
1.1 misho 234: struct sockaddr_in addr;
1.1.1.2 ! misho 235: #endif
1.1 misho 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;
243: }
244: p = obj->sub->callback;
245: p += 7; /* http:// */
1.1.1.2 ! misho 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
! 259: }
! 260: #endif
1.1 misho 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++;
270: }
271: obj->portstr[i] = 0;
272: } else {
273: port = 80;
274: obj->portstr[0] = '\0';
275: }
276: obj->path = p;
1.1.1.2 ! misho 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);
! 288: }
! 289: #else
1.1 misho 290: addr.sin_family = AF_INET;
291: inet_aton(obj->addrstr, &addr.sin_addr);
292: addr.sin_port = htons(port);
1.1.1.2 ! misho 293: #endif
1.1 misho 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;
301: }
302: }
303: }
304:
305: static void upnp_event_prepare(struct upnp_event_notify * obj)
306: {
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;
325: }
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
1.1.1.2 ! misho 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
1.1 misho 348: default:
349: xml = NULL;
350: l = 0;
351: }
352: obj->buffersize = 1024;
353: obj->buffer = malloc(obj->buffersize);
354: /*if(!obj->buffer) {
355: }*/
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;
363: }
364: obj->state = ESending;
365: }
366:
367: static void upnp_event_send(struct upnp_event_notify * obj)
368: {
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_NOTICE, "%s: send(): %m", "upnp_event_send");
377: obj->state = EError;
378: return;
379: }
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;
386: }
387:
388: static void upnp_event_recv(struct upnp_event_notify * obj)
389: {
390: int n;
391: n = recv(obj->s, obj->buffer, obj->buffersize, 0);
392: if(n<0) {
393: syslog(LOG_ERR, "%s: recv(): %m", "upnp_event_recv");
394: obj->state = EError;
395: return;
396: }
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++;
402: }
403:
404: static void
405: upnp_event_process_notify(struct upnp_event_notify * obj)
406: {
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");
425: }
426: }
427:
428: void upnpevents_selectfds(fd_set *readset, fd_set *writeset, int * max_fd)
429: {
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;
1.1.1.2 ! misho 451: default:
! 452: ;
1.1 misho 453: }
454: }
455: }
456: }
457:
458: void upnpevents_processfds(fd_set *readset, fd_set *writeset)
459: {
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);
472: }
473: }
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);
480: }
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);
487: }
488: if(obj->buffer) {
489: free(obj->buffer);
490: }
491: LIST_REMOVE(obj, entries);
492: free(obj);
493: }
494: obj = next;
495: }
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);
503: }
504: sub = subnext;
505: }
506: }
507:
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);
519: }
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",
1.1.1.2 ! misho 523: sub, (int)sub->timeout, sub->seq, sub->service);
1.1 misho 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);
531: }
532: }
533: #endif
534:
535: #endif
536:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>