//| Copyright: (C) 2020-2024 Kevin Larke //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file. #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwTest.h" #include "cwMem.h" #include "cwThread.h" #include "cwTcpSocket.h" #include "cwTcpSocketSrv.h" #include "cwMdns.h" #include "cwTime.h" namespace cw { namespace net { namespace mdns { typedef struct msg_str { uint16_t transactionId; uint16_t flags; uint16_t questionN; uint16_t answerN; uint16_t nameServerN; uint16_t additionalN; } msg_t; typedef struct question_str { char* name; uint16_t type; uint16_t clss; struct question_str* link; } question_t; typedef struct srv_rsrc_str { uint16_t priority; uint16_t weight; uint16_t port; char* target; } srv_rsrc_t; typedef struct rsrc_str { char* name; uint16_t type; uint16_t clss; uint32_t ttl; uint16_t dataByteN; union { char* text; srv_rsrc_t srv; uint32_t addr; }; struct rsrc_str* link; } rsrc_t; typedef struct mdns_str { rsrc_t* rsrcL; } mdns_t; typedef struct mdns_app_str { srv::handle_t mdnsH; socket::handle_t tcpH; thread::handle_t tcpThreadH; unsigned recvBufByteN; unsigned cbN; mdns_t mdns; unsigned protocolState; time::spec_t t0; unsigned txtXmtN; } mdns_app_t; void errorv( mdns_t* p, const char* fmt, va_list vl ) { printf("Error: "); vprintf(fmt,vl); } void logv( mdns_t* p, const char* fmt, va_list vl ) { vprintf(fmt,vl); fflush(stdout); } void error( mdns_t* p, const char* fmt, ... ) { va_list vl; va_start(vl,fmt); errorv(p,fmt,vl); va_end(vl); } void log( mdns_t* p, const char* fmt, ... ) { va_list vl; va_start(vl,fmt); logv(p,fmt,vl); va_end(vl); } enum { kInvalidRecdTId, kQuestionRecdTId, kAnswerRecdTId, kNameServerRecdTId, kAdditionalRecdTId }; enum { kA_DnsTId = 1, kPTR_DnsTId = 12, kTXT_DnsTId = 16, kAAAA_DnsTId = 28, kSRV_DnsTId = 33, kOPT_DnsTId = 41, kNSEC_DnsTId = 47, kANY_DnsTId = 255 // REMEMBER: Add new type id's to dnsTypeIdToString() }; enum { kHdrBodyByteN = 12, kQuestionBodyByteN = 4, kRsrcBodyByteN = 10, kABodyByteN = 4, kSrvBodyByteN = 6, kOptBodyByteN = 4, }; enum { kReplyHdrFl = 0x8000, kAuthoritativeHdrFl = 0x0400, kFlushClassFl = 0x8000, kInClassFl = 0x0001 }; const char* dnsTypeIdToString( uint16_t id ) { switch( id ) { case kA_DnsTId: return "A"; case kPTR_DnsTId: return "PTR"; case kTXT_DnsTId: return "TXT"; case kAAAA_DnsTId: return "AAAA"; case kSRV_DnsTId: return "SRV"; case kOPT_DnsTId: return "OPT"; case kNSEC_DnsTId: return "NSEC"; case kANY_DnsTId: return "ANY"; } return ""; } unsigned calc_msg_buf_byte_count( unsigned recdTId, const char* name, unsigned dnsTId, unsigned clss, unsigned ttl, unsigned numb0, const char* text, unsigned nextRecdTId, va_list vl ) { unsigned msgByteN = kHdrBodyByteN; // msg header bytes unsigned recdN = 0; while( true ) { // unsigned n0 = msgByteN; // record name bytes if( name[0] == ((char)0xc0) ) msgByteN += 2; else msgByteN += strlen(name) + 2; // add 1 for initial segment length and 1 for terminating zero if( recdTId == kQuestionRecdTId ) { msgByteN += kQuestionBodyByteN; } else { // resource record bytes msgByteN += kRsrcBodyByteN; switch( dnsTId ) { case kA_DnsTId: msgByteN += kABodyByteN; break; case kPTR_DnsTId: if( numb0 == 0 ) { if( text[0] == ((char)0xc0) ) msgByteN += 2; else msgByteN += strlen(text) + 1; } else { msgByteN += 2; } break; case kTXT_DnsTId: if( text[0] == ((char)0xc0) ) msgByteN += 2; else msgByteN += strlen(text) + 2; break; case kSRV_DnsTId: msgByteN += kSrvBodyByteN; if( text[0] == ((char)0xc0) ) msgByteN += 2; else msgByteN += strlen(text) + 1; break; default: assert(0); } } //printf("SIZE: %i %i\n", dnsTId, msgByteN-n0 ); recdTId = recdN==0 ? nextRecdTId : va_arg(vl,unsigned); if( recdTId == kInvalidRecdTId ) break; name = va_arg(vl,const char*); dnsTId = va_arg(vl,unsigned); clss = va_arg(vl,unsigned); ttl = va_arg(vl,unsigned); // not used numb0 = va_arg(vl,unsigned); // not used text = va_arg(vl,const char*); recdN += 1; } return msgByteN; } char* format_name( char* b, unsigned bN, const char* name, bool zeroTermFl=true, const char sepChar='.' ) { unsigned n = 0; unsigned j = 0; if( name[0] == ((char)0xc0) ) { assert( bN >= 2 ); b[0] = name[0]; b[1] = name[1]; return b + 1 + 1; } // for each input character for(unsigned i=0; true; ++i) { // if this char is a '.' or a '\0' then it is the end of a name segment if( name[i] == sepChar || name[i]==0 ) { assert( j < bN); b[j] = n; // write the length of the previous segment j = i+1; // advance j to the length cell of the next segments n = 0; // if this char is a '\0' then we are at the end of the input if( name[i] == 0 ) break; } else { n += 1; // advance the segment length counter assert( j+n < bN ); b[j+n] = name[i]; // write the current char to the output } } // terminate the output string if( zeroTermFl ) { assert( j < bN ); b[j] = 0; j += 1; } return b + j; // return a pointer just past the end of the output string } char* format_question( char* b, unsigned bN, const char* name, unsigned dnsTypeId ) { b = format_name(b,bN,name); uint16_t* u = (uint16_t*)b; u[0] = htons(dnsTypeId); u[1] = htons(kInClassFl); return b + kQuestionBodyByteN; } char* format_rsrc( char* b, unsigned bN, const char* name, unsigned typeId, unsigned clss, unsigned ttl, unsigned dataByteN ) { // u[0] u[1] u[2-3] u[4] // type class TTL dlen char* b1 = format_name(b,bN,name); uint16_t* u = (uint16_t*)b1; uint32_t* l = (uint32_t*)(u + 2); u[0] = htons(typeId); u[1] = htons(clss); l[0] = htonl(ttl); u[4] = htons(dataByteN); return b1 + kRsrcBodyByteN; } char* format_A_rsrc( char* b, unsigned bN, const char* name, unsigned clss, unsigned ttl, unsigned addr ) { char* b1 = format_rsrc( b, bN, name, kA_DnsTId, clss, ttl, kABodyByteN ); uint32_t* l = (uint32_t*)b1; l[0] = addr; //htonl(addr); return b1 + kABodyByteN; } char* format_PTR_rsrc( char* b, unsigned bN, const char* name, unsigned clss, unsigned ttl, const char* text, unsigned offset=0 ) { // u[0] u[1] u[2-3] u[4] u[5 ... ] // type class TTL dlen text unsigned dataByteN = strlen(text)+1; if( offset != 0 || text[0] == ((char)0xc0) ) dataByteN = 2; char* b1 = format_rsrc( b, bN, name, kPTR_DnsTId, clss, ttl, dataByteN ); assert( b < b1 ); unsigned n = b1-b; if( offset == 0 ) { b1 = format_name(b1,bN-n,text,false); } else { assert( bN - n >= 2 ); b1[0] = ((char)0xc0); b1[1] = offset; b1 += dataByteN; } return b1; } char* format_TXT_rsrc( char* b, unsigned bN, const char* name, unsigned clss, unsigned ttl, const char* text ) { // u[0] u[1] u[2-3] u[4] u[5 ... ] // type class TTL dlen text unsigned dataByteN = strlen(text)+1; char* b1 = format_rsrc( b, bN, name, kTXT_DnsTId, clss, ttl, dataByteN ); b1 = format_name(b1,bN-(b1-b),text,false,'\n'); return b1; } char* format_SRV_rsrc( char* b, unsigned bN, const char* name, unsigned clss, unsigned ttl, const char* text, unsigned port, unsigned priority=0, unsigned weight=0 ) { // u[0] u[1] u[2-3] u[4] u[5] u[6] u[7] u[8 ...] // type class TTL dlen pri weight port target unsigned dataByteN = kSrvBodyByteN; if( text[0] == ((char)0xc0)) dataByteN = 2; else dataByteN += strlen(text)+1; char* b1 = format_rsrc( b, bN, name, kSRV_DnsTId, clss, ttl, dataByteN+1 ); uint16_t* u = (uint16_t*)b1; u[0] = htons(priority); u[1] = htons(weight); u[2] = htons(port); b1 = format_name(b1 + kSrvBodyByteN,bN-((b1-b)+kSrvBodyByteN),text,true); return b1; } char* alloc_msgv( unsigned* msgByteNRef, uint16_t transactionId, uint16_t flags, unsigned recdTId, const char* name, unsigned dnsTId, unsigned clss, unsigned ttl, unsigned numb0, const char* text, unsigned nextRecdTId, va_list vl0 ) { va_list vl1; va_copy(vl1,vl0); unsigned byteN = calc_msg_buf_byte_count(recdTId,name,dnsTId,clss,ttl,numb0,text,nextRecdTId,vl1); va_end(vl1); if( msgByteNRef != nullptr ) *msgByteNRef = 0; char* buf = (char*)calloc(1,byteN); char* b0 = buf + kHdrBodyByteN; char* b1 = nullptr; int bN = byteN; uint16_t* u = (uint16_t*)buf; unsigned recdN = 0; // for each specified record while( true ) { // track the type of record switch( recdTId ) { case kQuestionRecdTId: u[2] += 1; break; case kAnswerRecdTId: u[3] += 1; break; case kNameServerRecdTId: u[4] += 1; break; case kAdditionalRecdTId: u[5] += 1; break; } // if this is a question record if( recdTId == kQuestionRecdTId ) { b1 = format_question( b0, bN, name, dnsTId ); } else { // select the resource record type to generate switch( dnsTId ) { case kA_DnsTId: b1 = format_A_rsrc( b0, bN, name, clss, ttl, numb0 ); break; case kPTR_DnsTId: b1 = format_PTR_rsrc(b0, bN, name, clss, ttl, text, numb0 ); break; case kTXT_DnsTId: b1 = format_TXT_rsrc(b0, bN, name, clss, ttl, text ); break; case kSRV_DnsTId: b1 = format_SRV_rsrc(b0, bN, name, clss, ttl, text, numb0 ); break; default: assert(0); } } //printf("FRMT: %i %li\n", dnsTId, b1-b0 ); bN -= (b1 - b0); // track the count of remaing bytes in the buffer assert(bN >= 0); // assert the buffer is not already full b0 = b1; // update the current buffer output pointer // get the next record type recdTId = recdN==0 ? nextRecdTId : va_arg(vl0,unsigned); // detect the end of records sentinel if( recdTId == kInvalidRecdTId ) break; // get the arguments for the next record name = va_arg(vl0,const char*); dnsTId = va_arg(vl0,unsigned); clss = va_arg(vl0,unsigned); ttl = va_arg(vl0,unsigned); numb0 = va_arg(vl0,unsigned); // not used text = va_arg(vl0,const char*); recdN += 1; } // Note that the buffer should be exactly full when all data is written. // If this is not true then either the buffer size calculation or // the buffer serialization code is incorrect. // BUG BUG BUG // BUG BUG BUG: see comment in send_txt() for reason that this check is turned off // BUG BUG BUG //assert( bN == kHdrBodyByteN ); if( msgByteNRef != nullptr ) *msgByteNRef = byteN; // convert the record counts to the network endianess u[0] = htons(transactionId); u[1] = htons(flags); u[2] = htons(u[2]); u[3] = htons(u[3]); u[4] = htons(u[4]); u[5] = htons(u[5]); return buf; } char* alloc_msg( unsigned* msgByteNRef, uint16_t transactionId, uint16_t flags, unsigned recdTId, const char* name, unsigned dnsTId, unsigned clss, unsigned ttl, unsigned numb0, const char* text, unsigned nextRecdTId, ... ) { va_list vl; va_start(vl,nextRecdTId); char* b = alloc_msgv( msgByteNRef, transactionId, flags, recdTId, name, dnsTId, clss, ttl, numb0, text, nextRecdTId, vl ); va_end(vl); return b; } unsigned calc_ptr_string_byte_count( const char* b, bool dotFl ) { unsigned n = 0; unsigned i = 0; // terminate when a zero or another ptr string is encountered while( b[i] != 0 && (b[i] & 0xc0) != 0xc0) { // TODO: what if this is a 'ptr' ... getting the length of a pointer string may require a recursive function? n += b[i] + (dotFl ? 1 : 0); i += b[i] + 1; dotFl = true; } return n; } unsigned calc_name_byte_count( mdns_t* p, const char* base, const char* b, unsigned maxSrcByteN, unsigned* strLenRef=nullptr, bool logFl=true ) { if( strLenRef != nullptr ) *strLenRef = 0; // Number of bytes required to represent the uncompressed string // (including the segment size bytes but not the terminating zero) unsigned strByteN = 0; unsigned segN = 0; // count of segments the name is formed from unsigned i = 0; while( maxSrcByteN ==0 || i < maxSrcByteN ) { // if this a pointer if( (b[i] & 0xc0) == 0xc0 ) { // TODO check for going past buffer before add 1 to index unsigned short offset = b[i] & 0x3f; offset = (offset<<8) + ((unsigned char)b[i+1]); strByteN += calc_ptr_string_byte_count( base + offset, i!=0) + 1; if( logFl ) log(p,"%.*s.", base[offset], base + offset + 1 ); i += 2; segN += 1; break; // ptr terminates the name } else { if( b[i] == 0 ) { ++i; strByteN += 1; break; // zero terminates the name } if( logFl ) log(p,"%.*s.", b[i], b+i+1 ); strByteN += b[i] + (i==0 ? 0 : 1); i += b[i] + 1; segN += 1; } } if( maxSrcByteN != 0 and i > maxSrcByteN ) { // we came to the end of a name without a zero or ptr this is a malformed packet error(p,"Malformed name."); return -1; } if( strLenRef != nullptr ) *strLenRef = strByteN; // add one for terminating zero return i; // i is the count of byte used by the name in the packet buffer } // name[0] must be the length of the segment. // Returns the count of bytes written to buf. unsigned copy_out_segment( const char* name, char* buf, unsigned bufN, bool dotFl ) { // get segment length unsigned n = name[0]; unsigned i = 0; if( dotFl ) { assert( bufN > 0 ); buf[0] = '.'; i += 1; bufN -= 1; } assert( n <= bufN ); strncpy(buf + i, name+1, n ); return i + n; } // Return the count of bytes written to buf[]. unsigned get_ptr_name( const char* name, char* buf, unsigned bufByteN, bool dotFl ) { unsigned i = 0; unsigned N = 0; // terminate when a zero or another ptr string is encountered while( name[i] != 0 && (name[i] & 0xc0) != 0xc0) { unsigned n = copy_out_segment( name + i, buf, bufByteN, dotFl ); buf += n; bufByteN -= n; dotFl = true; i += name[i] + 1; N += n; } return N; } void get_name( const char* b, const char* base, char* buf, unsigned bufByteN ) { unsigned i = 0; while( true ) { // if this a pointer if( (b[i] & 0xc0) == 0xc0 ) { // TODO check for going past buffer before add 1 to index unsigned short offset = b[i] & 0x3f; offset = (offset<<8) + ((unsigned char)b[i+1]); unsigned n = get_ptr_name( base + offset, buf, bufByteN, i!=0 ); bufByteN -= n; buf += n; i += 2; break; // ptr terminates the name } else { if( b[i] == 0 ) { break; // zero terminates the name } unsigned n = copy_out_segment( b + i, buf, bufByteN, i!=0 ); bufByteN -= n; buf += n; i += b[i] + 1; } } assert( bufByteN >= 1 ); buf[0] = 0; bufByteN -= 1; } unsigned resource_recd_byte_count( mdns_t* p, const char* base, const char* b, unsigned bN ) { unsigned nameN = calc_name_byte_count( p, base, b, bN, nullptr, false ); uint16_t* u = (uint16_t*)(b + nameN); return nameN + 10 + ntohs(u[4]); } const char* parse_A_recd( mdns_t* p, const char* base, const char* b, unsigned byteN ) { assert( byteN >= kABodyByteN ); unsigned addr = ntohl( *(unsigned *)b ); log(p,"0x%04x inet addr", addr ); return b + 4; } const char* parse_PTR_recd( mdns_t* p, const char* base, const char* b, unsigned byteN ) { unsigned nameN = calc_name_byte_count( p, base, b, byteN ); return b + nameN; } const char* parse_TXT_recd( mdns_t* p, const char* base, const char* b, unsigned byteN ) { unsigned i =0; while( i(buf); const uint16_t* hdr = static_cast(buf); uint16_t transId = ntohs(hdr[0]); uint16_t flags = ntohs(hdr[1]); uint16_t questionN = ntohs(hdr[2]); uint16_t answerN = ntohs(hdr[3]); uint16_t nameSrvN = ntohs(hdr[4]); uint16_t addN = ntohs(hdr[5]); log(p,"*** Msg: id:0x%04x flags:0x%04x qN:%i aN:%i nsN:%i addN:%i\n", transId, flags, questionN, answerN, nameSrvN,addN); const char* b0 = (const char*)(hdr + 6); const char* b1 = nullptr; int bN = byteN - (b0 - base); if((b1 = parse_msg_segment( p, parse_question, questionN, base, b0, bN )) == nullptr ) goto errLabel; bN -= b1 - b0; b0 = b1; if((b1 = parse_msg_segment( p, parse_answer, answerN, base, b0, bN )) == nullptr ) goto errLabel; bN -= b1 - b0; b0 = b1; if((b1 = parse_msg_segment( p, parse_name_server, nameSrvN, base, b0, bN )) == nullptr ) goto errLabel; bN -= b1 - b0; b0 = b1; if((b1 = parse_msg_segment( p, parse_additional, addN, base, b0, bN )) == nullptr ) goto errLabel; errLabel: return 0; } void print_hex( const char* buf, unsigned dataByteCnt ) { unsigned char* data = (unsigned char*)buf; const unsigned colN = 8; unsigned ci = 0; for(unsigned i=0; imdnsH), buf, bufByteN-1, "224.0.0.251", 5353 ); free(buf); return rc; } void udpReceiveCallback( void* arg, const void* data, unsigned dataByteCnt, const struct sockaddr_in* fromAddr ) { mdns_app_t* p = static_cast(arg); char addrBuf[ INET_ADDRSTRLEN ]; socket::addrToString( fromAddr, addrBuf, INET_ADDRSTRLEN ); p->cbN += 1; if( false ) { printf("%i bytes:%i %s\n", p->cbN, dataByteCnt, addrBuf ); print_hex( (const char*)data, dataByteCnt ); parse_msg(&p->mdns,data,dataByteCnt); } if( dataByteCnt > 0 ) { uint16_t* u = (uint16_t*)data; uint8_t* b = (uint8_t*)data; if( u[1]==0 && u[2] == 1 && b[12] == 0x0a ) { printf("dataByteCnt:%i\n",dataByteCnt); if( dataByteCnt == 51 ) printf("MATCH!\n"); } } } rc_t send_response1( mdns_app_t* app, socket::handle_t sockH ) { rc_t rc = kOkRC; // send_response( app, sockH ); // wifi: 98 5A EB 89 BA AA // enet: 38 C9 86 37 44 E7 unsigned char buf[] = { 0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x00,0x02,0x03,0xfc,0x01,0x05, 0x06,0x00, 0x38,0xc9,0x86,0x37,0x44,0xe7, 0x01,0x00, 0xc0,0xa8,0x00,0x44, 0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x03,0xff,0x00,0x30,0x08,0x00,0x00,0x80,0x00,0x40,0x01,0x01,0x00,0x00,0x00,0x00, 0x00,0x00 }; unsigned bufByteN = sizeof(buf); if((rc = socket::send( sockH, buf, bufByteN )) != kOkRC ) { error(&app->mdns,"Send failed."); } return rc; } rc_t send_response2( mdns_app_t* app, socket::handle_t sockH ) { rc_t rc = kOkRC; unsigned char buf[] = { 0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x08 }; unsigned bufByteN = sizeof(buf); if((rc = socket::send( sockH, buf, bufByteN )) != kOkRC ) { error(&app->mdns,"Send failed."); } return rc; } rc_t send_heart_beat( mdns_app_t* app, socket::handle_t sockH ) { rc_t rc = kOkRC; unsigned char buf[] = { 0x03,0x00,0x00,0x00 }; unsigned bufByteN = sizeof(buf); if((rc = socket::send( sockH, buf, bufByteN )) != kOkRC ) { error(&app->mdns,"Send failed."); } return rc; } bool tcpReceiveCallback( void* arg ) { mdns_app_t* app = static_cast(arg); socket::handle_t sockH = app->tcpH; char buf[ app->recvBufByteN ]; unsigned readByteN = 0; rc_t rc = kOkRC; time::spec_t t1; if( !socket::isConnected(sockH) ) { if((rc = socket::accept( sockH )) == kOkRC ) { printf("TCP connected.\n"); } } else { if((rc = socket::receive( sockH, buf, app->recvBufByteN, &readByteN, nullptr )) == kOkRC || rc == kTimeOutRC ) { if( rc == kTimeOutRC ) { } else if( readByteN > 0 ) { unsigned* h = (unsigned*)buf; unsigned id = h[0]; switch( app->protocolState ) { case 0: if( id == 10 ) { send_response1(app,sockH); sleepMs(20); send_heart_beat(app,sockH); app->protocolState+=1; } break; case 1: { if( buf[0] == 0x0c ) { send_response2(app,sockH); app->protocolState+=1; time::get(app->t0); } } break; case 2: { time::get(t1); if( time::elapsedMs( app->t0, t1 ) >= 4000 ) { send_heart_beat(app,sockH); app->t0 = t1; } } break; } } } } return true; } rc_t sendMsg1( mdns_app_t* p ) { rc_t rc = kOkRC; unsigned transId = 0; unsigned bufByteN = 0; unsigned flags = 0; unsigned ttl = 120; socket::handle_t sockH = srv::socketHandle(p->mdnsH); struct sockaddr_in addr; if((rc = socket::initAddr( "192.168.0.68", 4325, &addr )) != kOkRC ) { error(&p->mdns,"Get inet address failed."); goto errLabel; } else { // wifi: 98 5A EB 89 BA AA "985AEB89BAAA" 98-5A-EB-89-BA-AA // enet: 38 C9 86 37 44 E7 "38C9863744E7" 38-C9-86-37-44-E7 char* buf = alloc_msg( &bufByteN, transId, flags, kQuestionRecdTId, "68.0.168.192.in-addr.arpa", kANY_DnsTId, kInClassFl, 0, 0, nullptr, kQuestionRecdTId, "Euphonix-MC-38C9863744E7.local", kANY_DnsTId, kInClassFl, 0, 0, nullptr, kNameServerRecdTId, "Euphonix-MC-38C9863744E7.local", kA_DnsTId, kInClassFl, ttl, addr.sin_addr, nullptr, kNameServerRecdTId, "68.0.168.192.in-addr.arpa", kPTR_DnsTId, kInClassFl, ttl, 43, "Euphonix-MC-38C9863744E7.local", kInvalidRecdTId ); //print_hex(buf,bufByteN); //parse_msg( nullptr, buf, bufByteN ); send( sockH, buf, bufByteN, "224.0.0.251", 5353 ); free(buf); } errLabel: return rc; } rc_t sendMsg2( mdns_app_t* p ) { rc_t rc = kOkRC; unsigned transId = 0; unsigned bufByteN = 0; unsigned flags = 0x8400; socket::handle_t sockH = srv::socketHandle(p->mdnsH); struct sockaddr_in addr; if((rc = socket::initAddr( "192.168.0.68", 4325, &addr )) != kOkRC ) { error(&p->mdns,"Get inet address failed."); goto errLabel; } else { /* THIS MESSAGE IS NOT NECESSARY char* buf0 = alloc_msg( &bufByteN, transId, 0, kQuestionRecdTId, "MC Mix - 1._EuConProxy._tcp.local", kANY_DnsTId, kInClassFl, 0, 0, nullptr, kNameServerRecdTId, "\xc0\x0c", kSRV_DnsTId, kInClassFl, 120, 49168, "Euphonix-MC-38C9863744E7.local", kNameServerRecdTId, "\xc0\x0c", kTXT_DnsTId, kInClassFl, 4500, 0, "lmac=38-C9-86-37-44-E7\ndummy=0", kInvalidRecdTId ); //print_hex(buf0,bufByteN); send( sockH, buf0, bufByteN, "224.0.0.251", 5353 ); free(buf0); sleepMs(500); */ bufByteN = 0; char* buf = alloc_msg( &bufByteN, transId, flags, kAnswerRecdTId, "MC Mix - 1._EuConProxy._tcp.local", kSRV_DnsTId, kFlushClassFl | kInClassFl, 120, 49168, "Euphonix-MC-38C9863744E7.local", kAnswerRecdTId, "\xc0\x3f", kA_DnsTId, kFlushClassFl | kInClassFl, 120, addr.sin_addr, nullptr, kAnswerRecdTId, "\xc0\x17", kPTR_DnsTId, kInClassFl, 4500, 0, "\xc0\x0c", kAnswerRecdTId, "\xc0\x0c", kTXT_DnsTId, kFlushClassFl | kInClassFl, 4500, 0, "lmac=38-C9-86-37-44-E7\ndummy=1", kAnswerRecdTId, "_services._dns-sd._udp.local", kPTR_DnsTId, kInClassFl, 4500, 0, "\xc0\x17", kInvalidRecdTId ); /* char* buf = alloc_msg( &bufByteN, transId, flags, kAnswerRecdTId, "MC Mix._EuConProxy._tcp.local", kSRV_DnsTId, kFlushClassFl | kInClassFl, 120, 49168, "Euphonix-MC-38C9863744E7.local", kAnswerRecdTId, "\xc0\x3b", kA_DnsTId, kFlushClassFl | kInClassFl, 120, addr.sin_addr, nullptr, kAnswerRecdTId, "\xc0\x13", kPTR_DnsTId, kInClassFl, 4500, 0, "\xc0\x0c", kAnswerRecdTId, "\xc0\x0c", kTXT_DnsTId, kFlushClassFl | kInClassFl, 4500, 0, "lmac=38-C9-86-37-44-E7\ndummy=1", kAnswerRecdTId, "_services._dns-sd._udp.local", kPTR_DnsTId, kInClassFl, 4500, 0, "\xc0\x13", kInvalidRecdTId ); */ //print_hex(buf,bufByteN); //parse_msg( nullptr, buf, bufByteN ); send( sockH, buf, bufByteN, "224.0.0.251", 5353 ); free(buf); } errLabel: return rc; } void testAllocMsg( const char* tag ) { unsigned bufByteN = 0; unsigned transId = 0; unsigned flags = 0; unsigned ttl = 120; char* buf = alloc_msg( &bufByteN, transId, flags, kQuestionRecdTId, "80.0.168.192.in-addr.arpa", kANY_DnsTId, kInClassFl, 0, 0, nullptr, kQuestionRecdTId, "Euphonix-MC-0090D580F4DE.local", kANY_DnsTId, kInClassFl, 0, 0, nullptr, kNameServerRecdTId, "Euphonix-MC-0090D580F4DE.local", kA_DnsTId, kInClassFl, ttl, 49168, nullptr, kNameServerRecdTId, "80.0.168.192.in-addr.arpa", kPTR_DnsTId, kInClassFl, ttl, 0, "in-addr.arpa", kInvalidRecdTId ); print_hex( buf, bufByteN ); parse_msg( nullptr, buf, bufByteN ); free(buf); buf = alloc_msg( &bufByteN, transId, flags, kQuestionRecdTId, "MC Mix._EuConProxy._tcp.local", kANY_DnsTId, kInClassFl, 0, 0, nullptr, kNameServerRecdTId,"Euphonix-MC-0090D580F4DE.local", kSRV_DnsTId, kInClassFl, 120, 49168, "local", kInvalidRecdTId ); print_hex( buf, bufByteN ); parse_msg( nullptr, buf, bufByteN ); free(buf); buf = alloc_msg( &bufByteN, transId, kReplyHdrFl | kAuthoritativeHdrFl, kAnswerRecdTId, "MC Mix - 1._EuConProxy._tcp.local", kTXT_DnsTId, kFlushClassFl | kInClassFl, 0, 0, "lmac=00-90-D5-80-F4-DE\ndummy=0", kInvalidRecdTId ); print_hex( buf, bufByteN ); parse_msg( nullptr, buf, bufByteN ); free(buf); } } } } cw::rc_t cw::net::mdns::test() { rc_t rc; socket::portNumber_t mdnsPort = 5353; socket::portNumber_t tcpPort = 49168; unsigned udpTimeOutMs = 50; // if timeOutMs==0 server uses recv_from() unsigned tcpTimeOutMs = 50; const unsigned sbufN = 31; char sbuf[ sbufN+1 ]; mdns_app_t app; app.cbN = 0; app.recvBufByteN = 4096; app.protocolState = 0; app.txtXmtN = 0; // create the mDNS UDP socket server if((rc = srv::create( app.mdnsH, mdnsPort, socket::kNonBlockingFl | socket::kReuseAddrFl | socket::kReusePortFl | socket::kMultiCastTtlFl | socket::kMultiCastLoopFl, srv::kUseRecvFromFl, udpReceiveCallback, &app, app.recvBufByteN, udpTimeOutMs, NULL, socket::kInvalidPortNumber )) != kOkRC ) { return cwLogError(rc,"mDNS UDP socket create failed."); } // add the mDNS socket to the multicast group if((rc = join_multicast_group( socketHandle(app.mdnsH), "224.0.0.251" )) != kOkRC ) goto errLabel; // set the TTL for multicast if((rc = set_multicast_time_to_live( socketHandle(app.mdnsH), 255 )) != kOkRC ) goto errLabel; // create the TCP socket if((rc = socket::create( app.tcpH, tcpPort, socket::kTcpFl | socket::kBlockingFl | socket::kStreamFl | socket::kListenFl, tcpTimeOutMs, NULL, socket::kInvalidPortNumber )) != kOkRC ) { rc = cwLogError(rc,"mDNS TCP socket create failed."); goto errLabel; } // create the TCP listening thread if((rc = thread::create( app.tcpThreadH, tcpReceiveCallback, &app, "mdns" )) != kOkRC ) goto errLabel; // start the mDNS socket server if((rc = srv::start( app.mdnsH )) != kOkRC ) goto errLabel; // start the tcp thread if((rc = thread::unpause( app.tcpThreadH )) != kOkRC ) goto errLabel; while( true ) { printf("? "); if( std::fgets(sbuf,sbufN,stdin) == sbuf ) { if( strcmp(sbuf,"msg0\n") == 0 ) { //testAllocMsg(sbuf); send_txt(&app); break; } if( strcmp(sbuf,"msg1\n") == 0 ) { sendMsg1( &app ); } if( strcmp(sbuf,"msg2\n") == 0 ) { sendMsg2( &app ); } if( strcmp(sbuf,"quit\n") == 0) break; } } errLabel: // close the mDNS server rc_t rc0 = destroy(app.mdnsH); rc_t rc1 = thread::destroy(app.tcpThreadH); rc_t rc2 = socket::destroy(app.tcpH); return rcSelect(rc,rc0,rc1,rc2); }