Annotation of embedaddon/miniupnpd/upnpevents.c, revision 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>