File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / upnpevents.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:55:57 2012 UTC (12 years, 1 month ago) by misho
Branches: miniupnpd, elwix, MAIN
CVS tags: v1_6elwix, HEAD
miniupnpd 1.6+patches

    1: /* $Id: upnpevents.c,v 1.1.1.2 2012/05/29 12:55:57 misho Exp $ */
    2: /* MiniUPnP project
    3:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
    4:  * (c) 2008-2011 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 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: 	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
  112:  * also initiate 1st notify
  113:  * TODO : add a check on the number of subscriber in order to
  114:  * prevent memory overflow... */
  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;
  195: #ifdef ENABLE_IPV6
  196: 	obj->s = socket((obj->sub->callback[7] == '[') ? PF_INET6 : PF_INET,
  197: 	                SOCK_STREAM, 0);
  198: #else
  199: 	obj->s = socket(PF_INET, SOCK_STREAM, 0);
  200: #endif
  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(&notifylist, 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;
  231: #ifdef ENABLE_IPV6
  232: 	struct sockaddr_storage addr;
  233: #else
  234: 	struct sockaddr_in addr;
  235: #endif
  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:// */
  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
  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;
  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
  290: 	addr.sin_family = AF_INET;
  291: 	inet_aton(obj->addrstr, &addr.sin_addr);
  292: 	addr.sin_port = htons(port);
  293: #endif
  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
  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
  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;
  451: 			default:
  452: 				;
  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",
  523: 		             sub, (int)sub->timeout, sub->seq, sub->service);
  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>