Beispiel #1
0
void
ParseUPnPClient(char *location)
{
	char buf[8192];
	struct sockaddr_in dest;
	int s, n, do_headers = 0, nread = 0;
	struct timeval tv;
	char *addr, *path, *port_str;
	long port = 80;
	char *off = NULL, *p;
	int content_len = sizeof(buf);
	struct NameValueParserData xml;
	int client;
	int type = 0;
	char *model, *serial, *name;

	if (strncmp(location, "http://", 7) != 0)
		return;
	path = location + 7;
	port_str = strsep(&path, "/");
	if (!path)
		return;
	addr = strsep(&port_str, ":");
	if (port_str)
	{
		port = strtol(port_str, NULL, 10);
		if (!port)
			port = 80;
	}

	memset(&dest, '\0', sizeof(dest));
	if (!inet_aton(addr, &dest.sin_addr))
		return;
	/* Check if the client is already in cache */
	dest.sin_family = AF_INET;
	dest.sin_port = htons(port);

	s = socket(PF_INET, SOCK_STREAM, 0);
	if (s < 0)
		return;

	tv.tv_sec = 0;
	tv.tv_usec = 500000;
	setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
	setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));

	if (connect(s, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0)
		goto close;

	n = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n"
	                               "HOST: %s:%ld\r\n\r\n",
	                               path, addr, port);
	if (write(s, buf, n) < 1)
		goto close;

	while ((n = read(s, buf+nread, sizeof(buf)-nread-1)) > 0)
	{
		nread += n;
		buf[nread] = '\0';
		n = nread;
		p = buf;

		while (!off && (n-- > 0))
		{
			if (p[0] == '\r' && p[1] == '\n' && p[2] == '\r' && p[3] == '\n')
			{
				off = p + 4;
				do_headers = 1;
			}
			p++;
		}
		if (!off)
			continue;

		if (do_headers)
		{
			p = buf;
			if (strncmp(p, "HTTP/", 5) != 0)
				goto close;
			while (*p != ' ' && *p != '\t')
				p++;
			/* If we don't get a 200 status, ignore it */
			if (strtol(p, NULL, 10) != 200)
				goto close;
			p = strcasestr(p, "Content-Length:");
			if (p)
				content_len = strtol(p+15, NULL, 10);
			do_headers = 0;
		}
		if ((buf + nread - off) >= content_len)
			break;
	}
close:
	close(s);
	if (!off)
		return;
	nread -= off - buf;
	ParseNameValue(off, nread, &xml, 0);
	model = GetValueFromNameValueList(&xml, "modelName");
	serial = GetValueFromNameValueList(&xml, "serialNumber");
	name = GetValueFromNameValueList(&xml, "friendlyName");
	if (model)
	{
		int i;
		DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model);
		for (i = 0; client_types[i].name; i++)
		{
			if (client_types[i].match_type != EModelName)
				continue;
			if (strstr(model, client_types[i].match) != NULL)
			{
				type = i;
				break;
			}
		}

		/* Special Samsung handling.  It's very hard to tell Series A from B */
		if (type > 0 && client_types[type].type == ESamsungSeriesB)
		{
			if (serial)
			{
				DPRINTF(E_DEBUG, L_SSDP, "Serial: %s\n", serial);
				/* The Series B I saw was 20081224DMR.  Series A should be older than that. */
				if (atoi(serial) < 20081201)
					type = 0;
			}
			else
			{
				type = 0;
			}
		}

		if (type == 0 && name != NULL)
		{
			for (i = 0; client_types[i].name; i++)
			{
				if (client_types[i].match_type != EFriendlyNameSSDP)
					continue;
				if (strcmp(name, client_types[i].match) == 0)
				{
					type = i;
					break;
				}
			}
		}
	}
	ClearNameValueList(&xml);
	if (!type)
		return;
	/* Add this client to the cache if it's not there already. */
	client = SearchClientCache(dest.sin_addr, 1);
	if (client < 0)
	{
		AddClientCache(dest.sin_addr, type);
	}
	else
	{
		clients[client].type = type;
		clients[client].age = time(NULL);
	}
}
Beispiel #2
0
/* ProcessSSDPRequest()
 * process SSDP M-SEARCH requests and responds to them */
