1: /* $Id: upnpevents.c,v 1.1.1.3 2013/07/22 00:32:35 misho Exp $ */
2: /* MiniUPnP project
3: * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4: * (c) 2008-2012 Thomas Bernard
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 <errno.h>
20: #include "config.h"
21: #include "upnpevents.h"
22: #include "miniupnpdpath.h"
23: #include "upnpglobalvars.h"
24: #include "upnpdescgen.h"
25: #include "upnputils.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;
60: #ifdef ENABLE_IPV6
61: int ipv6;
62: char addrstr[48];
63: #else
64: char addrstr[16];
65: #endif
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);
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: #ifdef ENABLE_6FC_SERVICE
98: else if(strcmp(eventurl, WANIP6FC_EVENTURL)==0)
99: tmp->service = E6FC;
100: #endif
101: #ifdef ENABLE_DP_SERVICE
102: else if(strcmp(eventurl, DP_EVENTURL)==0)
103: tmp->service = EDP;
104: #endif
105: else {
106: free(tmp);
107: return NULL;
108: }
109: memcpy(tmp->callback, callback, callbacklen);
110: tmp->callback[callbacklen] = '\0';
111: /* make a dummy uuid */
112: /* TODO: improve that */
113: strncpy(tmp->uuid, uuidvalue, sizeof(tmp->uuid));
114: tmp->uuid[sizeof(tmp->uuid)-1] = '\0';
115: snprintf(tmp->uuid+37, 5, "%04lx", random() & 0xffff);
116: return tmp;
117: }
118:
119: /* creates a new subscriber and adds it to the subscriber list
120: * also initiate 1st notify
121: * TODO : add a check on the number of subscriber in order to
122: * prevent memory overflow... */
123: const char *
124: upnpevents_addSubscriber(const char * eventurl,
125: const char * callback, int callbacklen,
126: int timeout)
127: {
128: struct subscriber * tmp;
129: /*static char uuid[42];*/
130: /* "uuid:00000000-0000-0000-0000-000000000000"; 5+36+1=42bytes */
131: syslog(LOG_DEBUG, "addSubscriber(%s, %.*s, %d)",
132: eventurl, callbacklen, callback, timeout);
133: /*strncpy(uuid, uuidvalue, sizeof(uuid));
134: uuid[sizeof(uuid)-1] = '\0';*/
135: tmp = newSubscriber(eventurl, callback, callbacklen);
136: if(!tmp)
137: return NULL;
138: if(timeout)
139: tmp->timeout = time(NULL) + timeout;
140: LIST_INSERT_HEAD(&subscriberlist, tmp, entries);
141: upnp_event_create_notify(tmp);
142: return tmp->uuid;
143: }
144:
145: /* renew a subscription (update the timeout) */
146: int
147: renewSubscription(const char * sid, int sidlen, int timeout)
148: {
149: struct subscriber * sub;
150: for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
151: if((sidlen == 41) && (memcmp(sid, sub->uuid, 41) == 0)) {
152: sub->timeout = (timeout ? time(NULL) + timeout : 0);
153: return 0;
154: }
155: }
156: return -1;
157: }
158:
159: int
160: upnpevents_removeSubscriber(const char * sid, int sidlen)
161: {
162: struct subscriber * sub;
163: if(!sid)
164: return -1;
165: for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
166: if((sidlen == 41) && (memcmp(sid, sub->uuid, 41) == 0)) {
167: if(sub->notify) {
168: sub->notify->sub = NULL;
169: }
170: LIST_REMOVE(sub, entries);
171: free(sub);
172: return 0;
173: }
174: }
175: return -1;
176: }
177:
178: /* notifies all subscriber of a number of port mapping change
179: * or external ip address change */
180: void
181: upnp_event_var_change_notify(enum subscriber_service_enum service)
182: {
183: struct subscriber * sub;
184: for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
185: if(sub->service == service && sub->notify == NULL)
186: upnp_event_create_notify(sub);
187: }
188: }
189:
190: /* create and add the notify object to the list */
191: static void
192: upnp_event_create_notify(struct subscriber * sub)
193: {
194: struct upnp_event_notify * obj;
195:
196: obj = calloc(1, sizeof(struct upnp_event_notify));
197: if(!obj) {
198: syslog(LOG_ERR, "%s: calloc(): %m", "upnp_event_create_notify");
199: return;
200: }
201: obj->sub = sub;
202: obj->state = ECreated;
203: #ifdef ENABLE_IPV6
204: obj->s = socket((obj->sub->callback[7] == '[') ? PF_INET6 : PF_INET,
205: SOCK_STREAM, 0);
206: #else
207: obj->s = socket(PF_INET, SOCK_STREAM, 0);
208: #endif
209: if(obj->s<0) {
210: syslog(LOG_ERR, "%s: socket(): %m", "upnp_event_create_notify");
211: goto error;
212: }
213: if(!set_non_blocking(obj->s)) {
214: syslog(LOG_ERR, "%s: set_non_blocking(): %m",
215: "upnp_event_create_notify");
216: goto error;
217: }
218: if(sub)
219: sub->notify = obj;
220: LIST_INSERT_HEAD(¬ifylist, obj, entries);
221: return;
222: error:
223: if(obj->s >= 0)
224: close(obj->s);
225: free(obj);
226: }
227:
228: static void
229: upnp_event_notify_connect(struct upnp_event_notify * obj)
230: {
231: unsigned int i;
232: const char * p;
233: unsigned short port;
234: #ifdef ENABLE_IPV6
235: struct sockaddr_storage addr;
236: #else
237: struct sockaddr_in addr;
238: #endif
239: if(!obj)
240: return;
241: memset(&addr, 0, sizeof(addr));
242: i = 0;
243: if(obj->sub == NULL) {
244: obj->state = EError;
245: return;
246: }
247: p = obj->sub->callback;
248: p += 7; /* http:// */
249: #ifdef ENABLE_IPV6
250: if(*p == '[') { /* ip v6 */
251: p++;
252: obj->ipv6 = 1;
253: while(*p != ']' && i < (sizeof(obj->addrstr)-1))
254: obj->addrstr[i++] = *(p++);
255: if(*p == ']')
256: p++;
257: } else {
258: #endif
259: while(*p != '/' && *p != ':' && i < (sizeof(obj->addrstr)-1))
260: obj->addrstr[i++] = *(p++);
261: #ifdef ENABLE_IPV6
262: }
263: #endif
264: obj->addrstr[i] = '\0';
265: if(*p == ':') {
266: obj->portstr[0] = *p;
267: i = 1;
268: p++;
269: port = (unsigned short)atoi(p);
270: while(*p != '/') {
271: if(i<7) obj->portstr[i++] = *p;
272: p++;
273: }
274: obj->portstr[i] = 0;
275: } else {
276: port = 80;
277: obj->portstr[0] = '\0';
278: }
279: obj->path = p;
280: #ifdef ENABLE_IPV6
281: if(obj->ipv6) {
282: struct sockaddr_in6 * sa = (struct sockaddr_in6 *)&addr;
283: sa->sin6_family = AF_INET6;
284: inet_pton(AF_INET6, obj->addrstr, &(sa->sin6_addr));
285: sa->sin6_port = htons(port);
286: } else {
287: struct sockaddr_in * sa = (struct sockaddr_in *)&addr;
288: sa->sin_family = AF_INET;
289: inet_pton(AF_INET, obj->addrstr, &(sa->sin_addr));
290: sa->sin_port = htons(port);
291: }
292: #else
293: addr.sin_family = AF_INET;
294: inet_aton(obj->addrstr, &addr.sin_addr);
295: addr.sin_port = htons(port);
296: #endif
297: syslog(LOG_DEBUG, "%s: '%s' %hu '%s'", "upnp_event_notify_connect",
298: obj->addrstr, port, obj->path);
299: obj->state = EConnecting;
300: if(connect(obj->s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
301: if(errno != EINPROGRESS && errno != EWOULDBLOCK) {
302: syslog(LOG_ERR, "%s: connect(): %m", "upnp_event_notify_connect");
303: obj->state = EError;
304: }
305: }
306: }
307:
308: static void upnp_event_prepare(struct upnp_event_notify * obj)
309: {
310: static const char notifymsg[] =
311: "NOTIFY %s HTTP/1.1\r\n"
312: "Host: %s%s\r\n"
313: "Content-Type: text/xml\r\n"
314: "Content-Length: %d\r\n"
315: "NT: upnp:event\r\n"
316: "NTS: upnp:propchange\r\n"
317: "SID: %s\r\n"
318: "SEQ: %u\r\n"
319: "Connection: close\r\n"
320: "Cache-Control: no-cache\r\n"
321: "\r\n"
322: "%.*s\r\n";
323: char * xml;
324: int l;
325: if(obj->sub == NULL) {
326: obj->state = EError;
327: return;
328: }
329: switch(obj->sub->service) {
330: case EWanCFG:
331: xml = getVarsWANCfg(&l);
332: break;
333: case EWanIPC:
334: xml = getVarsWANIPCn(&l);
335: break;
336: #ifdef ENABLE_L3F_SERVICE
337: case EL3F:
338: xml = getVarsL3F(&l);
339: break;
340: #endif
341: #ifdef ENABLE_6FC_SERVICE
342: case E6FC:
343: xml = getVars6FC(&l);
344: break;
345: #endif
346: #ifdef ENABLE_DP_SERVICE
347: case EDP:
348: xml = getVarsDP(&l);
349: break;
350: #endif
351: default:
352: xml = NULL;
353: l = 0;
354: }
355: obj->buffersize = 1024;
356: obj->buffer = malloc(obj->buffersize);
357: if(!obj->buffer) {
358: syslog(LOG_ERR, "%s: malloc returned NULL", "upnp_event_prepare");
359: if(xml) {
360: free(xml);
361: }
362: obj->state = EError;
363: return;
364: }
365: obj->tosend = snprintf(obj->buffer, obj->buffersize, notifymsg,
366: obj->path, obj->addrstr, obj->portstr, l+2,
367: obj->sub->uuid, obj->sub->seq,
368: l, xml);
369: if(xml) {
370: free(xml);
371: xml = NULL;
372: }
373: obj->state = ESending;
374: }
375:
376: static void upnp_event_send(struct upnp_event_notify * obj)
377: {
378: int i;
379:
380: syslog(LOG_DEBUG, "%s: sending event notify message to %s:%s",
381: "upnp_event_send", obj->addrstr, obj->portstr);
382: syslog(LOG_DEBUG, "%s: msg: %s",
383: "upnp_event_send", obj->buffer + obj->sent);
384: i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0);
385: if(i<0) {
386: if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
387: syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send");
388: obj->state = EError;
389: return;
390: } else {
391: /* EAGAIN or EWOULDBLOCK or EINTR : no data sent */
392: i = 0;
393: }
394: }
395: if(i != (obj->tosend - obj->sent))
396: syslog(LOG_NOTICE, "%s: %d bytes send out of %d",
397: "upnp_event_send", i, obj->tosend - obj->sent);
398: obj->sent += i;
399: if(obj->sent == obj->tosend)
400: obj->state = EWaitingForResponse;
401: }
402:
403: static void upnp_event_recv(struct upnp_event_notify * obj)
404: {
405: int n;
406: n = recv(obj->s, obj->buffer, obj->buffersize, 0);
407: if(n<0) {
408: if(errno != EAGAIN &&
409: errno != EWOULDBLOCK &&
410: errno != EINTR) {
411: syslog(LOG_ERR, "%s: recv(): %m", "upnp_event_recv");
412: obj->state = EError;
413: }
414: return;
415: }
416: syslog(LOG_DEBUG, "%s: (%dbytes) %.*s", "upnp_event_recv",
417: n, n, obj->buffer);
418: /* TODO : do something with the data recevied ?
419: * right now, n (number of bytes received) is ignored
420: * We may need to recv() more bytes. */
421: obj->state = EFinished;
422: if(obj->sub)
423: obj->sub->seq++;
424: }
425:
426: static void
427: upnp_event_process_notify(struct upnp_event_notify * obj)
428: {
429: int err;
430: socklen_t len;
431: switch(obj->state) {
432: case EConnecting:
433: /* now connected or failed to connect */
434: len = sizeof(err);
435: if(getsockopt(obj->s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
436: syslog(LOG_ERR, "%s: getsockopt: %m", "upnp_event_process_notify");
437: obj->state = EError;
438: break;
439: }
440: if(err != 0) {
441: errno = err;
442: syslog(LOG_WARNING, "%s: connect failed: %m", "upnp_event_process_notify");
443: obj->state = EError;
444: break;
445: }
446: upnp_event_prepare(obj);
447: if(obj->state == ESending)
448: upnp_event_send(obj);
449: break;
450: case ESending:
451: upnp_event_send(obj);
452: break;
453: case EWaitingForResponse:
454: upnp_event_recv(obj);
455: break;
456: case EFinished:
457: close(obj->s);
458: obj->s = -1;
459: break;
460: default:
461: syslog(LOG_ERR, "%s: unknown state", "upnp_event_process_notify");
462: }
463: }
464:
465: void upnpevents_selectfds(fd_set *readset, fd_set *writeset, int * max_fd)
466: {
467: struct upnp_event_notify * obj;
468: for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
469: syslog(LOG_DEBUG, "upnpevents_selectfds: %p %d %d",
470: obj, obj->state, obj->s);
471: if(obj->s >= 0) {
472: switch(obj->state) {
473: case ECreated:
474: upnp_event_notify_connect(obj);
475: if(obj->state != EConnecting)
476: break;
477: case EConnecting:
478: case ESending:
479: FD_SET(obj->s, writeset);
480: if(obj->s > *max_fd)
481: *max_fd = obj->s;
482: break;
483: case EWaitingForResponse:
484: FD_SET(obj->s, readset);
485: if(obj->s > *max_fd)
486: *max_fd = obj->s;
487: break;
488: default:
489: ;
490: }
491: }
492: }
493: }
494:
495: void upnpevents_processfds(fd_set *readset, fd_set *writeset)
496: {
497: struct upnp_event_notify * obj;
498: struct upnp_event_notify * next;
499: struct subscriber * sub;
500: struct subscriber * subnext;
501: time_t curtime;
502: for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
503: syslog(LOG_DEBUG, "%s: %p %d %d %d %d",
504: "upnpevents_processfds", obj, obj->state, obj->s,
505: FD_ISSET(obj->s, readset), FD_ISSET(obj->s, writeset));
506: if(obj->s >= 0) {
507: if(FD_ISSET(obj->s, readset) || FD_ISSET(obj->s, writeset))
508: upnp_event_process_notify(obj);
509: }
510: }
511: obj = notifylist.lh_first;
512: while(obj != NULL) {
513: next = obj->entries.le_next;
514: if(obj->state == EError || obj->state == EFinished) {
515: if(obj->s >= 0) {
516: close(obj->s);
517: }
518: if(obj->sub)
519: obj->sub->notify = NULL;
520: /* remove also the subscriber from the list if there was an error */
521: if(obj->state == EError && obj->sub) {
522: LIST_REMOVE(obj->sub, entries);
523: free(obj->sub);
524: }
525: if(obj->buffer) {
526: free(obj->buffer);
527: }
528: LIST_REMOVE(obj, entries);
529: free(obj);
530: }
531: obj = next;
532: }
533: /* remove timeouted subscribers */
534: curtime = time(NULL);
535: for(sub = subscriberlist.lh_first; sub != NULL; ) {
536: subnext = sub->entries.le_next;
537: if(sub->timeout && curtime > sub->timeout && sub->notify == NULL) {
538: LIST_REMOVE(sub, entries);
539: free(sub);
540: }
541: sub = subnext;
542: }
543: }
544:
545: #ifdef USE_MINIUPNPDCTL
546: void write_events_details(int s) {
547: int n;
548: char buff[80];
549: struct upnp_event_notify * obj;
550: struct subscriber * sub;
551: write(s, "Events details :\n", 17);
552: for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
553: n = snprintf(buff, sizeof(buff), " %p sub=%p state=%d s=%d\n",
554: obj, obj->sub, obj->state, obj->s);
555: write(s, buff, n);
556: }
557: write(s, "Subscribers :\n", 14);
558: for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
559: n = snprintf(buff, sizeof(buff), " %p timeout=%d seq=%u service=%d\n",
560: sub, (int)sub->timeout, sub->seq, sub->service);
561: write(s, buff, n);
562: n = snprintf(buff, sizeof(buff), " notify=%p %s\n",
563: sub->notify, sub->uuid);
564: write(s, buff, n);
565: n = snprintf(buff, sizeof(buff), " %s\n",
566: sub->callback);
567: write(s, buff, n);
568: }
569: }
570: #endif
571:
572: #endif
573:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>