diff options
Diffstat (limited to 'ccast/ccmdns.c')
-rwxr-xr-x[-rw-r--r--] | ccast/ccmdns.c | 761 |
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 + +*/ + + |