void
ProcessSSDPRequest(int s, unsigned short port)
{
	int n;
	char bufr[1500];
	socklen_t len_r;
	struct sockaddr_in sendername;
	int i;
	char *st = NULL, *mx = NULL, *man = NULL, *mx_end = NULL;
	int man_len = 0;
	len_r = sizeof(struct sockaddr_in);

	n = recvfrom(s, bufr, sizeof(bufr)-1, 0,
	             (struct sockaddr *)&sendername, &len_r);
	if (n < 0)
	{
		DPRINTF(E_ERROR, L_SSDP, "recvfrom(udp): %s\n", strerror(errno));
		return;
	}
	bufr[n] = '\0';
	n -= 2;

	if (memcmp(bufr, "NOTIFY", 6) == 0)
	{
		char *loc = NULL, *srv = NULL, *nts = NULL, *nt = NULL;
		int loc_len = 0;
		//DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr);
		for (i = 0; i < n; i++)
		{
			if( bufr[i] == '*' )
				break;
		}
		if (strcasestrc(bufr+i, "HTTP/1.1", '\r') == NULL)
			return;
		while (i < n)
		{
			while ((i < n) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
				i++;
			i += 2;
			if (strncasecmp(bufr+i, "SERVER:", 7) == 0)
			{
				srv = bufr+i+7;
				while (*srv == ' ' || *srv == '\t')
					srv++;
			}
			else if (strncasecmp(bufr+i, "LOCATION:", 9) == 0)
			{
				loc = bufr+i+9;
				while (*loc == ' ' || *loc == '\t')
					loc++;
				while (loc[loc_len]!='\r' && loc[loc_len]!='\n')
					loc_len++;
			}
			else if (strncasecmp(bufr+i, "NTS:", 4) == 0)
			{
				nts = bufr+i+4;
				while (*nts == ' ' || *nts == '\t')
					nts++;
			}
			else if (strncasecmp(bufr+i, "NT:", 3) == 0)
			{
				nt = bufr+i+3;
				while(*nt == ' ' || *nt == '\t')
					nt++;
			}
		}
		if (!loc || !srv || !nt || !nts || (strncmp(nts, "ssdp:alive", 10) != 0) ||
		    (strncmp(nt, "urn:schemas-upnp-org:device:MediaRenderer", 41) != 0))
			return;
		loc[loc_len] = '\0';
		if ((strncmp(srv, "Allegro-Software-RomPlug", 24) == 0) || /* Roku */
		    (strstr(loc, "SamsungMRDesc.xml") != NULL) || /* Samsung TV */
		    (strstrc(srv, "DigiOn DiXiM", '\r') != NULL)) /* Marantz Receiver */
		{
			/* Check if the client is already in cache */
			i = SearchClientCache(sendername.sin_addr, 1);
			if (i >= 0)
			{
				if (clients[i].type < EStandardDLNA150 &&
				    clients[i].type != ESamsungSeriesA)
				{
					clients[i].age = time(NULL);
					return;
				}
			}
			ParseUPnPClient(loc);
		}
	}
	else if (memcmp(bufr, "M-SEARCH", 8) == 0)
	{
		int st_len = 0, mx_len = 0, mx_val = 0;
		//DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s\n", n, bufr);
		for (i = 0; i < n; i++)
		{
			if (bufr[i] == '*')
				break;
		}
		if (strcasestrc(bufr+i, "HTTP/1.1", '\r') == NULL)
			return;
		while (i < n)
		{
			while ((i < n) && (bufr[i] != '\r' || bufr[i+1] != '\n'))
				i++;
			i += 2;
			if (strncasecmp(bufr+i, "ST:", 3) == 0)
			{
				st = bufr+i+3;
				st_len = 0;
				while (*st == ' ' || *st == '\t')
					st++;
				while (st[st_len]!='\r' && st[st_len]!='\n')
					st_len++;
			}
			else if (strncasecmp(bufr+i, "MX:", 3) == 0)
			{
				mx = bufr+i+3;
				mx_len = 0;
				while (*mx == ' ' || *mx == '\t')
					mx++;
				while (mx[mx_len]!='\r' && mx[mx_len]!='\n')
					mx_len++;
        			mx_val = strtol(mx, &mx_end, 10);
			}
			else if (strncasecmp(bufr+i, "MAN:", 4) == 0)
			{
				man = bufr+i+4;
				man_len = 0;
				while (*man == ' ' || *man == '\t')
					man++;
				while (man[man_len]!='\r' && man[man_len]!='\n')
					man_len++;
			}
		}
		/*DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH packet received from %s:%d\n",
	           inet_ntoa(sendername.sin_addr),
	           ntohs(sendername.sin_port) );*/
		if (GETFLAG(DLNA_STRICT_MASK) && (ntohs(sendername.sin_port) <= 1024 || ntohs(sendername.sin_port) == 1900))
		{
			DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad source port %d]\n",
				inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port));
		}
		else if (!man || (strncmp(man, "\"ssdp:discover\"", 15) != 0))
		{
			DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
				inet_ntoa(sendername.sin_addr), "MAN", man_len, man);
		}
		else if (!mx || mx == mx_end || mx_val < 0)
		{
			DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n",
				inet_ntoa(sendername.sin_addr), "MX", mx_len, mx);
		}
		else if (st && (st_len > 0))
		{
			int l;
			int lan_addr_index = 0;
			/* find in which sub network the client is */
			for (i = 0; i < n_lan_addr; i++)
			{
				if((sendername.sin_addr.s_addr & lan_addr[i].mask.s_addr) ==
				   (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
				{
					lan_addr_index = i;
					break;
				}
			}
			if (n_lan_addr == i)
			{
				DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH on other interface [%s]\n",
					inet_ntoa(sendername.sin_addr));
				return;
			}
			DPRINTF(E_INFO, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n",
				inet_ntoa(sendername.sin_addr),
				ntohs(sendername.sin_port),
				st_len, st, mx_len, mx, man_len, man);
			/* Responds to request with a device as ST header */
			for (i = 0; known_service_types[i]; i++)
			{
				l = strlen(known_service_types[i]);
				if ((l <= st_len) && (memcmp(st, known_service_types[i], l) == 0))
				{
					if (st_len != l)
					{
						/* Check version number - must always be 1 currently. */
						if ((st[l-1] == ':') && (st[l] == '1'))
							l++;
						while (l < st_len)
						{
							if (!isspace(st[l]))
							{
								DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH with bad extra data [%s]\n",
									inet_ntoa(sendername.sin_addr));
								break;
							}
							l++;
						}
						if (l != st_len)
							break;
					}
					_usleep(random()>>20);
					SendSSDPAnnounce2(s, sendername, i,
					                  lan_addr[lan_addr_index].str, port);
					break;
				}
			}
			/* Responds to request with ST: ssdp:all */
			/* strlen("ssdp:all") == 8 */
			if ((st_len == 8) && (memcmp(st, "ssdp:all", 8) == 0))
			{
				for (i=0; known_service_types[i]; i++)
				{
					l = strlen(known_service_types[i]);
					SendSSDPAnnounce2(s, sendername, i,
					                  lan_addr[lan_addr_index].str, port);
				}
			}
		}
