File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / minissdp.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: minissdp.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) 2006-2013 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 <stdlib.h>
   10: #include <string.h>
   11: #include <unistd.h>
   12: #include <sys/socket.h>
   13: #include <sys/un.h>
   14: #include <netinet/in.h>
   15: #include <arpa/inet.h>
   16: #include <errno.h>
   17: #include <syslog.h>
   18: 
   19: #include "config.h"
   20: #include "upnpdescstrings.h"
   21: #include "miniupnpdpath.h"
   22: #include "upnphttp.h"
   23: #include "upnpglobalvars.h"
   24: #include "minissdp.h"
   25: #include "upnputils.h"
   26: #include "getroute.h"
   27: #include "codelength.h"
   28: 
   29: /* SSDP ip/port */
   30: #define SSDP_PORT (1900)
   31: #define SSDP_MCAST_ADDR ("239.255.255.250")
   32: #define LL_SSDP_MCAST_ADDR "FF02::C"
   33: #define SL_SSDP_MCAST_ADDR "FF05::C"
   34: 
   35: /* AddMulticastMembership()
   36:  * param s		socket
   37:  * param ifaddr	ip v4 address
   38:  */
   39: static int
   40: AddMulticastMembership(int s, in_addr_t ifaddr)
   41: {
   42: 	struct ip_mreq imr;	/* Ip multicast membership */
   43: 
   44:     /* setting up imr structure */
   45:     imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR);
   46:     /*imr.imr_interface.s_addr = htonl(INADDR_ANY);*/
   47:     imr.imr_interface.s_addr = ifaddr;	/*inet_addr(ifaddr);*/
   48: 
   49: 	if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(struct ip_mreq)) < 0)
   50: 	{
   51:         syslog(LOG_ERR, "setsockopt(udp, IP_ADD_MEMBERSHIP): %m");
   52: 		return -1;
   53:     }
   54: 
   55: 	return 0;
   56: }
   57: 
   58: /* AddMulticastMembershipIPv6()
   59:  * param s	socket (IPv6)
   60:  * To be improved to target specific network interfaces */
   61: #ifdef ENABLE_IPV6
   62: static int
   63: AddMulticastMembershipIPv6(int s)
   64: {
   65: 	struct ipv6_mreq mr;
   66: 	/*unsigned int ifindex;*/
   67: 
   68: 	memset(&mr, 0, sizeof(mr));
   69: 	inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
   70: 	/*mr.ipv6mr_interface = ifindex;*/
   71: 	mr.ipv6mr_interface = 0; /* 0 : all interfaces */
   72: #ifndef IPV6_ADD_MEMBERSHIP
   73: #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
   74: #endif
   75: 	if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
   76: 	{
   77: 		syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
   78: 		return -1;
   79: 	}
   80: 	inet_pton(AF_INET6, SL_SSDP_MCAST_ADDR, &mr.ipv6mr_multiaddr);
   81: 	if(setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(struct ipv6_mreq)) < 0)
   82: 	{
   83: 		syslog(LOG_ERR, "setsockopt(udp, IPV6_ADD_MEMBERSHIP): %m");
   84: 		return -1;
   85: 	}
   86: 	return 0;
   87: }
   88: #endif
   89: 
   90: /* Open and configure the socket listening for
   91:  * SSDP udp packets sent on 239.255.255.250 port 1900
   92:  * SSDP v6 udp packets sent on FF02::C, or FF05::C, port 1900 */
   93: int
   94: OpenAndConfSSDPReceiveSocket(int ipv6)
   95: {
   96: 	int s;
   97: 	struct sockaddr_storage sockname;
   98: 	socklen_t sockname_len;
   99: 	struct lan_addr_s * lan_addr;
  100: 	int j = 1;
  101: 
  102: 	if( (s = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0)) < 0)
  103: 	{
  104: 		syslog(LOG_ERR, "%s: socket(udp): %m",
  105: 		       "OpenAndConfSSDPReceiveSocket");
  106: 		return -1;
  107: 	}
  108: 
  109: 	memset(&sockname, 0, sizeof(struct sockaddr_storage));
  110: 	if(ipv6) {
  111: 		struct sockaddr_in6 * saddr = (struct sockaddr_in6 *)&sockname;
  112: 		saddr->sin6_family = AF_INET6;
  113: 		saddr->sin6_port = htons(SSDP_PORT);
  114: 		saddr->sin6_addr = in6addr_any;
  115: 		sockname_len = sizeof(struct sockaddr_in6);
  116: 	} else {
  117: 		struct sockaddr_in * saddr = (struct sockaddr_in *)&sockname;
  118: 		saddr->sin_family = AF_INET;
  119: 		saddr->sin_port = htons(SSDP_PORT);
  120: 		/* NOTE : it seems it doesnt work when binding on the specific address */
  121: 		/*saddr->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);*/
  122: 		saddr->sin_addr.s_addr = htonl(INADDR_ANY);
  123: 		/*saddr->sin_addr.s_addr = inet_addr(ifaddr);*/
  124: 		sockname_len = sizeof(struct sockaddr_in);
  125: 	}
  126: 
  127: 	if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &j, sizeof(j)) < 0)
  128: 	{
  129: 		syslog(LOG_WARNING, "setsockopt(udp, SO_REUSEADDR): %m");
  130: 	}
  131: 
  132: 	if(!set_non_blocking(s))
  133: 	{
  134: 		syslog(LOG_WARNING, "%s: set_non_blocking(): %m",
  135: 		       "OpenAndConfSSDPReceiveSocket");
  136: 	}
  137: 
  138: 	if(bind(s, (struct sockaddr *)&sockname, sockname_len) < 0)
  139: 	{
  140: 		syslog(LOG_ERR, "%s: bind(udp%s): %m",
  141: 		       "OpenAndConfSSDPReceiveSocket", ipv6 ? "6" : "");
  142: 		close(s);
  143: 		return -1;
  144: 	}
  145: 
  146: #ifdef ENABLE_IPV6
  147: 	if(ipv6)
  148: 	{
  149: 		AddMulticastMembershipIPv6(s);
  150: 	}
  151: 	else
  152: #endif
  153: 	{
  154: 		for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
  155: 		{
  156: 			if(AddMulticastMembership(s, lan_addr->addr.s_addr) < 0)
  157: 			{
  158: 				syslog(LOG_WARNING,
  159: 				       "Failed to add multicast membership for interface %s",
  160: 				       lan_addr->str ? lan_addr->str : "NULL");
  161: 			}
  162: 		}
  163: 	}
  164: 
  165: 	return s;
  166: }
  167: 
  168: /* open the UDP socket used to send SSDP notifications to
  169:  * the multicast group reserved for them */
  170: static int
  171: OpenAndConfSSDPNotifySocket(in_addr_t addr)
  172: {
  173: 	int s;
  174: 	unsigned char loopchar = 0;
  175: 	int bcast = 1;
  176: 	unsigned char ttl = 2; /* UDA v1.1 says :
  177: 		The TTL for the IP packet SHOULD default to 2 and
  178: 		SHOULD be configurable. */
  179: 	/* TODO: Make TTL be configurable */
  180: 	struct in_addr mc_if;
  181: 	struct sockaddr_in sockname;
  182: 
  183: 	if( (s = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
  184: 	{
  185: 		syslog(LOG_ERR, "socket(udp_notify): %m");
  186: 		return -1;
  187: 	}
  188: 
  189: 	mc_if.s_addr = addr;	/*inet_addr(addr);*/
  190: 
  191: 	if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopchar, sizeof(loopchar)) < 0)
  192: 	{
  193: 		syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_LOOP): %m");
  194: 		close(s);
  195: 		return -1;
  196: 	}
  197: 
  198: 	if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&mc_if, sizeof(mc_if)) < 0)
  199: 	{
  200: 		syslog(LOG_ERR, "setsockopt(udp_notify, IP_MULTICAST_IF): %m");
  201: 		close(s);
  202: 		return -1;
  203: 	}
  204: 
  205: 	if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
  206: 	{
  207: 		syslog(LOG_WARNING, "setsockopt(udp_notify, IP_MULTICAST_TTL,): %m");
  208: 	}
  209: 
  210: 	if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) < 0)
  211: 	{
  212: 		syslog(LOG_ERR, "setsockopt(udp_notify, SO_BROADCAST): %m");
  213: 		close(s);
  214: 		return -1;
  215: 	}
  216: 
  217: 	memset(&sockname, 0, sizeof(struct sockaddr_in));
  218:     sockname.sin_family = AF_INET;
  219:     sockname.sin_addr.s_addr = addr;	/*inet_addr(addr);*/
  220: 
  221:     if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0)
  222: 	{
  223: 		syslog(LOG_ERR, "bind(udp_notify): %m");
  224: 		close(s);
  225: 		return -1;
  226:     }
  227: 
  228: 	return s;
  229: }
  230: 
  231: #ifdef ENABLE_IPV6
  232: /* open the UDP socket used to send SSDP notifications to
  233:  * the multicast group reserved for them. IPv6 */
  234: static int
  235: OpenAndConfSSDPNotifySocketIPv6(unsigned int if_index)
  236: {
  237: 	int s;
  238: 	unsigned int loop = 0;
  239: 
  240: 	s = socket(PF_INET6, SOCK_DGRAM, 0);
  241: 	if(s < 0)
  242: 	{
  243: 		syslog(LOG_ERR, "socket(udp_notify IPv6): %m");
  244: 		return -1;
  245: 	}
  246: 	if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof(if_index)) < 0)
  247: 	{
  248: 		syslog(LOG_ERR, "setsockopt(udp_notify IPv6, IPV6_MULTICAST_IF, %u): %m", if_index);
  249: 		close(s);
  250: 		return -1;
  251: 	}
  252: 	if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) < 0)
  253: 	{
  254: 		syslog(LOG_ERR, "setsockopt(udp_notify, IPV6_MULTICAST_LOOP): %m");
  255: 		close(s);
  256: 		return -1;
  257: 	}
  258: 	return s;
  259: }
  260: #endif
  261: 
  262: int
  263: OpenAndConfSSDPNotifySockets(int * sockets)
  264: /*OpenAndConfSSDPNotifySockets(int * sockets,
  265:                              struct lan_addr_s * lan_addr, int n_lan_addr)*/
  266: {
  267: 	int i;
  268: 	struct lan_addr_s * lan_addr;
  269: 
  270: 	for(i=0, lan_addr = lan_addrs.lh_first;
  271: 	    lan_addr != NULL;
  272: 	    lan_addr = lan_addr->list.le_next)
  273: 	{
  274: 		sockets[i] = OpenAndConfSSDPNotifySocket(lan_addr->addr.s_addr);
  275: 		if(sockets[i] < 0)
  276: 			goto error;
  277: 		i++;
  278: #ifdef ENABLE_IPV6
  279: 		sockets[i] = OpenAndConfSSDPNotifySocketIPv6(lan_addr->index);
  280: 		if(sockets[i] < 0)
  281: 			goto error;
  282: 		i++;
  283: #endif
  284: 	}
  285: 	return 0;
  286: error:
  287: 	while(--i >= 0)
  288: 	{
  289: 		close(sockets[i]);
  290: 		sockets[i] = -1;
  291: 	}
  292: 	return -1;
  293: }
  294: 
  295: /*
  296:  * response from a LiveBox (Wanadoo)
  297: HTTP/1.1 200 OK
  298: CACHE-CONTROL: max-age=1800
  299: DATE: Thu, 01 Jan 1970 04:03:23 GMT
  300: EXT:
  301: LOCATION: http://192.168.0.1:49152/gatedesc.xml
  302: SERVER: Linux/2.4.17, UPnP/1.0, Intel SDK for UPnP devices /1.2
  303: ST: upnp:rootdevice
  304: USN: uuid:75802409-bccb-40e7-8e6c-fa095ecce13e::upnp:rootdevice
  305: 
  306:  * response from a Linksys 802.11b :
  307: HTTP/1.1 200 OK
  308: Cache-Control:max-age=120
  309: Location:http://192.168.5.1:5678/rootDesc.xml
  310: Server:NT/5.0 UPnP/1.0
  311: ST:upnp:rootdevice
  312: USN:uuid:upnp-InternetGatewayDevice-1_0-0090a2777777::upnp:rootdevice
  313: EXT:
  314:  */
  315: 
  316: /* not really an SSDP "announce" as it is the response
  317:  * to a SSDP "M-SEARCH" */
  318: static void
  319: SendSSDPAnnounce2(int s, const struct sockaddr * addr,
  320:                   const char * st, int st_len, const char * suffix,
  321:                   const char * host, unsigned short port)
  322: {
  323: 	int l, n;
  324: 	char buf[512];
  325: 	char addr_str[64];
  326: 	socklen_t addrlen;
  327: 	/*
  328: 	 * follow guideline from document "UPnP Device Architecture 1.0"
  329: 	 * uppercase is recommended.
  330: 	 * DATE: is recommended
  331: 	 * SERVER: OS/ver UPnP/1.0 miniupnpd/1.0
  332: 	 * - check what to put in the 'Cache-Control' header
  333: 	 *
  334: 	 * have a look at the document "UPnP Device Architecture v1.1 */
  335: 	l = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\n"
  336: 		"CACHE-CONTROL: max-age=120\r\n"
  337: 		/*"DATE: ...\r\n"*/
  338: 		"ST: %.*s%s\r\n"
  339: 		"USN: %s::%.*s%s\r\n"
  340: 		"EXT:\r\n"
  341: 		"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
  342: 		"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
  343: 		"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
  344: 		"01-NLS: %u\r\n" /* same as BOOTID. UDA v1.1 */
  345: 		"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
  346: 		"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
  347: 		"\r\n",
  348: 		st_len, st, suffix,
  349: 		uuidvalue, st_len, st, suffix,
  350: 		host, (unsigned int)port,
  351: 		upnp_bootid, upnp_bootid, upnp_configid);
  352: 	addrlen = (addr->sa_family == AF_INET6)
  353: 	          ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
  354: 	n = sendto(s, buf, l, 0,
  355: 	           addr, addrlen);
  356: 	sockaddr_to_string(addr, addr_str, sizeof(addr_str));
  357: 	syslog(LOG_INFO, "SSDP Announce %d bytes to %s ST: %.*s",n,
  358:        		addr_str,
  359: 		l, buf);
  360: 	if(n < 0)
  361: 	{
  362: 		/* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
  363: 		syslog(LOG_ERR, "sendto(udp): %m");
  364: 	}
  365: }
  366: 
  367: #ifndef IGD_V2
  368: #define IGD_VER 1
  369: #define WANIPC_VER 1
  370: #else
  371: #define IGD_VER 2
  372: #define WANIPC_VER 2
  373: #endif
  374: 
  375: static struct {
  376: 	const char * s;
  377: 	const int version;
  378: } const known_service_types[] =
  379: {
  380: 	{"upnp:rootdevice", 0},
  381: 	{"urn:schemas-upnp-org:device:InternetGatewayDevice:", IGD_VER},
  382: 	{"urn:schemas-upnp-org:device:WANConnectionDevice:", 1},
  383: 	{"urn:schemas-upnp-org:device:WANDevice:", 1},
  384: 	{"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:", 1},
  385: 	{"urn:schemas-upnp-org:service:WANIPConnection:", WANIPC_VER},
  386: 	{"urn:schemas-upnp-org:service:WANPPPConnection:", 1},
  387: #ifdef ENABLE_L3F_SERVICE
  388: 	{"urn:schemas-upnp-org:service:Layer3Forwarding:", 1},
  389: #endif
  390: #ifdef ENABLE_6FC_SERVICE
  391: 	{"url:schemas-upnp-org:service:WANIPv6FirewallControl:", 1},
  392: #endif
  393: 	{0, 0}
  394: };
  395: 
  396: static void
  397: SendSSDPNotifies(int s, const char * host, unsigned short port,
  398:                  unsigned int lifetime, int ipv6)
  399: {
  400: #ifdef ENABLE_IPV6
  401: 	struct sockaddr_storage sockname;
  402: #else
  403: 	struct sockaddr_in sockname;
  404: #endif
  405: 	int l, n, i=0;
  406: 	char bufr[512];
  407: 	char ver_str[4];
  408: 
  409: 	memset(&sockname, 0, sizeof(sockname));
  410: #ifdef ENABLE_IPV6
  411: 	if(ipv6)
  412: 	{
  413: 		struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockname;
  414: 		p->sin6_family = AF_INET6;
  415: 		p->sin6_port = htons(SSDP_PORT);
  416: 		inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(p->sin6_addr));
  417: 	}
  418: 	else
  419: #endif
  420: 	{
  421: 		struct sockaddr_in *p = (struct sockaddr_in *)&sockname;
  422: 		p->sin_family = AF_INET;
  423: 		p->sin_port = htons(SSDP_PORT);
  424: 		p->sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
  425: 	}
  426: 
  427: 	while(known_service_types[i].s)
  428: 	{
  429: 		if(i==0)
  430: 			ver_str[0] = '\0';
  431: 		else
  432: 			snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
  433: 		l = snprintf(bufr, sizeof(bufr),
  434: 			"NOTIFY * HTTP/1.1\r\n"
  435: 			"HOST: %s:%d\r\n"
  436: 			"CACHE-CONTROL: max-age=%u\r\n"
  437: 			"lOCATION: http://%s:%d" ROOTDESC_PATH"\r\n"
  438: 			"SERVER: " MINIUPNPD_SERVER_STRING "\r\n"
  439: 			"NT: %s%s\r\n"
  440: 			"USN: %s::%s%s\r\n"
  441: 			"NTS: ssdp:alive\r\n"
  442: 			"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
  443: 			"01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
  444: 			"BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
  445: 			"CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
  446: 			"\r\n",
  447: 			ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR,
  448: 			SSDP_PORT,
  449: 			lifetime,
  450: 			host, port,
  451: 			known_service_types[i].s, ver_str,
  452: 			uuidvalue, known_service_types[i].s, ver_str,
  453: 			upnp_bootid, upnp_bootid, upnp_configid );
  454: 		if(l<0)
  455: 		{
  456: 			syslog(LOG_ERR, "SendSSDPNotifies() snprintf error");
  457: 			continue;
  458: 		}
  459: 		if((unsigned int)l >= sizeof(bufr))
  460: 		{
  461: 			syslog(LOG_WARNING, "SendSSDPNotifies(): truncated output");
  462: 			l = sizeof(bufr);
  463: 		}
  464: 		n = sendto(s, bufr, l, 0,
  465: 			(struct sockaddr *)&sockname,
  466: #ifdef ENABLE_IPV6
  467: 			ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)
  468: #else
  469: 			sizeof(struct sockaddr_in)
  470: #endif
  471: 			);
  472: 		if(n < 0)
  473: 		{
  474: 			/* XXX handle EINTR, EAGAIN, EWOULDBLOCK */
  475: 			syslog(LOG_ERR, "sendto(udp_notify=%d, %s): %m", s,
  476: 			       host ? host : "NULL");
  477: 		}
  478: 		i++;
  479: 	}
  480: }
  481: 
  482: void
  483: SendSSDPNotifies2(int * sockets,
  484:                   unsigned short port,
  485:                   unsigned int lifetime)
  486: {
  487: 	int i;
  488: 	struct lan_addr_s * lan_addr;
  489: 	for(i=0, lan_addr = lan_addrs.lh_first;
  490: 	    lan_addr != NULL;
  491: 	    lan_addr = lan_addr->list.le_next)
  492: 	{
  493: 		SendSSDPNotifies(sockets[i], lan_addr->str, port,
  494: 		                 lifetime, 0);
  495: 		i++;
  496: #ifdef ENABLE_IPV6
  497: 		SendSSDPNotifies(sockets[i], ipv6_addr_for_http_with_brackets, port,
  498: 		                 lifetime, 1);
  499: 		i++;
  500: #endif
  501: 	}
  502: }
  503: 
  504: /* ProcessSSDPRequest()
  505:  * process SSDP M-SEARCH requests and responds to them */
  506: void
  507: ProcessSSDPRequest(int s, unsigned short port)
  508: {
  509: 	int n;
  510: 	char bufr[1500];
  511: 	socklen_t len_r;
  512: #ifdef ENABLE_IPV6
  513: 	struct sockaddr_storage sendername;
  514: 	len_r = sizeof(struct sockaddr_storage);
  515: #else
  516: 	struct sockaddr_in sendername;
  517: 	len_r = sizeof(struct sockaddr_in);
  518: #endif
  519: 
  520: 	n = recvfrom(s, bufr, sizeof(bufr), 0,
  521: 	             (struct sockaddr *)&sendername, &len_r);
  522: 	if(n < 0)
  523: 	{
  524: 		/* EAGAIN, EWOULDBLOCK, EINTR : silently ignore (try again next time)
  525: 		 * other errors : log to LOG_ERR */
  526: 		if(errno != EAGAIN &&
  527: 		   errno != EWOULDBLOCK &&
  528: 		   errno != EINTR)
  529: 		{
  530: 			syslog(LOG_ERR, "recvfrom(udp): %m");
  531: 		}
  532: 		return;
  533: 	}
  534: 	ProcessSSDPData(s, bufr, n, (struct sockaddr *)&sendername, port);
  535: 
  536: }
  537: 
  538: void
  539: ProcessSSDPData(int s, const char *bufr, int n,
  540:                 const struct sockaddr * sender, unsigned short port) {
  541: 	int i, l;
  542: 	struct lan_addr_s * lan_addr = NULL;
  543: 	const char * st = NULL;
  544: 	int st_len = 0;
  545: 	int st_ver = 0;
  546: 	char sender_str[64];
  547: 	char ver_str[4];
  548: 	const char * announced_host = NULL;
  549: #ifdef ENABLE_IPV6
  550: #ifdef UPNP_STRICT
  551: 	char announced_host_buf[64];
  552: #endif
  553: #endif
  554: 
  555: 	/* get the string representation of the sender address */
  556: 	sockaddr_to_string(sender, sender_str, sizeof(sender_str));
  557: 	lan_addr = get_lan_for_peer(sender);
  558: 	if(lan_addr == NULL)
  559: 	{
  560: 		syslog(LOG_WARNING, "SSDP packet sender %s not from a LAN, ignoring",
  561: 		       sender_str);
  562: 		return;
  563: 	}
  564: 
  565: 	if(memcmp(bufr, "NOTIFY", 6) == 0)
  566: 	{
  567: 		/* ignore NOTIFY packets. We could log the sender and device type */
  568: 		return;
  569: 	}
  570: 	else if(memcmp(bufr, "M-SEARCH", 8) == 0)
  571: 	{
  572: 		i = 0;
  573: 		while(i < n)
  574: 		{
  575: 			while((i < n - 1) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
  576: 				i++;
  577: 			i += 2;
  578: 			if((i < n - 3) && (strncasecmp(bufr+i, "st:", 3) == 0))
  579: 			{
  580: 				st = bufr+i+3;
  581: 				st_len = 0;
  582: 				while((*st == ' ' || *st == '\t') && (st < bufr + n))
  583: 					st++;
  584: 				while(st[st_len]!='\r' && st[st_len]!='\n'
  585: 				     && (st + st_len < bufr + n))
  586: 					st_len++;
  587: 				l = st_len;
  588: 				while(l > 0 && st[l-1] != ':')
  589: 					l--;
  590: 				st_ver = atoi(st+l);
  591: 				syslog(LOG_DEBUG, "ST: %.*s (ver=%d)", st_len, st, st_ver);
  592: 				/*j = 0;*/
  593: 				/*while(bufr[i+j]!='\r') j++;*/
  594: 				/*syslog(LOG_INFO, "%.*s", j, bufr+i);*/
  595: 			}
  596: 		}
  597: 		/*syslog(LOG_INFO, "SSDP M-SEARCH packet received from %s",
  598: 	           sender_str );*/
  599: 		if(st && (st_len > 0))
  600: 		{
  601: 			/* TODO : doesnt answer at once but wait for a random time */
  602: 			syslog(LOG_INFO, "SSDP M-SEARCH from %s ST: %.*s",
  603: 			       sender_str, st_len, st);
  604: 			/* find in which sub network the client is */
  605: 			if(sender->sa_family == AF_INET)
  606: 			{
  607: 				if (lan_addr == NULL)
  608: 				{
  609: 					syslog(LOG_ERR, "Can't find in which sub network the client is");
  610: 					return;
  611: 				}
  612: 				announced_host = lan_addr->str;
  613: 			}
  614: #ifdef ENABLE_IPV6
  615: 			else
  616: 			{
  617: 				/* IPv6 address with brackets */
  618: #ifdef UPNP_STRICT
  619: 				int index;
  620: 				struct in6_addr addr6;
  621: 				size_t addr6_len = sizeof(addr6);
  622: 				/* retrieve the IPv6 address which
  623: 				 * will be used locally to reach sender */
  624: 				memset(&addr6, 0, sizeof(addr6));
  625: 				if(get_src_for_route_to (sender, &addr6, &addr6_len, &index) < 0) {
  626: 					syslog(LOG_WARNING, "get_src_for_route_to() failed, using %s", ipv6_addr_for_http_with_brackets);
  627: 					announced_host = ipv6_addr_for_http_with_brackets;
  628: 				} else {
  629: 					if(inet_ntop(AF_INET6, &addr6,
  630: 					             announced_host_buf+1,
  631: 					             sizeof(announced_host_buf) - 2)) {
  632: 						announced_host_buf[0] = '[';
  633: 						i = strlen(announced_host_buf);
  634: 						if(i < (int)sizeof(announced_host_buf) - 1) {
  635: 							announced_host_buf[i] = ']';
  636: 							announced_host_buf[i+1] = '\0';
  637: 						} else {
  638: 							syslog(LOG_NOTICE, "cannot suffix %s with ']'",
  639: 							       announced_host_buf);
  640: 						}
  641: 						announced_host = announced_host_buf;
  642: 					} else {
  643: 						syslog(LOG_NOTICE, "inet_ntop() failed %m");
  644: 						announced_host = ipv6_addr_for_http_with_brackets;
  645: 					}
  646: 				}
  647: #else
  648: 				announced_host = ipv6_addr_for_http_with_brackets;
  649: #endif
  650: 			}
  651: #endif
  652: 			/* Responds to request with a device as ST header */
  653: 			for(i = 0; known_service_types[i].s; i++)
  654: 			{
  655: 				l = (int)strlen(known_service_types[i].s);
  656: 				if(l<=st_len && (0 == memcmp(st, known_service_types[i].s, l))
  657: #ifdef UPNP_STRICT
  658: 				   && (st_ver <= known_service_types[i].version)
  659: 		/* only answer for service version lower or equal of supported one */
  660: #endif
  661: 				   )
  662: 				{
  663: 					syslog(LOG_INFO, "Single search found");
  664: 					SendSSDPAnnounce2(s, sender,
  665: 					                  st, st_len, "",
  666: 					                  announced_host, port);
  667: 					break;
  668: 				}
  669: 			}
  670: 			/* Responds to request with ST: ssdp:all */
  671: 			/* strlen("ssdp:all") == 8 */
  672: 			if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
  673: 			{
  674: 				syslog(LOG_INFO, "ssdp:all found");
  675: 				for(i=0; known_service_types[i].s; i++)
  676: 				{
  677: 					if(i==0)
  678: 						ver_str[0] = '\0';
  679: 					else
  680: 						snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
  681: 					l = (int)strlen(known_service_types[i].s);
  682: 					SendSSDPAnnounce2(s, sender,
  683: 					                  known_service_types[i].s, l, ver_str,
  684: 					                  announced_host, port);
  685: 				}
  686: 				/* also answer for uuid */
  687: 				SendSSDPAnnounce2(s, sender, uuidvalue, strlen(uuidvalue), "",
  688: 				                  announced_host, port);
  689: 			}
  690: 			/* responds to request by UUID value */
  691: 			l = (int)strlen(uuidvalue);
  692: 			if(l==st_len && (0 == memcmp(st, uuidvalue, l)))
  693: 			{
  694: 				syslog(LOG_INFO, "ssdp:uuid found");
  695: 				SendSSDPAnnounce2(s, sender, st, st_len, "",
  696: 				                  announced_host, port);
  697: 			}
  698: 		}
  699: 		else
  700: 		{
  701: 			syslog(LOG_INFO, "Invalid SSDP M-SEARCH from %s", sender_str);
  702: 		}
  703: 	}
  704: 	else
  705: 	{
  706: 		syslog(LOG_NOTICE, "Unknown udp packet received from %s", sender_str);
  707: 	}
  708: }
  709: 
  710: /* This will broadcast ssdp:byebye notifications to inform
  711:  * the network that UPnP is going down. */
  712: int
  713: SendSSDPGoodbye(int * sockets, int n_sockets)
  714: {
  715: 	struct sockaddr_in sockname;
  716: #ifdef ENABLE_IPV6
  717: 	struct sockaddr_in6 sockname6;
  718: #endif
  719: 	int n, l;
  720: 	int i, j;
  721: 	char bufr[512];
  722: 	char ver_str[4];
  723: 	int ret = 0;
  724: 	int ipv6 = 0;
  725: 
  726:     memset(&sockname, 0, sizeof(struct sockaddr_in));
  727:     sockname.sin_family = AF_INET;
  728:     sockname.sin_port = htons(SSDP_PORT);
  729:     sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR);
  730: #ifdef ENABLE_IPV6
  731: 	memset(&sockname6, 0, sizeof(struct sockaddr_in6));
  732: 	sockname6.sin6_family = AF_INET6;
  733: 	sockname6.sin6_port = htons(SSDP_PORT);
  734: 	inet_pton(AF_INET6, LL_SSDP_MCAST_ADDR, &(sockname6.sin6_addr));
  735: #endif
  736: 
  737: 	for(j=0; j<n_sockets; j++)
  738: 	{
  739: #ifdef ENABLE_IPV6
  740: 		ipv6 = j & 1;
  741: #endif
  742: 	    for(i=0; known_service_types[i].s; i++)
  743: 	    {
  744: 			if(i==0)
  745: 				ver_str[0] = '\0';
  746: 			else
  747: 				snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
  748: 	        l = snprintf(bufr, sizeof(bufr),
  749:                  "NOTIFY * HTTP/1.1\r\n"
  750:                  "HOST: %s:%d\r\n"
  751:                  "NT: %s%s\r\n"
  752:                  "USN: %s::%s%s\r\n"
  753:                  "NTS: ssdp:byebye\r\n"
  754: 				 "OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n" /* UDA v1.1 */
  755: 				 "01-NLS: %u\r\n" /* same as BOOTID field. UDA v1.1 */
  756: 				 "BOOTID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
  757: 				 "CONFIGID.UPNP.ORG: %u\r\n" /* UDA v1.1 */
  758:                  "\r\n",
  759:                  ipv6 ? "[" LL_SSDP_MCAST_ADDR "]" : SSDP_MCAST_ADDR,
  760: 			     SSDP_PORT,
  761: 				 known_service_types[i].s, ver_str,
  762:                  uuidvalue, known_service_types[i].s, ver_str,
  763:                  upnp_bootid, upnp_bootid, upnp_configid);
  764: 	        n = sendto(sockets[j], bufr, l, 0,
  765: #ifdef ENABLE_IPV6
  766: 	                   ipv6 ? (struct sockaddr *)&sockname6 : (struct sockaddr *)&sockname,
  767: 	                   ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)
  768: #else
  769: 	                   (struct sockaddr *)&sockname,
  770: 	                   sizeof(struct sockaddr_in)
  771: #endif
  772: 	                 );
  773: 			if(n < 0)
  774: 			{
  775: 				syslog(LOG_ERR, "SendSSDPGoodbye: sendto(udp_shutdown=%d): %m",
  776: 				       sockets[j]);
  777: 				ret = -1;
  778: 			}
  779:     	}
  780: 	}
  781: 	return ret;
  782: }
  783: 
  784: /* SubmitServicesToMiniSSDPD() :
  785:  * register services offered by MiniUPnPd to a running instance of
  786:  * MiniSSDPd */
  787: int
  788: SubmitServicesToMiniSSDPD(const char * host, unsigned short port) {
  789: 	struct sockaddr_un addr;
  790: 	int s;
  791: 	unsigned char buffer[2048];
  792: 	char strbuf[256];
  793: 	unsigned char * p;
  794: 	int i, l, n;
  795: 	char ver_str[4];
  796: 
  797: 	s = socket(AF_UNIX, SOCK_STREAM, 0);
  798: 	if(s < 0) {
  799: 		syslog(LOG_ERR, "socket(unix): %m");
  800: 		return -1;
  801: 	}
  802: 	addr.sun_family = AF_UNIX;
  803: 	strncpy(addr.sun_path, minissdpdsocketpath, sizeof(addr.sun_path));
  804: 	if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
  805: 		syslog(LOG_ERR, "connect(\"%s\"): %m", minissdpdsocketpath);
  806: 		close(s);
  807: 		return -1;
  808: 	}
  809: 	for(i = 0; known_service_types[i].s; i++) {
  810: 		buffer[0] = 4;	/* request type 4 : submit service */
  811: 		/* 4 strings following : ST (service type), USN, Server, Location */
  812: 		p = buffer + 1;
  813: 		l = (int)strlen(known_service_types[i].s);
  814: 		if(i > 0)
  815: 			l++;
  816: 		CODELENGTH(l, p);
  817: 		memcpy(p, known_service_types[i].s, l);
  818: 		if(i > 0)
  819: 			p[l-1] = '1';
  820: 		p += l;
  821: 		if(i==0)
  822: 			ver_str[0] = '\0';
  823: 		else
  824: 			snprintf(ver_str, sizeof(ver_str), "%d", known_service_types[i].version);
  825: 		l = snprintf(strbuf, sizeof(strbuf), "%s::%s%s",
  826: 		             uuidvalue, known_service_types[i].s, ver_str);
  827: 		CODELENGTH(l, p);
  828: 		memcpy(p, strbuf, l);
  829: 		p += l;
  830: 		l = (int)strlen(MINIUPNPD_SERVER_STRING);
  831: 		CODELENGTH(l, p);
  832: 		memcpy(p, MINIUPNPD_SERVER_STRING, l);
  833: 		p += l;
  834: 		l = snprintf(strbuf, sizeof(strbuf), "http://%s:%u" ROOTDESC_PATH,
  835: 		             host, (unsigned int)port);
  836: 		CODELENGTH(l, p);
  837: 		memcpy(p, strbuf, l);
  838: 		p += l;
  839: 		/* now write the encoded data */
  840: 		n = p - buffer;	/* bytes to send */
  841: 		p = buffer;	/* start */
  842: 		while(n > 0) {
  843: 			l = write(s, p, n);
  844: 			if (l < 0) {
  845: 				syslog(LOG_ERR, "write(): %m");
  846: 				close(s);
  847: 				return -1;
  848: 			} else if (l == 0) {
  849: 				syslog(LOG_ERR, "write() returned 0");
  850: 				close(s);
  851: 				return -1;
  852: 			}
  853: 			p += l;
  854: 			n -= l;
  855: 		}
  856: 	}
  857:  	close(s);
  858: 	return 0;
  859: }
  860: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>