Annotation of embedaddon/miniupnpd/upnpevents.c, revision 1.1.1.1
1.1 misho 1: /* $Id: upnpevents.c,v 1.13 2009/12/22 17:20:10 nanard Exp $ */
2: /* MiniUPnP project
3: * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4: * (c) 2008-2009 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 <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 { EWanCFG = 1, EWanIPC, EL3F } service;*/
41: enum subscriber_service_enum service;
42: char uuid[42];
43: char callback[];
44: };
45:
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];
63: };
64:
65: /* prototypes */
66: static void
67: upnp_event_create_notify(struct subscriber * sub);
68:
69: /* Subscriber list */
70: LIST_HEAD(listhead, subscriber) subscriberlist = { NULL };
71:
72: /* notify list */
73: LIST_HEAD(listheadnotif, upnp_event_notify) notifylist = { NULL };
74:
75: /* create a new subscriber */
76: static struct subscriber *
77: newSubscriber(const char * eventurl, const char * callback, int callbacklen)
78: {
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;
94: }
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;
103: }
104:
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)
111: {
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;
127: }
128:
129: /* renew a subscription (update the timeout) */
130: int
131: renewSubscription(const char * sid, int sidlen, int timeout)
132: {
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;
138: }
139: }
140: return -1;
141: }
142:
143: int
144: upnpevents_removeSubscriber(const char * sid, int sidlen)
145: {
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;
153: }
154: LIST_REMOVE(sub, entries);
155: free(sub);
156: return 0;
157: }
158: }
159: return -1;
160: }
161:
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)
166: {
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);
171: }
172: }
173:
174: /* create and add the notify object to the list */
175: static void
176: upnp_event_create_notify(struct subscriber * sub)
177: {
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;
184: }
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;
191: }
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;
196: }
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;
201: }
202: if(sub)
203: sub->notify = obj;
204: LIST_INSERT_HEAD(¬ifylist, obj, entries);
205: return;
206: error:
207: if(obj->s >= 0)
208: close(obj->s);
209: free(obj);
210: }
211:
212: static void
213: upnp_event_notify_connect(struct upnp_event_notify * obj)
214: {
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;
226: }
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++;
240: }
241: obj->portstr[i] = 0;
242: } else {
243: port = 80;
244: obj->portstr[0] = '\0';
245: }
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;
257: }
258: }
259: }
260:
261: static void upnp_event_prepare(struct upnp_event_notify * obj)
262: {
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;
281: }
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: default:
295: xml = NULL;
296: l = 0;
297: }
298: obj->buffersize = 1024;
299: obj->buffer = malloc(obj->buffersize);
300: /*if(!obj->buffer) {
301: }*/
302: obj->tosend = snprintf(obj->buffer, obj->buffersize, notifymsg,
303: obj->path, obj->addrstr, obj->portstr, l+2,
304: obj->sub->uuid, obj->sub->seq,
305: l, xml);
306: if(xml) {
307: free(xml);
308: xml = NULL;
309: }
310: obj->state = ESending;
311: }
312:
313: static void upnp_event_send(struct upnp_event_notify * obj)
314: {
315: int i;
316: syslog(LOG_DEBUG, "%s: sending event notify message to %s:%s",
317: "upnp_event_send", obj->addrstr, obj->portstr);
318: syslog(LOG_DEBUG, "%s: msg: %s",
319: "upnp_event_send", obj->buffer + obj->sent);
320: i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0);
321: if(i<0) {
322: syslog(LOG_NOTICE, "%s: send(): %m", "upnp_event_send");
323: obj->state = EError;
324: return;
325: }
326: else if(i != (obj->tosend - obj->sent))
327: syslog(LOG_NOTICE, "%s: %d bytes send out of %d",
328: "upnp_event_send", i, obj->tosend - obj->sent);
329: obj->sent += i;
330: if(obj->sent == obj->tosend)
331: obj->state = EWaitingForResponse;
332: }
333:
334: static void upnp_event_recv(struct upnp_event_notify * obj)
335: {
336: int n;
337: n = recv(obj->s, obj->buffer, obj->buffersize, 0);
338: if(n<0) {
339: syslog(LOG_ERR, "%s: recv(): %m", "upnp_event_recv");
340: obj->state = EError;
341: return;
342: }
343: syslog(LOG_DEBUG, "%s: (%dbytes) %.*s", "upnp_event_recv",
344: n, n, obj->buffer);
345: obj->state = EFinished;
346: if(obj->sub)
347: obj->sub->seq++;
348: }
349:
350: static void
351: upnp_event_process_notify(struct upnp_event_notify * obj)
352: {
353: switch(obj->state) {
354: case EConnecting:
355: /* now connected or failed to connect */
356: upnp_event_prepare(obj);
357: upnp_event_send(obj);
358: break;
359: case ESending:
360: upnp_event_send(obj);
361: break;
362: case EWaitingForResponse:
363: upnp_event_recv(obj);
364: break;
365: case EFinished:
366: close(obj->s);
367: obj->s = -1;
368: break;
369: default:
370: syslog(LOG_ERR, "upnp_event_process_notify: unknown state");
371: }
372: }
373:
374: void upnpevents_selectfds(fd_set *readset, fd_set *writeset, int * max_fd)
375: {
376: struct upnp_event_notify * obj;
377: for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
378: syslog(LOG_DEBUG, "upnpevents_selectfds: %p %d %d",
379: obj, obj->state, obj->s);
380: if(obj->s >= 0) {
381: switch(obj->state) {
382: case ECreated:
383: upnp_event_notify_connect(obj);
384: if(obj->state != EConnecting)
385: break;
386: case EConnecting:
387: case ESending:
388: FD_SET(obj->s, writeset);
389: if(obj->s > *max_fd)
390: *max_fd = obj->s;
391: break;
392: case EWaitingForResponse:
393: FD_SET(obj->s, readset);
394: if(obj->s > *max_fd)
395: *max_fd = obj->s;
396: break;
397: }
398: }
399: }
400: }
401:
402: void upnpevents_processfds(fd_set *readset, fd_set *writeset)
403: {
404: struct upnp_event_notify * obj;
405: struct upnp_event_notify * next;
406: struct subscriber * sub;
407: struct subscriber * subnext;
408: time_t curtime;
409: for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
410: syslog(LOG_DEBUG, "%s: %p %d %d %d %d",
411: "upnpevents_processfds", obj, obj->state, obj->s,
412: FD_ISSET(obj->s, readset), FD_ISSET(obj->s, writeset));
413: if(obj->s >= 0) {
414: if(FD_ISSET(obj->s, readset) || FD_ISSET(obj->s, writeset))
415: upnp_event_process_notify(obj);
416: }
417: }
418: obj = notifylist.lh_first;
419: while(obj != NULL) {
420: next = obj->entries.le_next;
421: if(obj->state == EError || obj->state == EFinished) {
422: if(obj->s >= 0) {
423: close(obj->s);
424: }
425: if(obj->sub)
426: obj->sub->notify = NULL;
427: /* remove also the subscriber from the list if there was an error */
428: if(obj->state == EError && obj->sub) {
429: LIST_REMOVE(obj->sub, entries);
430: free(obj->sub);
431: }
432: if(obj->buffer) {
433: free(obj->buffer);
434: }
435: LIST_REMOVE(obj, entries);
436: free(obj);
437: }
438: obj = next;
439: }
440: /* remove timeouted subscribers */
441: curtime = time(NULL);
442: for(sub = subscriberlist.lh_first; sub != NULL; ) {
443: subnext = sub->entries.le_next;
444: if(sub->timeout && curtime > sub->timeout && sub->notify == NULL) {
445: LIST_REMOVE(sub, entries);
446: free(sub);
447: }
448: sub = subnext;
449: }
450: }
451:
452: #ifdef USE_MINIUPNPDCTL
453: void write_events_details(int s) {
454: int n;
455: char buff[80];
456: struct upnp_event_notify * obj;
457: struct subscriber * sub;
458: write(s, "Events details :\n", 17);
459: for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) {
460: n = snprintf(buff, sizeof(buff), " %p sub=%p state=%d s=%d\n",
461: obj, obj->sub, obj->state, obj->s);
462: write(s, buff, n);
463: }
464: write(s, "Subscribers :\n", 14);
465: for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) {
466: n = snprintf(buff, sizeof(buff), " %p timeout=%d seq=%u service=%d\n",
467: sub, sub->timeout, sub->seq, sub->service);
468: write(s, buff, n);
469: n = snprintf(buff, sizeof(buff), " notify=%p %s\n",
470: sub->notify, sub->uuid);
471: write(s, buff, n);
472: n = snprintf(buff, sizeof(buff), " %s\n",
473: sub->callback);
474: write(s, buff, n);
475: }
476: }
477: #endif
478:
479: #endif
480:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>