File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / upnpevents.c
Revision 1.1: download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:16:02 2012 UTC (12 years, 4 months ago) by misho
CVS tags: MAIN, HEAD
Initial revision

    1: /* $Id: upnpevents.c,v 1.1 2012/02/21 23:16:02 misho 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(&notifylist, 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>