Beispiel #3
0
void
ParseUPnPClient(char *location)
{
	char buf[8192];
	struct sockaddr_in dest;
	int s, n, do_headers = 0, nread = 0;
	struct timeval tv;
	char *addr, *path, *port_str;
	long port = 80;
	char *off = NULL, *p;
	int content_len = sizeof(buf);
	struct NameValueParserData xml;
	int client;
	enum client_types type = 0;
	uint32_t flags = 0;
	char *model, *serial, *name;

	if (strncmp(location, "http://", 7) != 0)
		return;
	path = location + 7;
	port_str = strsep(&path, "/");
	if (!path)
		return;
	addr = strsep(&port_str, ":");
	if (port_str)
	{
		port = strtol(port_str, NULL, 10);
		if (!port)
			port = 80;
	}

	memset(&dest, '\0', sizeof(dest));
	if (!inet_aton(addr, &dest.sin_addr))
		return;
	/* Check if the client is already in cache */
	dest.sin_family = AF_INET;
	dest.sin_port = htons(port);

	s = socket(PF_INET, SOCK_STREAM, 0);
	if( s < 0 )
		return;

	tv.tv_sec = 0;
	tv.tv_usec = 500000;
	setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
	setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));

	if( connect(s, (struct sockaddr*)&dest, sizeof(struct sockaddr_in)) < 0 )
		goto close;

	n = snprintf(buf, sizeof(buf), "GET /%s HTTP/1.0\r\n"
	                               "HOST: %s:%ld\r\n\r\n",
	                               path, addr, port);
	if( write(s, buf, n) < 1 )
		goto close;

	while( (n = read(s, buf+nread, sizeof(buf)-nread-1)) > 0 )
	{
		nread += n;
		buf[nread] = '\0';
		n = nread;
		p = buf;

		while( !off && n-- > 0 )
		{
			if(p[0]=='\r' && p[1]=='\n' && p[2]=='\r' && p[3]=='\n')
			{
				off = p + 4;
				do_headers = 1;
			}
			p++;
		}
		if( !off )
			continue;

		if( do_headers )
		{
			p = buf;
			if( strncmp(p, "HTTP/", 5) != 0 )
				goto close;
			while(*p != ' ' && *p != '\t') p++;
			/* If we don't get a 200 status, ignore it */
			if( strtol(p, NULL, 10) != 200 )
				goto close;
			if( (p = strcasestr(p, "Content-Length:")) )
				content_len = strtol(p+15, NULL, 10);
			do_headers = 0;
		}
		if( buf + nread - off >= content_len )
			break;
	}
