File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / upnpevents.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 00:32:35 2013 UTC (10 years, 10 months ago) by misho
Branches: miniupnpd, elwix, MAIN
CVS tags: v1_8p0, v1_8, HEAD
1.8

    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(&notifylist, 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>