summaryrefslogtreecommitdiff
path: root/ccast/ccmdns.c
diff options
context:
space:
mode:
Diffstat (limited to 'ccast/ccmdns.c')
-rwxr-xr-x[-rw-r--r--]ccast/ccmdns.c761
1 files changed, 542 insertions, 219 deletions
diff --git a/ccast/ccmdns.c b/ccast/ccmdns.c
index 1000db3..8fd7046 100644..100755
--- a/ccast/ccmdns.c
+++ b/ccast/ccmdns.c
@@ -130,10 +130,12 @@ typedef int SOCKET;
# define DBG(xxx) a1logd xxx ;
# define DBG2(xxx) a1logd xxx ;
# define DLEV 0
+# define DLEVP1 0
#else
# define DBG(xxx) ;
# define DBG2(xxx) a1logd xxx ;
# define DLEV 2
+# define DLEVP1 3
#endif /* DEBUG */
/* ================================================================ */
@@ -144,7 +146,7 @@ typedef int SOCKET;
#define SOURCE_PORT 5353
#define DESTINATION_PORT 5353
-#define BUFSIZE 1024
+#define BUFSIZE 2048
/* Various DNS defines */
#define DNS_CLASS_IN 0x0001
@@ -158,6 +160,25 @@ typedef int SOCKET;
#define DNS_TYPE_NSEC 47 /* DNS Next Secure Name */
+char *cctype2str(cctype typ) {
+ switch (typ) {
+ case cctyp_unkn:
+ return "Unknown";
+ case cctyp_1:
+ return "One";
+ case cctyp_2:
+ return "Two";
+ case cctyp_Audio:
+ return "Audio";
+ case cctyp_Ultra:
+ return "Ultra";
+ case cctyp_Other:
+ return "Other";
+ default:
+ return "Unexpected";
+ }
+}
+
#ifdef NEVER
/* Print out a V6 address in zero compresed form */
/* (It would be nice to add zero compression) */
@@ -201,20 +222,21 @@ static int init_mDNS() {
return 0;
}
-/* Setup the send socket */
+/* Setup the send and recieve socket */
/* Return nz on error */
-static int init_send_mDNS(SOCKET *psock) {
+static int init_socket_mDNS(SOCKET *psock) {
int nRet, nOptVal;
int off;
SOCKET sock;
struct sockaddr_in stSourceAddr;
+ struct ip_mreq stIpMreq;
- DBG((g_log,0,"init_send_mDNS() called\n"))
+ DBG((g_log,0,"init_socket_mDNS() called\n"))
/* get a datagram (UDP) socket */
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
- DBG((g_log,0,"opening send UDP socked failed with %d\n",ERRNO))
+ DBG((g_log,0,"opening UDP socked failed with %d\n",ERRNO))
return 1;
}
@@ -234,7 +256,7 @@ static int init_send_mDNS(SOCKET *psock) {
{
int on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on))) {
- DBG((g_log,0,"setsockopt(SO_REUSEADDR) failed with %d\n",ERRNO))
+ DBG((g_log,0,"setsockopt(SO_REUSEADDR) failed with %d\n",ERRNO))
closesocket(sock);
return 1;
}
@@ -272,6 +294,9 @@ static int init_send_mDNS(SOCKET *psock) {
return 1;
}
+ /* - - - - - - - */
+ /* Send setup */
+
/* disable loopback of multicast datagrams we send, since the
* default--according to Steve Deering--is to loopback all
* datagrams sent on any interface which is a member of the
@@ -306,13 +331,55 @@ static int init_send_mDNS(SOCKET *psock) {
if (psock != NULL)
*psock = sock;
- DBG((g_log,0,"init sending mDNS succeed\n",ERRNO))
+ /* - - - - - - - */
+ /* Recieve setup */
+
+ /* join the multicast group we want to receive datagrams from */
+ stIpMreq.imr_multiaddr.s_addr = inet_addr(DESTINATION_MCAST); /* group addr */
+ stIpMreq.imr_interface.s_addr = INADDR_ANY; /* use default */
+ nRet = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&stIpMreq, sizeof (struct ip_mreq));
+
+ if (nRet == SOCKET_ERROR) {
+ DBG((g_log,0,"registering for read events failed with %d\n",ERRNO))
+ closesocket(sock);
+ return 1;
+ }
+
+ /* Make this timeout after 100 msec second */
+#ifdef NT
+ {
+ DWORD tv;
+ tv = 100;
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) < 0) {
+ DBG((g_log,0,"setsockopt timout failed with %d\n",ERRNO))
+ closesocket(sock);
+ return 1;
+ }
+ }
+#else
+ {
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 100 * 1000;
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) < 0) {
+ DBG((g_log,0,"setsockopt timout failed with %d\n",ERRNO))
+ closesocket(sock);
+ return 1;
+ }
+ }
+#endif
+
+ if (psock != NULL)
+ *psock = sock;
+
+ DBG((g_log,0,"init mDNS socket succeed\n",ERRNO))
return 0;
}
-/* Send an mDNS quesry */
-/* on some platforms if we try to transmit & recieve at the same time */
+/* Send an mDNS query */
+/* On some platforms if we try to transmit & recieve at the same time (what ?) */
/* Return nz on error */
static int send_mDNS(SOCKET sock) {
int nRet;
@@ -360,7 +427,7 @@ static int send_mDNS(SOCKET sock) {
return 0;
}
-static int parse_dns(char **name, char **ip, ORD8 *buf, int size);
+static int parse_dns(char **name, char **ip, cctype *ptyp, int *pcaflags, ORD8 *buf, int size);
/* Free up what get_ccids returned */
void free_ccids(ccast_id **ids) {
@@ -379,132 +446,22 @@ void free_ccids(ccast_id **ids) {
/* Spend the given time waiting for replies. */
/* (Note than on MSWin this will be a minimum of 500msec) */
/* Add any ChromeCast replies to the list. */
-/* return nz on error */
-static int init_receive_mDNS(SOCKET *psock) {
- int nRet;
- int off, size;
- struct sockaddr_in stSourceAddr;
- struct ip_mreq stIpMreq;
- SOCKET sock;
-
- DBG((g_log,0,"init_receive_mDNS() called\n"))
-
- /* get a datagram (UDP) socket */
- sock = socket(PF_INET, SOCK_DGRAM, 0);
- if (sock == INVALID_SOCKET) {
- DBG((g_log,0,"opening receive UDP socked failed with %d\n",ERRNO))
- return 1;
- }
-
- /* We can't receive from port 5353 if someone else is using it, */
- /* so set the SO_REUSEADDR option (which is enough for MSWin), */
- /* and SO_REUSEPORT for OS X and Linux */
- {
- int on = 1, off = 0;
- if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on))) {
- DBG((g_log,0,"setsockopt(SO_REUSEADDR) failed with %d\n",ERRNO))
- closesocket(sock);
- return 1;
- }
-
- /* Need this to be able to open port on Unix like systems */
-#ifndef NT
-# ifndef SO_REUSEPORT
-# ifdef __APPLE__
-# define SO_REUSEPORT 0x0200
-# else /* Linux */
-# define SO_REUSEPORT 15
-# endif
-# endif
- if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char *)&on, sizeof(on))) {
- DBG((g_log,0,"setsockopt(SO_REUSEPORT) failed with %d\n",ERRNO))
- }
-#endif
- }
-
- /* init source address structure */
- stSourceAddr.sin_family = PF_INET;
- stSourceAddr.sin_port = htons(SOURCE_PORT);
- stSourceAddr.sin_addr.s_addr = INADDR_ANY;
-
- /*
- * Calling bind() is not required, but some implementations need it
- * before you can reference any multicast socket options
- * and in this case we must be on port 5353.
- */
- nRet = bind(sock, (struct sockaddr *)&stSourceAddr,
- sizeof(struct sockaddr));
- if (nRet == SOCKET_ERROR) {
- DBG((g_log,0,"bind failed with %d\n",ERRNO))
- closesocket(sock);
- return 1;
- }
-
- /* join the multicast group we want to receive datagrams from */
- stIpMreq.imr_multiaddr.s_addr = inet_addr(DESTINATION_MCAST); /* group addr */
- stIpMreq.imr_interface.s_addr = INADDR_ANY; /* use default */
- nRet = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- (char *)&stIpMreq, sizeof (struct ip_mreq));
-
- if (nRet == SOCKET_ERROR) {
- DBG((g_log,0,"registering for read events failed with %d\n",ERRNO))
- closesocket(sock);
- return 1;
- }
-
- /* Make this timeout after 100 msec second */
-#ifdef NT
- {
- DWORD tv;
- tv = 100;
- if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) < 0) {
- DBG((g_log,0,"setsockopt timout failed with %d\n",ERRNO))
- closesocket(sock);
- return 1;
- }
- }
-#else
- {
- struct timeval tv;
- tv.tv_sec = 0;
- tv.tv_usec = 100 * 1000;
- if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) < 0) {
- DBG((g_log,0,"setsockopt timout failed with %d\n",ERRNO))
- closesocket(sock);
- return 1;
- }
- }
-#endif
-
- if (psock != NULL)
- *psock = sock;
-
- return 0;
-}
-
-/* Spend the given time waiting for replies. */
-/* (Note than on MSWin this will be a minimum of 500msec) */
-/* Add any ChromeCast replies to the list. */
/* return nz on error & free *ids */
-static int receive_mDNS(SOCKET sock, ccast_id ***ids, int emsec) {
- int nids = 0;
+static int receive_mDNS(SOCKET sock, ccast_id ***ids, int *nids, int emsec) {
unsigned int smsec;
unsigned int nSize;
int off, size;
ORD8 achInBuf[BUFSIZE];
struct sockaddr_in stSourceAddr;
- /* Count the number of current id's */
- if (*ids != NULL) {
- for (nids = 0; (*ids)[nids] != NULL; nids++)
- ;
- }
-
- DBG((g_log,0,"receive_mDNS() called with %d ids\n",nids))
+ DBG((g_log,0,"receive_mDNS() called with %d ids\n",*nids))
+ /* While there is still wait time */
for (smsec = msec_time(), emsec += smsec;msec_time() <= emsec;) {
int i;
char *name, *ip;
+ cctype typ;
+ int caflags; /* (We're not currently saving this) */
struct sockaddr stSockAddr;
/* Recv the available data */
@@ -514,75 +471,86 @@ static int receive_mDNS(SOCKET sock, ccast_id ***ids, int emsec) {
if (size == SOCKET_ERROR) {
if (ERRNO == UDP_SOCKET_TIMEOUT)
continue; /* Timeout */
- DBG((g_log,0,"recvfrom failed with %d\n",ERRNO))
+ DBG2((g_log,DLEV,"recvfrom failed with %d\n",ERRNO))
free_ccids(*ids);
*ids = NULL;
return 1;
}
- DBG((g_log,0,"Got mDNS message length %d bytes\n",size))
+
+ DBG2((g_log,DLEVP1,"Got mDNS message length %d bytes\n",size))
#ifdef DEBUG
adump_bytes(g_log, " ", achInBuf, 0, size);
#endif
- if (parse_dns(&name, &ip, achInBuf, size) != 0) {
+ if (parse_dns(&name, &ip, &typ, &caflags, achInBuf, size) != 0) {
DBG((g_log,0,"Failed to parse the reply\n"))
} else {
DBG((g_log,0,"Parsed reply OK\n"))
-//if (*pnids > 0) {
-// name = strdup("Argyll1234");
-// ip = strdup("10.0.0.129");
-//}
/* If we found an entry */
if (name != NULL && ip != NULL) {
- DBG((g_log,0,"Got a name '%s' & IP '%s'\n",name,ip))
- /* Check if it is a duplcate */
- for (i = 0; i < nids; i++) {
- if (strcmp((*ids)[i]->name, name) == 0
- && strcmp((*ids)[i]->ip, ip) == 0)
- break; /* yes */
- }
- if (i < nids) {
- DBG((g_log,0,"Duplicate\n"))
+ DBG((g_log,0,"Got a name '%s', IP '%s', type %s\n",name,ip, cctype2str(typ)))
+
+ /* Check if it is a Chromecast-Audio or Other */
+ if (typ == cctyp_Audio
+ || typ == cctyp_Other) {
+ DBG((g_log,0,"Ignoring Chromecast-Audio/Other\n"))
free(name);
free(ip);
+
} else {
- ccast_id **tids;
- if ((tids = realloc(*ids, (nids + 2) * sizeof(ccast_id *))) == NULL
- || (*ids = tids, (*ids)[nids] = malloc(sizeof(ccast_id)), (*ids)[nids]) == NULL) {
- DBG((g_log,0,"realloc/malloc fail\n"))
+
+ /* Check if it is a duplcate */
+ for (i = 0; i < *nids; i++) {
+ if (strcmp((*ids)[i]->name, name) == 0
+ && strcmp((*ids)[i]->ip, ip) == 0)
+ break; /* yes */
+ }
+ if (i < *nids) {
+ DBG((g_log,0,"Duplicate\n"))
free(name);
free(ip);
- free_ccids(*ids);
- *ids = NULL;
- return 1;
} else {
- DBG((g_log,0,"Adding entry\n"))
- (*ids)[nids]->name = name;
- (*ids)[nids]->ip = ip;
- (*ids)[++nids] = NULL; /* End marker */
+ ccast_id **tids;
+ if ((tids = realloc(*ids, (*nids + 2) * sizeof(ccast_id *))) == NULL
+ || (*ids = tids, (*ids)[*nids] = malloc(sizeof(ccast_id)), (*ids)[*nids]) == NULL) {
+ DBG((g_log,0,"realloc/malloc fail\n"))
+ free(name);
+ free(ip);
+ free_ccids(*ids);
+ *ids = NULL;
+ return 1;
+ } else {
+ DBG((g_log,0,"Adding entry\n"))
+ (*ids)[*nids]->name = name;
+ (*ids)[*nids]->ip = ip;
+ (*ids)[*nids]->typ = typ;
+ (*nids)++;
+ (*ids)[*nids] = NULL; /* End marker */
+ }
}
}
}
}
}
- DBG((g_log,0,"receive_mDNS() returning %d in list\n",nids))
+ DBG2((g_log,DLEVP1,"receive_mDNS() returning %d in list\n",*nids))
return 0;
}
/* ==================================================================== */
-/* Get a list of Chromecasts. Return NULL on error */
+/* Get a list of Video output capable Chromecasts. Return NULL on error */
/* Last pointer in array is NULL */
-/* Takes 1.0 second to return */
+/* Takes 1.5 second to return */
ccast_id **get_ccids() {
ccast_id **ids = NULL;
+ int nids = 0;
int i, j, k;
unsigned int smsec;
- int waittime = 100;
- SOCKET ssock, rsock;
+ int waittime = 200;
+ SOCKET sock;
DBG2((g_log,DLEV,"get_ccids: called\n"))
@@ -591,45 +559,38 @@ ccast_id **get_ccids() {
return NULL;
}
- if (init_send_mDNS(&ssock)) {
- DBG2((g_log,0,"get_ccids: init_send_mDNS() failed\n"))
- return NULL;
- }
-
- if (init_receive_mDNS(&rsock)) {
- DBG2((g_log,0,"get_ccids: init_receive_mDNS() failed\n"))
- closesocket(ssock);
+ if (init_socket_mDNS(&sock)) {
+ DBG2((g_log,0,"get_ccids: init_socket_mDNS() failed\n"))
return NULL;
}
smsec = msec_time();
/* Try a few times, with increasing response wait time */
- for (k = 1; ids == NULL && (msec_time() - smsec) < 1000; k++) {
+ for (k = 1;
+ ((msec_time() - smsec) < 700)
+ || (nids == 0 && (msec_time() - smsec) < 1600);
+ k++) {
DBG2((g_log,DLEV,"get_ccids: Sending mDNS query #%d:\n",k))
- if (send_mDNS(ssock)) {
+ if (send_mDNS(sock)) {
DBG2((g_log,0,"get_ccids: send_mDNS() #1 failed\n"))
- closesocket(ssock);
- closesocket(rsock);
+ closesocket(sock);
return NULL;
}
DBG2((g_log,DLEV,"get_ccids: Waiting for mDNS reply #%d:\n",k))
- if (receive_mDNS(rsock, &ids, waittime)) {
+ if (receive_mDNS(sock, &ids, &nids, waittime)) {
DBG2((g_log,0,"get_ccids: receive_mDNS() #%d failed\n",k))
- closesocket(ssock);
- closesocket(rsock);
+ closesocket(sock);
return NULL;
}
- if (ids != NULL)
- DBG2((g_log,DLEV,"get_ccids: Got reply\n"))
+ DBG2((g_log,DLEV,"get_ccids: have %d %s\n",nids, nids == 1 ? "reply" : "replies"))
- waittime *= 2;
+ if (waittime < 500)
+ waittime = 500;
}
-
- closesocket(ssock);
- closesocket(rsock);
+ closesocket(sock);
/* If no ChromCasts found, return an empty list */
if (ids == NULL) {
@@ -655,6 +616,7 @@ ccast_id **get_ccids() {
DBG2((g_log,DLEV," Entry %d:\n",i))
DBG2((g_log,DLEV," Name: %s\n",ids[i]->name))
DBG2((g_log,DLEV," IP: %s\n",ids[i]->ip))
+ DBG2((g_log,DLEV," Type: %s\n",cctype2str(ids[i]->typ)))
}
DBG2((g_log,DLEV,"get_ccids: Returning %d devices\n",i))
@@ -698,46 +660,66 @@ static int read_string_imp(char **rv, int *slen, ORD8 *buf, int off, int size, i
int len;
int d1 = 0;
-//printf("~1 read_string_imp called for off 0x%x rec %d\n",off,rec);
+ DBG((g_log,8,"read_string_imp called for off %d (0x%x) rec %d\n",off,off,rec));
- if (rec > 10) /* Too many recursions */
+ if (rec > 10) { /* Too many recursions */
+ DBG((g_log,8,"read_string_imp too many recursions\n"));
return -1; /* Error */
+ }
- if (off >= size)
+ if (off >= size) {
+ DBG((g_log,8,"read_string_imp off %d >= size %d\n",off,size));
return -1; /* Error */
+ }
for (;;) {
-//printf("~1 top of loop at off 0x%x\n",off);
+ DBG((g_log,8,"top of loop at off %d\n",off));
len = buf[off];
- if (len == 0xc0) { /* Is it an offset marker */
+ if ((len & 0xc0) == 0xc0) { /* Is it an offset marker */
int poff;
-//printf("~1 got pointer\n");
- if ((size - off) < 2)
+
+ DBG((g_log,8,"got pointer\n"));
+ if ((size - off) < 2) {
+ DBG((g_log,8,"read_string_imp size %d - off %d < 2\n",size,off));
return -1;
+ }
poff = read_ORD16_be(buf + off); off += 2;
poff -= 0xc000;
- if (poff < 0 || poff >= size)
+ if (poff < 0 || poff >= size) {
return -1;
+ }
+ DBG((g_log,8,"read_string_imp recursing\n"));
read_string_imp(rv, slen, buf, poff, size, rec+1);
-//if (slen != NULL) printf("~1 after recurse, slen = %d, off = 0x%x\n",*slen,off);
+ if (slen != NULL) {
+ DBG((g_log,8,"after recurse, slen = %d, off = %d (0x%x)\n",*slen,off,off));
+ }
break; /* we're done */
} else {
-//printf("~1 got string length %d\n",len);
+ DBG((g_log,8,"got string length %d\n",len));
+
off++;
- if ((off + len) >= size)
+ if ((off + len) >= size) {
+ DBG((g_log,8,"read_string_imp size off %d + len %d >= size %d\n",off,len,size));
return -1;
+ }
- if (len == 0)
+ if (len == 0) {
+ DBG((g_log,8,"read_string_imp len == 0 - done\n"));
break; /* we're done */
+ }
if (rv != NULL) {
memcpy(*rv, buf + off, len);
-//(*rv)[len] = '\000'; printf("Copied string %p = '%s'\n",*rv,*rv);
+
+#ifdef DEBUG
+ (*rv)[len] = '\000';
+ DBG((g_log,8,"Copied string %p = '%s'\n",*rv,*rv));
+#endif
*rv += len;
}
@@ -745,21 +727,25 @@ static int read_string_imp(char **rv, int *slen, ORD8 *buf, int off, int size, i
if (slen != NULL) {
(*slen) += len;
-//printf("~1 slen now = %d\n",*slen);
+ DBG((g_log,8,"slen now = %d\n",*slen));
}
}
d1 = 1;
- if (slen != NULL)
+ if (slen != NULL) {
(*slen)++;
+ }
if (rv != NULL) {
(*rv)[0] = '.';
(*rv)++;
}
}
-//if (slen != NULL) printf("~1 returning slen = %d\n",*slen);
+ if (slen != NULL) {
+ DBG((g_log,8,"returning slen = %d\n",*slen));
+ }
+
return off;
}
@@ -769,29 +755,31 @@ static int read_string(char **rv, ORD8 *buf, int off, int size) {
int len = 0, toff = off;
char *trv;
-//printf("~1 read_string called for off 0x%x\n",off);
+ DBG((g_log,7,"read_string called for off 0x%x\n",off));
/* See how long it will be */
if ((toff = read_string_imp(NULL, &len, buf, off, size, 0)) < 0) {
-//printf("~1 read_string_imp length returned error\n");
+ DBG((g_log,7,"read_string_imp length returned error\n"));
return toff;
}
-//printf("~1 read_string_imp got length %d\n",len);
+ DBG((g_log,7,"read_string_imp got length %d\n",len));
if (len == 0) {
-//printf("~1 Got zero length string\n");
+ DBG((g_log,7,"Got zero length string\n"));
len++; /* Room for null string */
}
if ((*rv = trv = malloc(len)) == NULL) {
+ DBG((g_log,7,"malloc for string failed\n"));
return -1;
}
-//printf("Malloced %p\n",*rv);
+ DBG((g_log,7,"loced %p\n",*rv));
off = read_string_imp(&trv, NULL, buf, off, size, 0);
if (off >= 0) {
(*rv)[len-1] = '\000';
-//printf("~1 read string ok: %p = '%s'\n",*rv, *rv);
+ DBG((g_log,7,"read string ok: %p = '%s'\n",*rv, *rv));
+ } else {
+ DBG((g_log,7,"reading string failed\n"));
}
-//else printf("~1 reading string failed\n");
return off;
}
@@ -820,9 +808,9 @@ int parse_query(ORD8 *buf, int off, int size) {
return off;
}
-/* Parse an mDNS reply */
+/* Parse an mDNS reply, and set Friendly name (if known) + formal name + IP */
/* Return updated off value or -1 on error */
-int parse_reply(char **pname, char **pip, ORD8 *buf, int off, int size) {
+int parse_reply(char **pname, char **pip, cctype *ptyp, int *pcaflags, ORD8 *buf, int off, int size) {
char *sv;
int rtype, rclass, rdlength;
unsigned int ttl;
@@ -831,21 +819,21 @@ int parse_reply(char **pname, char **pip, ORD8 *buf, int off, int size) {
if ((off = read_string(&sv, buf, off, size)) < 0)
return -1;
- DBG((g_log,0," Got string '%s'\n",sv))
+ DBG((g_log,0," Got string '%s', now off = 0x%x\n",sv,off))
if ((size - off) < 2) {
free(sv);
return -1;
}
rtype = read_ORD16_be(buf + off); off += 2;
- DBG((g_log,0," RTYPE = %d\n",rtype))
+ DBG((g_log,0," RTYPE = %d, now off = 0x%x\n",rtype,off))
if ((size - off) < 2) {
free(sv);
return -1;
}
rclass = read_ORD16_be(buf + off); off += 2;
- DBG((g_log,0," RCLASS = 0x%04x\n",rclass))
+ DBG((g_log,0," RCLASS = 0x%04x, now off = 0x%x\n",rclass,off))
/* rclass top bit is cache flush bit */
if ((rclass & 0x7fff) != DNS_CLASS_IN) {
@@ -857,12 +845,12 @@ int parse_reply(char **pname, char **pip, ORD8 *buf, int off, int size) {
if ((size - off) < 4)
return -1;
ttl = read_ORD32_be(buf + off); off += 4;
- DBG((g_log,0," TTL = %u\n",ttl))
+ DBG((g_log,0," TTL = %u, now off = 0x%x\n",ttl,off))
if ((size - off) < 2)
return -1;
rdlength = read_ORD16_be(buf + off); off += 2;
- DBG((g_log,0," RDLENGTH = %d\n",rdlength))
+ DBG((g_log,0," RDLENGTH = %d, now off = 0x%x\n",rdlength,off))
if ((off + rdlength) > size) {
DBG((g_log,0," response RDLENGTH is longer than remaining buffer (%d)\n",size - off))
@@ -872,7 +860,10 @@ int parse_reply(char **pname, char **pip, ORD8 *buf, int off, int size) {
/* Just decode the replies we need */
if (rtype == DNS_TYPE_TXT) { /* Check it's a ChromeCast & get its name */
- char *cp;
+ char *cp, *fn = NULL;
+ int rdsize = off + rdlength;
+ int caflags = 0;
+
if ((cp = strchr(sv, '.')) == NULL) {
free(sv);
return -1;
@@ -884,12 +875,112 @@ int parse_reply(char **pname, char **pip, ORD8 *buf, int off, int size) {
return -1;
}
- DBG((g_log,0," Chromacast '%s'\n", sv))
- if ((*pname = strdup(sv)) == NULL) {
- DBG((g_log,0,"strdup failed\n"))
- free(sv);
- return -1;
+#ifdef NEVER
+ DBG((g_log,0," TXT data:\n"))
+ adump_bytes(g_log, " ", buf + off, 0, rdlength);
+#endif
+
+ /* Chromecast TXT DATA format:
+
+ Buffer is series of strings, each beginning with a byte length.
+ Each string is id=value, with the following known values:
+
+ id=c17a8e82ee7187d5013e2d12c61bbd40 uuidNoHyphens
+ rm=669B9448366A01CB
+ ve=05 SW version ????
+ md=Chromecast Model ?
+ i.e. Chromecast-Audio
+ i.e. Chromecast-Ultra
+ i.e. Group
+ ic=/setup/icon.png Icon file ??
+ fn=Chromecast6892 Friendly name
+ ca=4101 Capabilities bits ???
+ st=1 Application running ??
+ bs=FA8FCA566645 Application ID ??
+ rs=Pattern generator ready Application state ??
+
+ */
+
+ /* Parse the Chromacast TX Data */
+ for (; off < rdsize; ) {
+ int slen;
+ char *ss;
+
+ /* Read the string length */
+ if ((rdsize - off) < 1)
+ goto done_tx;
+
+ slen = read_ORD8(buf + off); off += 1;
+
+ if ((rdsize - off) < slen)
+ goto done_tx;
+
+ if ((ss = malloc(slen + 1)) == NULL) {
+ DBG((g_log,0,"malloc for sub-string failed\n"));
+ return -1;
+ }
+
+ memcpy(ss, buf + off, slen);
+ ss[slen] = '\000';
+ off += slen;
+
+ DBG((g_log,0," TX Sub-string '%s'\n", ss))
+
+ /* Record info we want: */
+ if (strncmp(ss, "fn=", 3) == 0) { /* Friendly name */
+ if ((fn = malloc(slen -3 +1)) == NULL) {
+ DBG((g_log,0,"malloc for fn-string failed\n"));
+ return -1;
+ }
+ strcpy(fn, ss + 3);
+ }
+
+ if (strncmp(ss, "ca=", 3) == 0) { /* Capability bits ? */
+ caflags = atoi(ss + 3);
+ }
+ free(ss);
}
+
+ done_tx:
+
+ if (fn != NULL) {
+ DBG((g_log,0," Chromacast '%s', fn '%s'\n", sv, fn))
+ } else {
+ DBG((g_log,0," Chromacast '%s'\n", sv))
+ }
+
+ DBG2((g_log,DLEV,"ca bits 0x%x\n", caflags))
+
+ if (strncmp(sv, "Chromecast-Ultra", 16) == 0) {
+ *ptyp = cctyp_Ultra;
+ } else if (strncmp(sv, "Chromecast-Audio", 16) == 0) {
+ *ptyp = cctyp_Audio;
+ } else {
+ // Hmm. A CC1 or non-Google device.
+
+ /* We're guessing that bit 0 of the ca bits == Video capable */
+ /* and bit 1 of the ca bits == Audio capable */
+ if (caflags & 1)
+ *ptyp = cctyp_1;
+ else if (caflags & 4)
+ *ptyp = cctyp_Audio;
+ else
+ *ptyp = cctyp_Other;
+ }
+
+ if (fn != NULL) {
+ *pname = fn;
+ } else {
+ free(fn);
+ if ((*pname = strdup(sv)) == NULL) {
+ DBG((g_log,0,"strdup failed\n"))
+ free(sv);
+ return -1;
+ }
+ }
+
+ off = rdsize;
+
} else if (rtype == DNS_TYPE_A) {
/* Should we check name matches ? */
if ((*pip = malloc(3 * 4 + 3 + 1)) == NULL) {
@@ -900,6 +991,7 @@ int parse_reply(char **pname, char **pip, ORD8 *buf, int off, int size) {
sprintf(*pip, "%d.%d.%d.%d", buf[off], buf[off+1], buf[off+2], buf[off+3]);
DBG((g_log,0," V4 address = %s\n",*pip))
+ off += rdlength;
} else if (rtype == DNS_TYPE_AAAA) { /* The IPV6 address */
/* Should we check name matches ? */
if ((*pip = malloc(8 * 4 + 7 + 1)) == NULL) {
@@ -918,10 +1010,11 @@ int parse_reply(char **pname, char **pip, ORD8 *buf, int off, int size) {
buf[off+14] * 245 + buf[off+15]);
DBG((g_log,0," V6 address = %s\n",*pip))
+ off += rdlength;
} else {
DBG((g_log,0," Skipping reply at 0x%x\n",off))
+ off += rdlength;
}
- off += rdlength;
free(sv);
return off;
@@ -930,7 +1023,7 @@ int parse_reply(char **pname, char **pip, ORD8 *buf, int off, int size) {
/* Parse an mDNS reply into a ChromCast name & IP address */
/* Allocate and return name and IP on finding ChromeCast reply, NULL otherwise */
/* Return nz on failure */
-static int parse_dns(char **pname, char **pip, ORD8 *buf, int size) {
+static int parse_dns(char **pname, char **pip, cctype *ptyp, int *pcaflags, ORD8 *buf, int size) {
int i, off = 0;
int id, flags, qdcount, ancount, nscount, arcount;
@@ -976,7 +1069,7 @@ static int parse_dns(char **pname, char **pip, ORD8 *buf, int size) {
// Parse all the answers (ANCOUNT)
for (i = 0; i < ancount; i++) {
- if ((off = parse_reply(pname, pip, buf, off, size)) < 0) {
+ if ((off = parse_reply(pname, pip, ptyp, pcaflags, buf, off, size)) < 0) {
DBG((g_log,0," ### Parsing answer failed ###\n"))
return 1;
}
@@ -984,7 +1077,7 @@ static int parse_dns(char **pname, char **pip, ORD8 *buf, int size) {
// Parse all the NS records (NSCOUNT)
for (i = 0; i < nscount; i++) {
- if ((off = parse_reply(pname, pip, buf, off, size)) < 0) {
+ if ((off = parse_reply(pname, pip, ptyp, pcaflags, buf, off, size)) < 0) {
DBG((g_log,0," ### Parsing NS record failed ###\n"))
return 1;
}
@@ -992,7 +1085,7 @@ static int parse_dns(char **pname, char **pip, ORD8 *buf, int size) {
// Parse all the addition RR answers (ARCOUNT)
for (i = 0; i < arcount; i++) {
- if ((off = parse_reply(pname, pip, buf, off, size)) < 0) {
+ if ((off = parse_reply(pname, pip, ptyp, pcaflags, buf, off, size)) < 0) {
DBG((g_log,0," ### Parsing additional records failed ###\n"))
return 1;
}
@@ -1032,6 +1125,35 @@ static int parse_dns(char **pname, char **pip, ORD8 *buf, int size) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*/
+
+/*
+ .ca values:
+
+ Original CC 5 = 0x0005
+ Latest CC1 4101 = 0x1005
+ Latest CC2 4101 = 0x1005
+ Latest CC3 4101 = 0x1005
+ Vizio TV 2053 = 0x0805
+ Toshiba TV 4101 = 0x1005
+ Audio CC 4100 = 0x1004
+ Sound bar 2052 = 0x0804
+
+ Google documents the following Capability strings:
+
+ VIDEO_OUT The receiver supports video output.
+ VIDEO_IN The receiver supports video input (camera).
+ AUDIO_OUT The receiver supports audio output.
+ AUDIO_IN The receiver supports audio input (microphone).
+ MULTIZONE_GROUP The receiver represents a multi-zone group.
+
+ Which may correspond to ca bits 0, 1, 2, 3 respectively ?
+
+*/
+
+/*
+ Original CC 1 dump ?
+
0000: 00 00 84 00 00 00 00 01 00 00 00 05 0b 5f 67 6f ............._go
0010: 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f oglecast._tcp.lo
0020: 63 61 6c 00 00 0c 00 01 00 00 11 94 00 11 0e 43 cal............C
@@ -1050,6 +1172,207 @@ static int parse_dns(char **pname, char **pip, ORD8 *buf, int size) {
00f0: 09 c0 2e 00 05 00 00 80 00 40 c0 c4 00 2f 80 01 .........@.../..
0100: 00 00 00 78 00 05 c0 c4 00 01 40 ...x......@
+*/
+
+/*
+
+ ChromeCast 1 dump
+
+0000: 00 00 84 00 00 00 00 01 00 00 00 03 0b 5f 67 6f ............._go
+0010: 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f oglecast._tcp.lo
+0020: 63 61 6c 00 00 0c 00 01 00 00 00 78 00 44 2b 43 cal........x.D+C
+0030: 68 72 6f 6d 65 63 61 73 74 2d 63 31 37 61 65 38 hromecast-c17ae8
+0040: 38 32 65 37 65 31 38 37 35 64 30 31 33 65 64 32 82e7e1875d013ed2
+0050: 31 32 63 31 36 62 62 64 34 30 0b 5f 67 6f 6f 67 12c16bbd40._goog
+0060: 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f 63 61 lecast._tcp.loca
+0070: 6c 00 2b 43 68 72 6f 6d 65 63 61 73 74 2d 63 31 l.+Chromecast-c1
+0080: 37 61 65 38 38 32 65 37 65 31 38 37 35 64 30 31 7ae882e7e1875d01
+0090: 33 65 64 32 31 32 63 31 36 62 62 64 34 30 0b 5f 3ed212c16bbd40._
+00a0: 67 6f 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 googlecast._tcp.
+00b0: 6c 6f 63 61 6c 00 00 10 80 01 00 00 11 94 00 82 local...........
+00c0: 23 69 64 3d 63 31 37 61 65 38 38 32 65 37 65 31 #id=c17ae882e7e1
+00d0: 38 37 35 64 30 31 33 65 64 32 31 32 63 31 36 62 875d013ed212c16b
+00e0: 62 64 34 30 03 72 6d 3d 05 76 65 3d 30 35 0d 6d bd40.rm=.ve=05.m
+00f0: 64 3d 43 68 72 6f 6d 65 63 61 73 74 12 69 63 3d d=Chromecast.ic=
+0100: 2f 73 65 74 75 70 2f 69 63 6f 6e 2e 70 6e 67 11 /setup/icon.png.
+0110: 66 6e 3d 43 68 72 6f 6d 65 63 61 73 74 36 38 39 fn=Chromecast689
+0120: 32 07 63 61 3d 34 31 30 31 04 73 74 3d 30 0f 62 2.ca=4101.st=0.b
+0130: 73 3d 46 41 38 46 43 41 35 36 36 36 34 35 03 72 s=FA8FCA566645.r
+0140: 73 3d 2b 43 68 72 6f 6d 65 63 61 73 74 2d 63 31 s=+Chromecast-c1
+0150: 37 61 65 38 38 32 65 37 65 31 38 37 35 64 30 31 7ae882e7e1875d01
+0160: 33 65 64 32 31 32 63 31 36 62 62 64 34 30 0b 5f 3ed212c16bbd40._
+0170: 67 6f 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 googlecast._tcp.
+0180: 6c 6f 63 61 6c 00 00 21 80 01 00 00 00 78 00 32 local..!.....x.2
+0190: 00 00 00 00 1f 49 24 63 31 37 61 65 38 38 32 2d .....I$c17ae882-
+01a0: 65 37 65 31 2d 38 37 35 64 2d 30 31 33 65 2d 64 e7e1-875d-013e-d
+01b0: 32 31 32 63 31 36 62 62 64 34 30 05 6c 6f 63 61 212c16bbd40.loca
+01c0: 6c 00 24 63 31 37 61 65 38 38 32 2d 65 37 65 31 l.$c17ae882-e7e1
+01d0: 2d 38 37 35 64 2d 30 31 33 65 2d 64 32 31 32 63 -875d-013e-d212c
+01e0: 31 36 62 62 64 34 30 05 6c 6f 63 61 6c 00 00 01 16bbd40.local...
+01f0: 80 01 00 00 00 78 00 04 c0 a8 01 68 .....x.....h
+
+
+ChromeCast 2 dump:
+
+0000: 00 00 84 00 00 00 00 01 00 00 00 03 0b 5f 67 6f ............._go
+0010: 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f oglecast._tcp.lo
+0020: 63 61 6c 00 00 0c 00 01 00 00 00 78 00 44 2b 43 cal........x.D+C
+0030: 68 72 6f 6d 65 63 61 73 74 2d 38 32 38 30 30 66 hromecast-82800f
+0040: 65 63 33 37 37 36 35 39 38 66 35 38 64 34 61 61 ec3776598f58d4aa
+0050: 63 39 65 36 35 64 30 30 30 61 0b 5f 67 6f 6f 67 c9e65d000a._goog
+0060: 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f 63 61 lecast._tcp.loca
+0070: 6c 00 2b 43 68 72 6f 6d 65 63 61 73 74 2d 38 32 l.+Chromecast-82
+0080: 38 30 30 66 65 63 33 37 37 36 35 39 38 66 35 38 800fec3776598f58
+0090: 64 34 61 61 63 39 65 36 35 64 30 30 30 61 0b 5f d4aac9e65d000a._
+00a0: 67 6f 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 googlecast._tcp.
+00b0: 6c 6f 63 61 6c 00 00 10 80 01 00 00 11 94 00 a9 local...........
+00c0: 23 69 64 3d 38 32 38 30 30 66 65 63 33 37 37 36 #id=82800fec3776
+00d0: 35 39 38 66 35 38 64 34 61 61 63 39 65 36 35 64 598f58d4aac9e65d
+00e0: 30 30 30 61 13 72 6d 3d 36 36 39 42 39 34 34 38 000a.rm=669B9448
+00f0: 36 33 36 30 41 31 43 42 05 76 65 3d 30 35 0d 6d 6360A1CB.ve=05.m
+0100: 64 3d 43 68 72 6f 6d 65 63 61 73 74 12 69 63 3d d=Chromecast.ic=
+0110: 2f 73 65 74 75 70 2f 69 63 6f 6e 2e 70 6e 67 11 /setup/icon.png.
+0120: 66 6e 3d 43 68 72 6f 6d 65 63 61 73 74 36 35 30 fn=Chromecast650
+0130: 32 07 63 61 3d 34 31 30 31 04 73 74 3d 31 0f 62 2.ca=4101.st=1.b
+0140: 73 3d 46 41 38 46 43 41 39 45 33 45 30 31 1a 72 s=FA8FCA9E3E01.r
+0150: 73 3d 50 61 74 74 65 72 6e 20 67 65 6e 65 72 61 s=Pattern genera
+0160: 74 6f 72 20 72 65 61 64 79 2b 43 68 72 6f 6d 65 tor ready+Chrome
+0170: 63 61 73 74 2d 38 32 38 30 30 66 65 63 33 37 37 cast-82800fec377
+0180: 36 35 39 38 66 35 38 64 34 61 61 63 39 65 36 35 6598f58d4aac9e65
+0190: 64 30 30 30 61 0b 5f 67 6f 6f 67 6c 65 63 61 73 d000a._googlecas
+01a0: 74 04 5f 74 63 70 05 6c 6f 63 61 6c 00 00 21 80 t._tcp.local..!.
+01b0: 01 00 00 00 78 00 32 00 00 00 00 1f 49 24 38 32 ....x.2.....I$82
+01c0: 38 30 30 66 65 63 2d 33 37 37 36 2d 35 39 38 66 800fec-3776-598f
+01d0: 2d 35 38 64 34 2d 61 61 63 39 65 36 35 64 30 30 -58d4-aac9e65d00
+01e0: 30 61 05 6c 6f 63 61 6c 00 24 38 32 38 30 30 66 0a.local.$82800f
+01f0: 65 63 2d 33 37 37 36 2d 35 39 38 66 2d 35 38 64 ec-3776-598f-58d
+0200: 34 2d 61 61 63 39 65 36 35 64 30 30 30 61 05 6c 4-aac9e65d000a.l
+0210: 6f 63 61 6c 00 00 01 80 01 00 00 00 78 00 04 0a ocal........x...
+0220: 00 00 80 ...
+
+ChromeCast 3 (Ultra)
+
+0000: 00 00 84 00 00 00 00 01 00 00 00 03 0b 5f 67 6f ............._go
+0010: 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f oglecast._tcp.lo
+0020: 63 61 6c 00 00 0c 00 01 00 00 00 78 00 34 31 43 cal........x.41C
+0030: 68 72 6f 6d 65 63 61 73 74 2d 55 6c 74 72 61 2d hromecast-Ultra-
+0040: 32 38 36 65 36 63 63 65 38 34 65 32 38 65 61 33 286e6cce84e28ea3
+0050: 36 63 37 63 33 31 39 32 66 33 35 62 65 30 33 65 6c7c3192f35be03e
+0060: c0 0c c0 2e 00 10 80 01 00 00 11 94 00 8d 23 69 ..............#i
+0070: 64 3d 32 38 36 65 36 63 63 65 38 34 65 32 38 65 d=286e6cce84e28e
+0080: 61 33 36 63 37 63 33 31 39 32 66 33 35 62 65 30 a36c7c3192f35be0
+0090: 33 65 03 72 6d 3d 05 76 65 3d 30 35 13 6d 64 3d 3e.rm=.ve=05.md=
+00a0: 43 68 72 6f 6d 65 63 61 73 74 20 55 6c 74 72 61 Chromecast Ultra
+00b0: 12 69 63 3d 2f 73 65 74 75 70 2f 69 63 6f 6e 2e .ic=/setup/icon.
+00c0: 70 6e 67 16 66 6e 3d 43 68 72 6f 6d 65 63 61 73 png.fn=Chromecas
+00d0: 74 55 6c 74 72 61 36 32 35 30 07 63 61 3d 34 31 tUltra6250.ca=41
+00e0: 30 31 04 73 74 3d 30 0f 62 73 3d 46 41 38 46 43 01.st=0.bs=FA8FC
+00f0: 41 37 30 38 35 42 35 03 72 73 3d c0 2e 00 21 80 A7085B5.rs=...!.
+0100: 01 00 00 00 78 00 2d 00 00 00 00 1f 49 24 32 38 ....x.-.....I$28
+0110: 36 65 36 63 63 65 2d 38 34 65 32 2d 38 65 61 33 6e6cce-84e2-8ea3
+0120: 2d 36 63 37 63 2d 33 31 39 32 66 33 35 62 65 30 -6c7c-3192f35be0
+0130: 33 65 c0 1d c1 0d 00 01 80 01 00 00 00 78 00 04 3e...........x..
+0140: 0a 00 01 1d ....
+
+*/
+
+
+/* Other non-Google devices:
+
+Vizio M60-D1 :- Smart TV
+
+00000000 00 00 84 00 00 00 00 01 00 00 00 03 0b 5f 67 6f |............._go|
+00000010 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f |oglecast._tcp.lo|
+00000020 63 61 6c 00 00 0c 00 01 00 00 00 78 00 40 27 4d |cal........x.@'M|
+00000030 36 30 2d 44 31 2d 37 63 35 35 36 37 39 31 35 38 |60-D1-7c55679158|
+00000040 38 64 63 64 33 37 63 37 37 32 33 31 61 30 64 64 |8dcd37c77231a0dd|
+00000050 31 35 61 35 30 36 0b 5f 67 6f 6f 67 6c 65 63 61 |15a506._googleca|
+00000060 73 74 04 5f 74 63 70 05 6c 6f 63 61 6c 00 27 4d |st._tcp.local.'M|
+00000070 36 30 2d 44 31 2d 37 63 35 35 36 37 39 31 35 38 |60-D1-7c55679158|
+00000080 38 64 63 64 33 37 63 37 37 32 33 31 61 30 64 64 |8dcd37c77231a0dd|
+00000090 31 35 61 35 30 36 0b 5f 67 6f 6f 67 6c 65 63 61 |15a506._googleca|
+000000a0 73 74 04 5f 74 63 70 05 6c 6f 63 61 6c 00 00 10 |st._tcp.local...|
+000000b0 80 01 00 00 11 94 00 80 23 69 64 3d 37 63 35 35 |........#id=7c55|
+000000c0 36 37 39 31 35 38 38 64 63 64 33 37 63 37 37 32 |6791588dcd37c772|
+000000d0 33 31 61 30 64 64 31 35 61 35 30 36 13 72 6d 3d |31a0dd15a506.rm=|
+000000e0 31 33 39 46 33 32 34 34 38 34 44 46 41 39 32 34 |139F324484DFA924|
+000000f0 05 76 65 3d 30 35 09 6d 64 3d 4d 36 30 2d 44 31 |.ve=05.md=M60-D1|
+00000100 12 69 63 3d 2f 73 65 74 75 70 2f 69 63 6f 6e 2e |.ic=/setup/icon.|
+00000110 70 6e 67 0f 66 6e 3d 56 69 7a 69 6f 20 4d 36 30 |png.fn=Vizio M60|
+00000120 2d 44 31 07 63 61 3d 32 30 35 33 04 73 74 3d 30 |-D1.ca=2053.st=0|
+00000130 03 62 73 3d 03 72 73 3d 27 4d 36 30 2d 44 31 2d |.bs=.rs='M60-D1-|
+00000140 37 63 35 35 36 37 39 31 35 38 38 64 63 64 33 37 |7c556791588dcd37|
+00000150 63 37 37 32 33 31 61 30 64 64 31 35 61 35 30 36 |c77231a0dd15a506|
+00000160 0b 5f 67 6f 6f 67 6c 65 63 61 73 74 04 5f 74 63 |._googlecast._tc|
+00000170 70 05 6c 6f 63 61 6c 00 00 21 80 01 00 00 00 78 |p.local..!.....x|
+00000180 00 32 00 00 00 00 1f 49 24 37 63 35 35 36 37 39 |.2.....I$7c55679|
+00000190 31 2d 35 38 38 64 2d 63 64 33 37 2d 63 37 37 32 |1-588d-cd37-c772|
+000001a0 2d 33 31 61 30 64 64 31 35 61 35 30 36 05 6c 6f |-31a0dd15a506.lo|
+000001b0 63 61 6c 00 24 37 63 35 35 36 37 39 31 2d 35 38 |cal.$7c556791-58|
+000001c0 38 64 2d 63 64 33 37 2d 63 37 37 32 2d 33 31 61 |8d-cd37-c772-31a|
+000001d0 30 64 64 31 35 61 35 30 36 05 6c 6f 63 61 6c 00 |0dd15a506.local.|
+000001e0 00 01 80 01 00 00 00 78 00 04 c0 a8 00 3e |.......x.....>|
+000001ee
+
+SmartCast Sound Bar 3851-D0 :- Sound bar
+
+00000000 00 00 84 00 00 00 00 01 00 00 00 03 0b 5f 67 6f |............._go|
+00000010 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f |oglecast._tcp.lo|
+00000020 63 61 6c 00 00 0c 00 01 00 00 00 78 00 22 09 53 |cal........x.".S|
+00000030 42 33 38 35 31 2d 44 30 0b 5f 67 6f 6f 67 6c 65 |B3851-D0._google|
+00000040 63 61 73 74 04 5f 74 63 70 05 6c 6f 63 61 6c 00 |cast._tcp.local.|
+00000050 09 53 42 33 38 35 31 2d 44 30 0b 5f 67 6f 6f 67 |.SB3851-D0._goog|
+00000060 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f 63 61 |lecast._tcp.loca|
+00000070 6c 00 00 10 80 01 00 00 11 94 00 8a 23 69 64 3d |l...........#id=|
+00000080 64 35 36 63 33 66 30 39 36 66 30 31 30 31 36 66 |d56c3f096f01016f|
+00000090 33 64 37 30 65 62 62 63 36 32 36 34 65 66 33 31 |3d70ebbc6264ef31|
+000000a0 05 76 65 3d 30 34 1e 6d 64 3d 53 6d 61 72 74 43 |.ve=04.md=SmartC|
+000000b0 61 73 74 20 53 6f 75 6e 64 20 42 61 72 20 33 38 |ast Sound Bar 38|
+000000c0 35 31 2d 44 30 12 69 63 3d 2f 73 65 74 75 70 2f |51-D0.ic=/setup/|
+000000d0 69 63 6f 6e 2e 70 6e 67 0c 66 6e 3d 53 42 33 38 |icon.png.fn=SB38|
+000000e0 35 31 2d 44 30 07 63 61 3d 32 30 35 32 04 73 74 |51-D0.ca=2052.st|
+000000f0 3d 30 0f 62 73 3d 46 46 46 46 46 46 46 46 46 46 |=0.bs=FFFFFFFFFF|
+00000100 46 46 03 72 73 3d 09 53 42 33 38 35 31 2d 44 30 |FF.rs=.SB3851-D0|
+00000110 0b 5f 67 6f 6f 67 6c 65 63 61 73 74 04 5f 74 63 |._googlecast._tc|
+00000120 70 05 6c 6f 63 61 6c 00 00 21 80 01 00 00 00 78 |p.local..!.....x|
+00000130 00 17 00 00 00 00 1f 49 09 53 42 33 38 35 31 2d |.......I.SB3851-|
+00000140 44 30 05 6c 6f 63 61 6c 00 09 53 42 33 38 35 31 |D0.local..SB3851|
+00000150 2d 44 30 05 6c 6f 63 61 6c 00 00 01 80 01 00 00 |-D0.local.......|
+00000160 00 78 00 04 c0 a8 00 26 |.x.....&|
+00000168
+
+ZChromecast Toshiba :- TV ?
+
+00000000 00 00 84 00 00 00 00 01 00 00 00 03 0b 5f 67 6f |............._go|
+00000010 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f |oglecast._tcp.lo|
+00000020 63 61 6c 00 00 0c 00 01 00 00 00 78 00 2e 2b 43 |cal........x..+C|
+00000030 68 72 6f 6d 65 63 61 73 74 2d 64 61 30 33 34 62 |hromecast-da034b|
+00000040 31 39 30 30 34 34 62 63 38 65 35 33 30 31 34 37 |190044bc8e530147|
+00000050 63 37 61 65 33 32 63 35 61 33 c0 0c c0 2e 00 10 |c7ae32c5a3......|
+00000060 80 01 00 00 11 94 00 87 23 69 64 3d 64 61 30 33 |........#id=da03|
+00000070 34 62 31 39 30 30 34 34 62 63 38 65 35 33 30 31 |4b190044bc8e5301|
+00000080 34 37 63 37 61 65 33 32 63 35 61 33 03 72 6d 3d |47c7ae32c5a3.rm=|
+00000090 05 76 65 3d 30 35 0d 6d 64 3d 43 68 72 6f 6d 65 |.ve=05.md=Chrome|
+000000a0 63 61 73 74 12 69 63 3d 2f 73 65 74 75 70 2f 69 |cast.ic=/setup/i|
+000000b0 63 6f 6e 2e 70 6e 67 16 66 6e 3d 5a 43 68 72 6f |con.png.fn=ZChro|
+000000c0 6d 65 63 61 73 74 20 54 6f 73 68 69 62 61 07 63 |mecast Toshiba.c|
+000000d0 61 3d 34 31 30 31 04 73 74 3d 30 0f 62 73 3d 46 |a=4101.st=0.bs=F|
+000000e0 41 38 46 43 41 37 30 32 30 36 32 03 72 73 3d c0 |A8FCA702062.rs=.|
+000000f0 2e 00 21 80 01 00 00 00 78 00 2d 00 00 00 00 1f |..!.....x.-.....|
+00000100 49 24 64 61 30 33 34 62 31 39 2d 30 30 34 34 2d |I$da034b19-0044-|
+00000110 62 63 38 65 2d 35 33 30 31 2d 34 37 63 37 61 65 |bc8e-5301-47c7ae|
+00000120 33 32 63 35 61 33 c0 1d c1 01 00 01 80 01 00 00 |32c5a3..........|
+00000130 00 78 00 04 c0 a8 00 1b |.x......|
+00000138
*/
+/*
+
+ Can get info from Chromecast http server at http://XX.XX.XX.XX:8008/ssdp/device-desc.xml
+
+ Get info about the app running: http://XX.XX.XX.XX:8008/apps/ChromeCast
+
+*/
+
+