blob: 1c9955336400ddb155af085b7b3a92e8c7f69124 [file] [log] [blame]
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* ssdptax (SSDP Taxonomy)
*
* A client implementing the API described in
* http://miniupnp.free.fr/minissdpd.html
*
* Requests the list of all known SSDP nodes, requests
* device info from them, and tries to figure out what
* they are.
*/
#include <arpa/inet.h>
#include <asm/types.h>
#include <ctype.h>
#include <curl/curl.h>
#include <getopt.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include "l2utils.h"
/* Encode length by using 7bit per Byte :
* Most significant bit of each byte specifies that the
* following byte is part of the code */
#define DECODELENGTH(n, p) { \
n = 0; \
do { n = (n << 7) | (*p & 0x7f); } \
while (*(p++)&0x80); \
}
#define CODELENGTH(n, p) { \
if(n>=0x10000000) *(p++) = (n >> 28) | 0x80; \
if(n>=0x200000) *(p++) = (n >> 21) | 0x80; \
if(n>=0x4000) *(p++) = (n >> 14) | 0x80; \
if(n>=0x80) *(p++) = (n >> 7) | 0x80; \
*(p++) = n & 0x7f; \
}
#define SOCK_PATH "/var/run/minissdpd.sock"
typedef struct {
char server[512];
char url[512];
char friendlyName[64];
int failed;
} ssdp_info_t;
/* Unit test support */
char *get_test_ssdp_data();
void get_test_l2_map(L2Map *l2map);
static void memcpy_printable(char *dst, const char *src, size_t n)
{
size_t i;
for (i = 0; i < n; ++i) {
unsigned char s = src[i];
if (isspace(s)) {
dst[i] = ' '; // deliberately convert newline to space
} else if (isprint(s)) {
dst[i] = s;
} else {
dst[i] = '_';
}
}
}
/*
* Send a request to minissdpd. Returns a pointer to a buffer
* allocated using malloc(). Caller must free() the buffer when done.
*/
char *request_from_ssdpd(int reqtype, const char *device)
{
int s = socket(AF_UNIX, SOCK_STREAM, 0);
struct sockaddr_un addr;
size_t siz = 256 * 1024;
char *buffer, *p;
ssize_t len;
int device_len = (int)strlen(device);
fd_set readfds;
struct timeval tv;
if (s < 0) {
perror("socket AF_UNIX failed");
exit(1);
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path));
if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
perror("connect to minisspd failed");
exit(1);
}
if ((buffer = (char *)malloc(siz)) == NULL) {
fprintf(stderr, "malloc(%zu) failed\n", siz);
exit(1);
}
memset(buffer, 0, siz);
buffer[0] = reqtype;
p = buffer + 1;
CODELENGTH(device_len, p);
memcpy(p, device, device_len);
p += device_len;
if (write(s, buffer, p - buffer) < 0) {
perror("write to minissdpd failed");
exit(1);
}
FD_ZERO(&readfds);
FD_SET(s, &readfds);
memset(&tv, 0, sizeof(tv));
tv.tv_sec = 2;
if (select(s + 1, &readfds, NULL, NULL, &tv) < 1) {
fprintf(stderr, "select failed\n");
exit(1);
}
if ((len = read(s, buffer, siz)) < 0) {
perror("read from minissdpd failed");
exit(1);
}
close(s);
return(buffer);
}
static void print_responses(const std::string &ipaddr,
const ssdp_info_t *info, L2Map *l2map)
{
const char *mac;
if (info->failed) {
/*
* We could not fetch information from this client. That often means that
* the device was powered off recently. minissdpd still remembers that
* it is there, but we cannot contact it.
*
* Don't print anything for these, as we'd end up calling them "Unknown"
* and that is misleading. We only report information about devices which
* are active right now.
*/
return;
}
L2Map::const_iterator ii = l2map->find(ipaddr);
if (ii != l2map->end()) {
mac = ii->second.c_str();
} else {
mac = "00:00:00:00:00:00";
}
/* taxonomy information to stdout */
if (strlen(info->friendlyName)) {
printf("ssdp %s %s\n", mac, info->friendlyName);
} else {
printf("ssdp %s Unknown;%s\n", mac, info->server);
}
}
const char *parse_minissdpd_response(const char *response,
char *key, size_t key_len,
char *url, size_t url_len,
char *value, size_t value_len)
{
const char *p = response;
size_t copylen, slen;
int prefix = 0;
struct in6_addr in6;
struct in_addr in;
char ip[INET6_ADDRSTRLEN];
key[0] = url[0] = value[0] = '\0';
DECODELENGTH(slen, p);
copylen = (slen >= url_len) ? url_len - 1 : slen;
memcpy_printable(url, p, copylen);
url[copylen] = '\0';
p += slen;
DECODELENGTH(slen, p);
copylen = (slen >= value_len) ? value_len - 1 : slen;
memcpy_printable(value, p, copylen);
value[copylen] = '\0';
p += slen;
if (strncasecmp(url, "https://[", 9) == 0) prefix = 9;
if (strncasecmp(url, "http://[", 8) == 0) prefix = 8;
if (strncasecmp(url, "https://", 8) == 0) prefix = 8;
if (strncasecmp(url, "http://", 7) == 0) prefix = 7;
strncpy(ip, url + prefix, sizeof(ip));
strtok(ip, ":/@");
if (inet_pton(AF_INET6, ip, &in6)) {
inet_ntop(AF_INET6, &in6, key, key_len);
}
if (inet_pton(AF_INET, ip, &in)) {
inet_ntop(AF_INET, &in, key, key_len);
}
return p;
}
ssdp_info_t *dupinfo(ssdp_info_t *info)
{
ssdp_info_t *i = (ssdp_info_t *)malloc(sizeof(*info));
if (i == NULL) {
perror("malloc");
exit(1);
}
memcpy(i, info, sizeof(*i));
return i;
}
const char *findXmlField(const char *ptr, const char *label, ssize_t *len)
{
char openlabel[64], closelabel[64];
const char *start, *end;
snprintf(openlabel, sizeof(openlabel), "<%s>", label);
snprintf(closelabel, sizeof(closelabel), "</%s>", label);
start = strcasestr(ptr, openlabel) + strlen(openlabel);
end = strcasestr(ptr, closelabel);
if ((end - start) > 0) {
*len = end - start;
return start;
}
return NULL;
}
/*
* libcurl calls this function back with the result of the HTTP GET.
*
* Expected value is an XML blob of
* http://upnp.org/specs/basic/UPnP-basic-Basic-v1-Device.pdf
*
* Like this (a Samsung TV):
* <?xml version="1.0"?>
* <root xmlns='urn:schemas-upnp-org:device-1-0' ...
* <device>
* <deviceType>urn:dial-multiscreen-org:device:dialreceiver:1</deviceType>
* <friendlyName>[TV]Samsung LED60</friendlyName>
* <manufacturer>Samsung Electronics</manufacturer>
* <manufacturerURL>http://www.samsung.com/sec</manufacturerURL>
* <modelDescription>Samsung TV NS</modelDescription>
* <modelName>UN60F6300</modelName>
* <modelNumber>1.0</modelNumber>
* <modelURL>http://www.samsung.com/sec</modelURL>
* ... etc, etc ...
*/
size_t callback(const char *ptr, size_t size, size_t nmemb, void *userdata)
{
ssdp_info_t *info = (ssdp_info_t *)userdata;
const char *p;
ssize_t len;
ssize_t max = (ssize_t)sizeof(info->friendlyName);
if ((p = findXmlField(ptr, "friendlyName", &len)) == NULL) {
p = findXmlField(ptr, "modelDescription", &len);
}
if (p && (len > 0) && (len < max)) {
/* the len < max check ensures there will be a NUL byte at the end */
memcpy(info->friendlyName, p, len);
}
return size * nmemb;
}
/*
* SSDP returned an endpoint URL, use curl to GET its contents.
*/
void fetch_device_info(const char *url, ssdp_info_t *ssdp)
{
CURL *curl = curl_easy_init();
int rc;
if (!curl) {
fprintf(stderr, "curl_easy_init failed\n");
return;
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_PATH_AS_IS, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ssdp);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "ssdptax/1.0");
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L);
if ((rc = curl_easy_perform(curl)) != CURLE_OK) {
ssdp->failed = 1;
}
curl_easy_cleanup(curl);
}
void usage(char *progname) {
printf("usage: %s [-t]\n", progname);
printf("\t-t\ttest mode, run a test with fake SSDP data.\n");
exit(1);
}
int main(int argc, char **argv)
{
char *buffer;
const char *p;
typedef std::tr1::unordered_map<std::string, ssdp_info_t*> ResponsesMap;
ResponsesMap responses;
L2Map l2map;
int c, num;
int testmode = 0;
setlinebuf(stdout);
alarm(30);
if (curl_global_init(CURL_GLOBAL_NOTHING)) {
fprintf(stderr, "curl_global_init failed\n");
exit(1);
}
while ((c = getopt(argc, argv, "t")) != -1) {
switch(c) {
case 't': testmode = 1; break;
default: usage(argv[0]); break;
}
}
if (!testmode) {
/* 5 == request all device server IDs */
buffer = request_from_ssdpd(5, "ssdp:all");
} else {
buffer = get_test_ssdp_data();
}
num = buffer[0];
p = buffer + 1;
while ((num-- > 0) && (p < (buffer + sizeof(buffer)))) {
char key[INET6_ADDRSTRLEN];
ssdp_info_t info;
memset(&info, 0, sizeof(info));
p = parse_minissdpd_response(p, key, sizeof(key),
info.url, sizeof(info.url), info.server, sizeof(info.server));
if (strlen(key) && responses.find(std::string(key)) == responses.end()) {
if (!testmode) {
fetch_device_info(info.url, &info);
} else {
snprintf(info.friendlyName, sizeof(info.friendlyName), "Test Device");
}
responses.insert(std::make_pair<std::string,
ssdp_info_t*>(std::string(key), dupinfo(&info)));
}
}
free(buffer);
if (!testmode) {
get_l2_map(&l2map);
} else {
get_test_l2_map(&l2map);
}
for(ResponsesMap::const_iterator ii = responses.begin();
ii != responses.end(); ++ii) {
print_responses(ii->first, ii->second, &l2map);
}
curl_global_cleanup();
exit(0);
}
/*
* data for a unit test, response from a single SSDP
* client.
*/
uint8_t test_ssdp_data[] = {
0x12, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
0x2f, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38,
0x2e, 0x34, 0x32, 0x2e, 0x31, 0x32, 0x35, 0x3a,
0x37, 0x36, 0x37, 0x36, 0x2f, 0x73, 0x6d, 0x70,
0x5f, 0x32, 0x39, 0x5f, 0x23, 0x53, 0x48, 0x50,
0x2c, 0x20, 0x55, 0x50, 0x6e, 0x50, 0x2f, 0x31,
0x2e, 0x30, 0x2c, 0x20, 0x53, 0x61, 0x6d, 0x73,
0x75, 0x6e, 0x67, 0x20, 0x55, 0x50, 0x6e, 0x50,
0x20, 0x53, 0x44, 0x4b, 0x2f, 0x31, 0x2e, 0x30,
0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e,
0x34, 0x32, 0x2e, 0x31, 0x32, 0x35, 0x3a, 0x37,
0x36, 0x37, 0x36, 0x2f, 0x73, 0x6d, 0x70, 0x5f,
0x32, 0x39, 0x5f, 0x23, 0x53, 0x48, 0x50, 0x2c,
0x20, 0x55, 0x50, 0x6e, 0x50, 0x2f, 0x31, 0x2e,
0x30, 0x2c, 0x20, 0x53, 0x61, 0x6d, 0x73, 0x75,
0x6e, 0x67, 0x20, 0x55, 0x50, 0x6e, 0x50, 0x20,
0x53, 0x44, 0x4b, 0x2f, 0x31, 0x2e, 0x30, 0x22,
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31,
0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x34,
0x32, 0x2e, 0x31, 0x32, 0x35, 0x3a, 0x37, 0x36,
0x37, 0x36, 0x2f, 0x73, 0x6d, 0x70, 0x5f, 0x32,
0x39, 0x5f, 0x23, 0x53, 0x48, 0x50, 0x2c, 0x20,
0x55, 0x50, 0x6e, 0x50, 0x2f, 0x31, 0x2e, 0x30,
0x2c, 0x20, 0x53, 0x61, 0x6d, 0x73, 0x75, 0x6e,
0x67, 0x20, 0x55, 0x50, 0x6e, 0x50, 0x20, 0x53,
0x44, 0x4b, 0x2f, 0x31, 0x2e, 0x30, 0x22, 0x68,
0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x39,
0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x34, 0x32,
0x2e, 0x31, 0x32, 0x35, 0x3a, 0x37, 0x36, 0x37,
0x36, 0x2f, 0x73, 0x6d, 0x70, 0x5f, 0x32, 0x39,
0x5f, 0x23, 0x53, 0x48, 0x50, 0x2c, 0x20, 0x55,
0x50, 0x6e, 0x50, 0x2f, 0x31, 0x2e, 0x30, 0x2c,
0x20, 0x53, 0x61, 0x6d, 0x73, 0x75, 0x6e, 0x67,
0x20, 0x55, 0x50, 0x6e, 0x50, 0x20, 0x53, 0x44,
0x4b, 0x2f, 0x31, 0x2e, 0x30, 0x22, 0x68, 0x74,
0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
0x2e, 0x31, 0x36, 0x38, 0x2e, 0x34, 0x32, 0x2e,
0x31, 0x32, 0x35, 0x3a, 0x37, 0x36, 0x37, 0x36,
0x2f, 0x73, 0x6d, 0x70, 0x5f, 0x31, 0x39, 0x5f,
0x23, 0x53, 0x48, 0x50, 0x2c, 0x20, 0x55, 0x50,
0x6e, 0x50, 0x2f, 0x31, 0x2e, 0x30, 0x2c, 0x20,
0x53, 0x61, 0x6d, 0x73, 0x75, 0x6e, 0x67, 0x20,
0x55, 0x50, 0x6e, 0x50, 0x20, 0x53, 0x44, 0x4b,
0x2f, 0x31, 0x2e, 0x30, 0x22, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e,
0x31, 0x36, 0x38, 0x2e, 0x34, 0x32, 0x2e, 0x31,
0x32, 0x35, 0x3a, 0x37, 0x36, 0x37, 0x36, 0x2f,
0x73, 0x6d, 0x70, 0x5f, 0x31, 0x39, 0x5f, 0x23,
0x53, 0x48, 0x50, 0x2c, 0x20, 0x55, 0x50, 0x6e,
0x50, 0x2f, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x53,
0x61, 0x6d, 0x73, 0x75, 0x6e, 0x67, 0x20, 0x55,
0x50, 0x6e, 0x50, 0x20, 0x53, 0x44, 0x4b, 0x2f,
0x31, 0x2e, 0x30, 0x22, 0x68, 0x74, 0x74, 0x70,
0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x31,
0x36, 0x38, 0x2e, 0x34, 0x32, 0x2e, 0x31, 0x32,
0x35, 0x3a, 0x37, 0x36, 0x37, 0x36, 0x2f, 0x73,
0x6d, 0x70, 0x5f, 0x31, 0x39, 0x5f, 0x23, 0x53,
0x48, 0x50, 0x2c, 0x20, 0x55, 0x50, 0x6e, 0x50,
0x2f, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x53, 0x61,
0x6d, 0x73, 0x75, 0x6e, 0x67, 0x20, 0x55, 0x50,
0x6e, 0x50, 0x20, 0x53, 0x44, 0x4b, 0x2f, 0x31,
0x2e, 0x30, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a,
0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36,
0x38, 0x2e, 0x34, 0x32, 0x2e, 0x31, 0x32, 0x35,
0x3a, 0x37, 0x36, 0x37, 0x36, 0x2f, 0x73, 0x6d,
0x70, 0x5f, 0x31, 0x39, 0x5f, 0x23, 0x53, 0x48,
0x50, 0x2c, 0x20, 0x55, 0x50, 0x6e, 0x50, 0x2f,
0x31, 0x2e, 0x30, 0x2c, 0x20, 0x53, 0x61, 0x6d,
0x73, 0x75, 0x6e, 0x67, 0x20, 0x55, 0x50, 0x6e,
0x50, 0x20, 0x53, 0x44, 0x4b, 0x2f, 0x31, 0x2e,
0x30, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
0x2f, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38,
0x2e, 0x34, 0x32, 0x2e, 0x31, 0x32, 0x35, 0x3a,
0x37, 0x36, 0x37, 0x36, 0x2f, 0x73, 0x6d, 0x70,
0x5f, 0x31, 0x39, 0x5f, 0x23, 0x53, 0x48, 0x50,
0x2c, 0x20, 0x55, 0x50, 0x6e, 0x50, 0x2f, 0x31,
0x2e, 0x30, 0x2c, 0x20, 0x53, 0x61, 0x6d, 0x73,
0x75, 0x6e, 0x67, 0x20, 0x55, 0x50, 0x6e, 0x50,
0x20, 0x53, 0x44, 0x4b, 0x2f, 0x31, 0x2e, 0x30,
0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f,
0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e,
0x34, 0x32, 0x2e, 0x31, 0x32, 0x35, 0x3a, 0x37,
0x36, 0x37, 0x36, 0x2f, 0x73, 0x6d, 0x70, 0x5f,
0x31, 0x39, 0x5f, 0x23, 0x53, 0x48, 0x50, 0x2c,
0x20, 0x55, 0x50, 0x6e, 0x50, 0x2f, 0x31, 0x2e,
0x30, 0x2c, 0x20, 0x53, 0x61, 0x6d, 0x73, 0x75,
0x6e, 0x67, 0x20, 0x55, 0x50, 0x6e, 0x50, 0x20,
0x53, 0x44, 0x4b, 0x2f, 0x31, 0x2e, 0x30, 0x22,
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31,
0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x34,
0x32, 0x2e, 0x31, 0x32, 0x35, 0x3a, 0x37, 0x36,
0x37, 0x36, 0x2f, 0x73, 0x6d, 0x70, 0x5f, 0x31,
0x31, 0x5f, 0x23, 0x53, 0x48, 0x50, 0x2c, 0x20,
0x55, 0x50, 0x6e, 0x50, 0x2f, 0x31, 0x2e, 0x30,
0x2c, 0x20, 0x53, 0x61, 0x6d, 0x73, 0x75, 0x6e,
0x67, 0x20, 0x55, 0x50, 0x6e, 0x50, 0x20, 0x53,
0x44, 0x4b, 0x2f, 0x31, 0x2e, 0x30, 0x22, 0x68,
0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x39,
0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x34, 0x32,
0x2e, 0x31, 0x32, 0x35, 0x3a, 0x37, 0x36, 0x37,
0x36, 0x2f, 0x73, 0x6d, 0x70, 0x5f, 0x31, 0x31,
0x5f, 0x23, 0x53, 0x48, 0x50, 0x2c, 0x20, 0x55,
0x50, 0x6e, 0x50, 0x2f, 0x31, 0x2e, 0x30, 0x2c,
0x20, 0x53, 0x61, 0x6d, 0x73, 0x75, 0x6e, 0x67,
0x20, 0x55, 0x50, 0x6e, 0x50, 0x20, 0x53, 0x44,
0x4b, 0x2f, 0x31, 0x2e, 0x30, 0x22, 0x68, 0x74,
0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
0x2e, 0x31, 0x36, 0x38, 0x2e, 0x34, 0x32, 0x2e,
0x31, 0x32, 0x35, 0x3a, 0x37, 0x36, 0x37, 0x36,
0x2f, 0x73, 0x6d, 0x70, 0x5f, 0x31, 0x31, 0x5f,
0x23, 0x53, 0x48, 0x50, 0x2c, 0x20, 0x55, 0x50,
0x6e, 0x50, 0x2f, 0x31, 0x2e, 0x30, 0x2c, 0x20,
0x53, 0x61, 0x6d, 0x73, 0x75, 0x6e, 0x67, 0x20,
0x55, 0x50, 0x6e, 0x50, 0x20, 0x53, 0x44, 0x4b,
0x2f, 0x31, 0x2e, 0x30, 0x22, 0x68, 0x74, 0x74,
0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e,
0x31, 0x36, 0x38, 0x2e, 0x34, 0x32, 0x2e, 0x31,
0x32, 0x35, 0x3a, 0x37, 0x36, 0x37, 0x36, 0x2f,
0x73, 0x6d, 0x70, 0x5f, 0x31, 0x31, 0x5f, 0x23,
0x53, 0x48, 0x50, 0x2c, 0x20, 0x55, 0x50, 0x6e,
0x50, 0x2f, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x53,
0x61, 0x6d, 0x73, 0x75, 0x6e, 0x67, 0x20, 0x55,
0x50, 0x6e, 0x50, 0x20, 0x53, 0x44, 0x4b, 0x2f,
0x31, 0x2e, 0x30, 0x21, 0x68, 0x74, 0x74, 0x70,
0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32, 0x2e, 0x31,
0x36, 0x38, 0x2e, 0x34, 0x32, 0x2e, 0x31, 0x32,
0x35, 0x3a, 0x37, 0x36, 0x37, 0x36, 0x2f, 0x73,
0x6d, 0x70, 0x5f, 0x32, 0x5f, 0x23, 0x53, 0x48,
0x50, 0x2c, 0x20, 0x55, 0x50, 0x6e, 0x50, 0x2f,
0x31, 0x2e, 0x30, 0x2c, 0x20, 0x53, 0x61, 0x6d,
0x73, 0x75, 0x6e, 0x67, 0x20, 0x55, 0x50, 0x6e,
0x50, 0x20, 0x53, 0x44, 0x4b, 0x2f, 0x31, 0x2e,
0x30, 0x21, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f,
0x2f, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38,
0x2e, 0x34, 0x32, 0x2e, 0x31, 0x32, 0x35, 0x3a,
0x37, 0x36, 0x37, 0x36, 0x2f, 0x73, 0x6d, 0x70,
0x5f, 0x32, 0x5f, 0x23, 0x53, 0x48, 0x50, 0x2c,
0x20, 0x55, 0x50, 0x6e, 0x50, 0x2f, 0x31, 0x2e,
0x30, 0x2c, 0x20, 0x53, 0x61, 0x6d, 0x73, 0x75,
0x6e, 0x67, 0x20, 0x55, 0x50, 0x6e, 0x50, 0x20,
0x53, 0x44, 0x4b, 0x2f, 0x31, 0x2e, 0x30, 0x21,
0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31,
0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x34,
0x32, 0x2e, 0x31, 0x32, 0x35, 0x3a, 0x37, 0x36,
0x37, 0x36, 0x2f, 0x73, 0x6d, 0x70, 0x5f, 0x32,
0x5f, 0x23, 0x53, 0x48, 0x50, 0x2c, 0x20, 0x55,
0x50, 0x6e, 0x50, 0x2f, 0x31, 0x2e, 0x30, 0x2c,
0x20, 0x53, 0x61, 0x6d, 0x73, 0x75, 0x6e, 0x67,
0x20, 0x55, 0x50, 0x6e, 0x50, 0x20, 0x53, 0x44,
0x4b, 0x2f, 0x31, 0x2e, 0x30, 0x21, 0x68, 0x74,
0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x31, 0x39, 0x32,
0x2e, 0x31, 0x36, 0x38, 0x2e, 0x34, 0x32, 0x2e,
0x31, 0x32, 0x35, 0x3a, 0x37, 0x36, 0x37, 0x36,
0x2f, 0x73, 0x6d, 0x70, 0x5f, 0x32, 0x5f, 0x23,
0x53, 0x48, 0x50, 0x2c, 0x20, 0x55, 0x50, 0x6e,
0x50, 0x2f, 0x31, 0x2e, 0x30, 0x2c, 0x20, 0x53,
0x61, 0x6d, 0x73, 0x75, 0x6e, 0x67, 0x20, 0x55,
0x50, 0x6e, 0x50, 0x20, 0x53, 0x44, 0x4b, 0x2f,
0x31, 0x2e, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00
};
char *get_test_ssdp_data()
{
size_t len = sizeof(test_ssdp_data);
char *buffer = (char *)malloc(len);
if (buffer == NULL) {
perror("malloc failed");
exit(1);
}
memcpy(buffer, test_ssdp_data, len);
return buffer;
}
void get_test_l2_map(L2Map *l2map)
{
(*l2map)[std::string("192.168.42.125")] = std::string("00:01:02:03:04:05");
}