close:
	close(s);
	if( !off )
		return;
	nread -= off - buf;
	ParseNameValue(off, nread, &xml);
	model = GetValueFromNameValueList(&xml, "modelName");
	serial = GetValueFromNameValueList(&xml, "serialNumber");
	name = GetValueFromNameValueList(&xml, "friendlyName");
	if( model )
	{
		DPRINTF(E_DEBUG, L_SSDP, "Model: %s\n", model);
		if( strstr(model, "Roku SoundBridge") )
		{
			type = ERokuSoundBridge;
			flags |= FLAG_MS_PFS;
			flags |= FLAG_AUDIO_ONLY;
			flags |= FLAG_MIME_WAV_WAV;
		}
		else if( strcmp(model, "Samsung DTV DMR") == 0 && serial )
		{
			DPRINTF(E_DEBUG, L_SSDP, "Serial: %s\n", serial);
			/* The Series B I saw was 20081224DMR.  Series A should be older than that. */
			if( atoi(serial) > 20081200 )
			{
				type = ESamsungSeriesB;
				flags |= FLAG_SAMSUNG;
				flags |= FLAG_DLNA;
				flags |= FLAG_NO_RESIZE;
			}
		}
		else
		{
			if( name && (strcmp(name, "marantz DMP") == 0) )
			{
				type = EMarantzDMP;
				flags |= FLAG_DLNA;
				flags |= FLAG_MIME_WAV_WAV;
			}
		}
	}
	ClearNameValueList(&xml);
	if( !type )
		return;
	/* Add this client to the cache if it's not there already. */
	client = SearchClientCache(dest.sin_addr, 1);
	if( client < 0 )
	{
		for( client=0; client<CLIENT_CACHE_SLOTS; client++ )
		{
			if( clients[client].addr.s_addr )
				continue;
			get_remote_mac(dest.sin_addr, clients[client].mac);
			clients[client].addr = dest.sin_addr;
			DPRINTF(E_DEBUG, L_SSDP, "Added client [%d/%s/%02X:%02X:%02X:%02X:%02X:%02X] to cache slot %d.\n",
			                         type, inet_ntoa(clients[client].addr),
			                         clients[client].mac[0], clients[client].mac[1], clients[client].mac[2],
			                         clients[client].mac[3], clients[client].mac[4], clients[client].mac[5], client);
			break;
		}
	}
	clients[client].type = type;
	clients[client].flags = flags;
	clients[client].age = time(NULL);
}