diff --git a/dns_sd/dns_sd.cpp b/dns_sd/dns_sd.cpp new file mode 100644 index 0000000..3c676ee --- /dev/null +++ b/dns_sd/dns_sd.cpp @@ -0,0 +1,307 @@ +#include "dns_sd.h" +#include +#include +#include +#include +#include "dns_sd_const.h" +#include "dns_sd_print.h" + + +#define DNS_SD_SERVICE_TYPE_STRING "_services._dns-sd._udp" + +dns_sd::dns_sd(sendCallback_t sendCbFunc, void* sendCbArg ) + : _sendCbFunc(sendCbFunc),_sendCbArg(sendCbArg),_serviceName(nullptr),_serviceType(nullptr),_serviceDomain(nullptr),_hostName(nullptr),_hostPort(0),_text(nullptr) +{} + +dns_sd::dns_sd( sendCallback_t sendCbFunc, void* sendCbArg, const char* serviceName, const char* serviceType, const char* serviceDomain, const char* hostName, uint32_t hostAddr, uint16_t hostPort, const char* text ) + : _sendCbFunc(sendCbFunc),_sendCbArg(sendCbArg),_serviceName(nullptr),_serviceType(nullptr),_serviceDomain(nullptr),_hostName(nullptr),_hostPort(0),_text(nullptr) +{ + setup( serviceName, serviceType, serviceDomain, hostName, hostAddr, hostPort, text ); +} + +dns_sd::~dns_sd() +{ + _free(); +} + +dns_sd::result_t dns_sd::setup( const char* serviceName, const char* serviceType, const char* serviceDomain, const char* hostName, uint32_t hostAddr, uint16_t hostPort, const char* text ) +{ + _free(); + + _serviceName = strdup(serviceName); + _serviceType = strdup(serviceType); + _serviceDomain = strdup(serviceDomain); + _hostName = strdup(hostName); + _hostAddr = hostAddr; + _text = strdup(text); + _hostPort = hostPort; + + return kOkRC; +} + +dns_sd::result_t dns_sd::receive( const void* buf, unsigned bufByteN ) +{ + _parse((const char*)buf,bufByteN); + return kOkRC; +} + +void dns_sd::gen_question() +{ + unsigned n = _calc_question_byte_count(); + unsigned char *b = (unsigned char*)calloc(1,n); + _format_question(b, n); + _send(b,n); + free(b); + +} + +void dns_sd::gen_response() +{ + unsigned n = _calc_response_byte_count(); + unsigned char* b = (unsigned char*)calloc(1,n); + _format_response(b, n); + _send(b,n); + free(b); +} + +void dns_sd::_free() +{ + if( _serviceName ) free( _serviceName ); + if( _serviceType ) free( _serviceType ); + if( _serviceDomain) free( _serviceDomain ); + if( _hostName) free( _hostName ); + if( _text ) free( _text ); +} + +unsigned dns_sd::_calc_question_byte_count() +{ + unsigned n = kHdrBodyByteN; + n += 1 + strlen(_serviceName) + 1 + strlen(_serviceType) + 1 + strlen(_serviceDomain) + 1 + kQuestionBodyByteN; + n += 2 + kRsrcBodyByteN + kSrvBodyByteN + 1 + strlen(_hostName) + 2; + //n += 2 + kRsrcBodyByteN + strlen(_text) + 1; + return n; +} + +void dns_sd::_format_question( unsigned char* buf, unsigned bufByteN ) +{ + assert( bufByteN > kHdrBodyByteN ); + unsigned char* bend = buf + bufByteN; + + uint16_t* u = (uint16_t*)buf; + u[0] = htons(0); // transaction id + u[1] = htons(0); // flags + u[2] = htons(1); // question + u[3] = htons(0); // answer + u[4] = htons(1); // name server + u[5] = htons(0); // other + + unsigned char* b = (unsigned char*)(u + 6); + + // Format question + unsigned char namePtr[] = {0xc0, (unsigned char)(b - buf)}; + b = _write_name( b, bend, _serviceName ); + + //unsigned char typePtr[] = { 0xc0, (unsigned char)(b - buf)}; + b = _write_name( b, bend, _serviceType ); + + unsigned char domainPtr[] = { 0xc0, (unsigned char)(b - buf) }; + b = _write_name( b, bend, _serviceDomain, true ); + + b = _write_uint16( b, bend, kANY_DnsTId ); + b = _write_uint16( b, bend, kInClassDnsFl ); + + // Format SRV name server + b = _write_ptr( namePtr, b, bend ); // name + b = _write_uint16( b, bend, kSRV_DnsTId ); // type + b = _write_uint16( b, bend, kInClassDnsFl ); // class + b = _write_uint32( b, bend, 120 ); // TTL + b = _write_uint16( b, bend, kSrvBodyByteN + strlen(_hostName) + 1 + 2 ); + b = _write_uint16( b, bend, 0 ); // priority + b = _write_uint16( b, bend, 0 ); // weight + b = _write_uint16( b, bend, _hostPort ); // port + b = _write_text( b, bend, _hostName ); // host + b = _write_ptr( b, bend, domainPtr ); // host suffix (.local) + /* + // Format TXT name server + b = _write_ptr( namePtr, b, bend ); // name + b = _write_uint16( kTXT_DnsTId, b, bend ); // type + b = _write_uint16( kInClassDnsFl, b, bend ); // class + b = _write_uint32( 4500, b, bend ); // TTL + b = _write_uint16( strlen(_text),b, bend ); // dlen + b = _write_text( _text, b, bend ); // text + */ + assert( b == bend ); +} + +unsigned char* dns_sd::_write_uint16( unsigned char* b, unsigned char* bend, uint16_t value ) +{ + assert( bend - b >= (int)sizeof(value)); + uint16_t* u = (uint16_t*)b; + u[0] = htons(value); + return (unsigned char*)(u + 1); +} + +unsigned char* dns_sd::_write_uint32( unsigned char* b, unsigned char* bend, uint32_t value ) +{ + assert( bend - b >= (int)sizeof(value)); + uint32_t* u = (uint32_t*)b; + u[0] = htonl(value); + return (unsigned char*)(u + 1); +} + +unsigned char* dns_sd::_write_ptr( unsigned char* b, unsigned char* bend, const unsigned char ptr[2] ) +{ + assert( (ptr[0] & 0xc0) == 0xc0 ); + b[0] = ptr[0]; + b[1] = ptr[1]; + return b+2; +} + +unsigned char* dns_sd::_write_text( unsigned char* b, unsigned char* bend, const char* name ) +{ return _write_name( b, bend, name, false, '\n' ); } + +unsigned char* dns_sd::_write_name( unsigned char* b, unsigned char* bend, const char* name, bool zeroTermFl, const unsigned char eosChar ) +{ + unsigned char* b0 = b; // segment length prefix pointer + unsigned n = 0; // segment length + + + b += 1; // advance past the first segment length byte + + // for every name char advance both the src and dst location + for(; *name; ++name, ++b ) + { + if( *name == eosChar ) + { + *b0 = n; // write the segment length + n = 0; // reset the segment length counter + b0 = b; // reset the segment length prefix pointer + } + else + { + assert( b < bend ); + *b = *name; // write a name character + ++n; // advance the segment length counter + } + } + + *b0 = n; // write the segment length of the last segment + + if( zeroTermFl ) + { + assert( b < bend ); + *b = 0; // write the zero termination + b += 1; // + } + + return b; +} + + +unsigned dns_sd::_calc_response_byte_count() +{ + unsigned n = kHdrBodyByteN; + + // TXT + n += 1 + strlen(_serviceName) + 1 + strlen(_serviceType) + 1 + strlen(_serviceDomain) + 1 + kRsrcBodyByteN + 1 + strlen(_text); + + // PTR + n += 2 + kRsrcBodyByteN + 2; + + + // SRV + n += 2 + kRsrcBodyByteN + kSrvBodyByteN + 1 + strlen(_hostName) + 2; + + // A + n += 2 + kRsrcBodyByteN + kABodyByteN; + + // PTR + n += 1 + strlen(DNS_SD_SERVICE_TYPE_STRING) + 2 + kRsrcBodyByteN + 2; + + return n; +} + +void dns_sd::_format_response( unsigned char* buf, unsigned bufByteN ) +{ + unsigned char* bend = buf + bufByteN; + uint16_t* u = (uint16_t*)buf; + + u[0] = htons(0); // transaction id + u[1] = htons(0x8400); // flags + u[2] = htons(0); // question + u[3] = htons(5); // answer + u[4] = htons(0); // name server + u[5] = htons(0); // other + + unsigned char* b = (unsigned char*)(u+6); + + // Format TXT resource record + unsigned char namePtr[] = { 0xc0, (unsigned char)(b-buf) }; + b = _write_name( b, buf+bufByteN, _serviceName ); + + unsigned char typePtr[] = { 0xc0, (unsigned char)(b - buf) }; + b = _write_name( b, bend, _serviceType ); + + unsigned char domainPtr[] = { 0xc0, (unsigned char)(b - buf) }; + b = _write_name( b, bend, _serviceDomain, true ); + + + b = _write_uint16( b, bend, kTXT_DnsTId ); // type + b = _write_uint16( b, bend, kFlushClassDnsFl | kInClassDnsFl ); // class + b = _write_uint32( b, bend, 4500 ); // TTL + b = _write_uint16( b, bend, strlen(_text)+1 ); // dlen + b = _write_text( b, bend, _text ); // text + + // Format a PTR resource record + b = _write_ptr( b, bend, typePtr ); + b = _write_uint16( b, bend, kPTR_DnsTId ); + b = _write_uint16( b, bend, kInClassDnsFl ); + b = _write_uint32( b, bend, 4500 ); + b = _write_uint16( b, bend, 2 ); + b = _write_ptr( b, bend, namePtr ); + + // Format SRV response + b = _write_ptr( b, bend, namePtr ); // name + b = _write_uint16( b, bend, kSRV_DnsTId ); // type + b = _write_uint16( b, bend, kFlushClassDnsFl | kInClassDnsFl ); // class + b = _write_uint32( b, bend, 120 ); // TTL + b = _write_uint16( b, bend, kSrvBodyByteN + 1 + strlen(_hostName) + 2 ); + b = _write_uint16( b, bend, 0 ); // priority + b = _write_uint16( b, bend, 0 ); // weight + b = _write_uint16( b, bend, _hostPort ); // port + unsigned char hostPtr[] = { 0xc0, (unsigned char)(b - buf) }; + b = _write_text( b, bend, _hostName ); // target + b = _write_ptr( b, bend, domainPtr ); + + // Format A resource record + b = _write_ptr( b, bend, hostPtr ); + b = _write_uint16( b, bend, kA_DnsTId ); // type + b = _write_uint16( b, bend, kFlushClassDnsFl | kInClassDnsFl ); // class + b = _write_uint32( b, bend, 120 ); // TTL + b = _write_uint16( b, bend, kABodyByteN ); + b = _write_uint32( b, bend, _hostAddr ); // priority + + // Format a PTR resource record + b = _write_name( b, bend, DNS_SD_SERVICE_TYPE_STRING ); + b = _write_ptr( b, bend, domainPtr ); + b = _write_uint16( b, bend, kPTR_DnsTId ); + b = _write_uint16( b, bend, kInClassDnsFl ); + b = _write_uint32( b, bend, 4500 ); + b = _write_uint16( b, bend, 2 ); + b = _write_ptr( b, bend, typePtr ); + + + printf("%li %li : %s\n", b - buf, bend - buf, _text ); +} + + +void dns_sd::_parse( const char* buf, unsigned bufByteN ) +{ + dns_sd_print(buf,bufByteN); +} + +void dns_sd::_send( const void* buf, unsigned bufByteN ) +{ + if( _sendCbFunc != nullptr ) + _sendCbFunc(_sendCbArg,buf,bufByteN); +} diff --git a/dns_sd/dns_sd.h b/dns_sd/dns_sd.h new file mode 100644 index 0000000..85cd420 --- /dev/null +++ b/dns_sd/dns_sd.h @@ -0,0 +1,69 @@ +#ifndef dns_sd_h +#define dns_sd_h + +#include +#include + +class dns_sd +{ +public: + typedef enum + { + kOkRC + } result_t; + + typedef void (*sendCallback_t)( void* arg, const void* buf, unsigned bufByteN ); + + dns_sd( sendCallback_t sendCbFunc, void* sendCbArg ); + dns_sd( sendCallback_t sendCbFunc, void* sendCbArg, const char* serviceName, const char* serviceType, const char* serviceDomain, const char* hostName, uint32_t hostAddr, uint16_t hostPort, const char* text ); + virtual ~dns_sd(); + + result_t setup( const char* serviceName, const char* serviceType, const char* serviceDomain, const char* hostName, uint32_t hostAddr, uint16_t hostPort, const char* text ); + + result_t receive( const void* buf, unsigned bufByteN ); + + void gen_question(); + void gen_response(); + +private: + + enum + { + kHdrBodyByteN = 12, + kQuestionBodyByteN = 4, + kRsrcBodyByteN = 10, + kABodyByteN = 4, + kSrvBodyByteN = 6, + kOptBodyByteN = 4, + }; + + + sendCallback_t _sendCbFunc; + void* _sendCbArg; + char* _serviceName; + char* _serviceType; + char* _serviceDomain; + char* _hostName; + uint32_t _hostAddr; + uint16_t _hostPort; + char* _text; + + + void _free(); + unsigned _calc_question_byte_count(); + void _format_question( unsigned char* buf, unsigned bufByteN ); + unsigned char* _write_uint16( unsigned char* b, unsigned char* bend, uint16_t value ); + unsigned char* _write_uint32( unsigned char* b, unsigned char* bend, uint32_t value ); + unsigned char* _write_ptr( unsigned char* b, unsigned char* bend, const unsigned char ptr[2] ); + unsigned char* _write_text( unsigned char* b, unsigned char* bend, const char* name ); + unsigned char* _write_name( unsigned char* b, unsigned char* bend, const char* name, bool zeroTermFl=false, const unsigned char eosChar='.' ); + unsigned _calc_response_byte_count(); + void _format_response( unsigned char* buf, unsigned bufByteN ); + void _parse( const char* buf, unsigned bufByteN ); + void _send( const void* buf, unsigned bufByteN ); + + +}; + + +#endif diff --git a/dns_sd/dns_sd_const.h b/dns_sd/dns_sd_const.h new file mode 100644 index 0000000..5beac49 --- /dev/null +++ b/dns_sd/dns_sd_const.h @@ -0,0 +1,25 @@ +#ifndef dns_sd_const_h +#define dns_sd_const_h + + +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 + }; + + enum + { + kReplyHdrDnsFl = 0x8000, + kAuthoritativeHdrDnsFl = 0x0400, + kFlushClassDnsFl = 0x8000, + kInClassDnsFl = 0x0001 + }; + +#endif diff --git a/dns_sd/dns_sd_print.cpp b/dns_sd/dns_sd_print.cpp new file mode 100644 index 0000000..5c1c3fb --- /dev/null +++ b/dns_sd/dns_sd_print.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include "dns_sd_print.h" +#include "dns_sd_const.h" + + +unsigned _print_name( const char* s, const char* buf ) +{ + unsigned n = 0; // track allocated length of the name in this record + bool incrFl = true; // do not incrmement 'n' if the name switches to a ptr segment + + while( *s ) + { + if( (*s & 0xc0) == 0xc0 ) + { + if( incrFl ) + n += 2; + incrFl = false; + s = buf + s[1]; + } + else + { + for(char i=0; i 0x%2x",ntohs(u[0])); break; + } + + if( ntohs(u[1]) & 0x80 ) + printf("flush "); + + printf("\n"); +} + + diff --git a/dns_sd/dns_sd_print.h b/dns_sd/dns_sd_print.h new file mode 100644 index 0000000..4e9ac44 --- /dev/null +++ b/dns_sd/dns_sd_print.h @@ -0,0 +1,7 @@ +#ifndef print_dns_sd_h +#define print_dns_sd_h + +void dns_sd_print( const void* buf, unsigned bufByteN ); + + +#endif diff --git a/dns_sd/fader.cpp b/dns_sd/fader.cpp new file mode 100644 index 0000000..6cd70ec --- /dev/null +++ b/dns_sd/fader.cpp @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include "fader.h" + + +fader::fader( const unsigned char faderMac[6], uint32_t faderInetAddr, hostCallback_t hostCbFunc, void* hostCbArg, unsigned chN ) + : _inetAddr(faderInetAddr),_lastTickSeconds(0),_chArray(nullptr),_hostCbFunc(hostCbFunc),_hostCbArg(hostCbArg),_protoState(kWaitForHandshake_0_Id) +{ + memcpy(_mac,faderMac,6); + + _chArray = new ch_t[chN]; + _chN = chN; + for(unsigned i=0; i0 && ((uint8_t*)buf)[0] == 10 ) + { + printf("HS 0\n"); + _send_response_0(); + _protoState = kWaitForHandshake_Tick_Id; + } + break; + + case kWaitForHandshake_Tick_Id: + break; + + case kWaitForHandshake_1_Id: + printf("HS 1: %i",bufByteN); + _protoState = kWaitForHeartBeat_Id; + break; + + case kWaitForHeartBeat_Id: + break; + } + return rc; +} + +fader::rc_t fader::tick() +{ + rc_t rc = kOkRC; + switch( _protoState ) + { + case kWaitForHandshake_0_Id: + break; + + case kWaitForHandshake_Tick_Id: + printf("HS Tick"); + _send_heartbeat(); + _protoState = kWaitForHandshake_1_Id; + break; + + case kWaitForHandshake_1_Id: + break; + case kWaitForHeartBeat_Id: + break; + } + return rc; +} + +fader::rc_t fader::physical_fader_touched( uint16_t chanIdx ) +{ + return kOkRC; +} + +fader::rc_t fader::physical_fader_moved( uint16_t chanIdx, uint16_t value ) +{ + return kOkRC; +} + +fader::rc_t fader::physical_mute_switched( uint16_t chanIdx, bool value ) +{ + return kOkRC; +} + +void fader::_send_response_0() +{ + 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, // mac: 16 + 0x01,0x00, + 0xc0,0xa8,0x00,0x44, // ip: 24 + 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 + }; + + memcpy(buf+16,_mac,6); + memcpy((unsigned char *)(buf+24),(unsigned char*)&_inetAddr, 4); + + _send(buf,sizeof(buf)); +} + +void fader::_send_heartbeat() +{ + const unsigned char buf[] = { 0x03, 0x00, 0x00, 0x00 }; + _send(buf,sizeof(buf)); +} + +void fader::_send( const void* buf, unsigned bufByteN ) +{ + return _hostCbFunc(_hostCbArg,buf,bufByteN); +} + +void fader::_on_fader_receive( uint16_t chanIdx, uint16_t value ) +{ +} + +void fader::_on_mute_receive( uint16_t chanIdx, bool value ) +{ +} + diff --git a/dns_sd/fader.h b/dns_sd/fader.h new file mode 100644 index 0000000..94e6646 --- /dev/null +++ b/dns_sd/fader.h @@ -0,0 +1,70 @@ +#ifndef fader_h +#define fader_h + + +class fader +{ +public: + typedef enum + { + kOkRC, + kTimeOutRC, + kUnknownMsgRC + } rc_t; + + // Function to send TCP messages to the host. + typedef void (*hostCallback_t)( void* arg, const void* buf, unsigned bufByteN ); + + fader( const unsigned char faderMac[6], uint32_t faderInetAddr, hostCallback_t hostCbFunc, void* cbArg, unsigned chN = 8 ); + virtual ~fader(); + + // Called by the TCP receive function to update the faders state + // based on host state changes. + // Return kUnknownMsgRC if the received msg is not recognized. + rc_t host_receive( const void* buf, unsigned bufByteN ); + + // Called by the application to drive time dependent functions. + // Return kTimeOut if the protocol state machine has timed out. + rc_t tick(); + + // Call these function to generate messages to the host when + // the controls physical state changes. + rc_t physical_fader_touched( uint16_t chIdx ); + rc_t physical_fader_moved( uint16_t chIdx, uint16_t newPosition ); + rc_t physical_mute_switched( uint16_t chIdx, bool newMuteFl ); + +private: + typedef enum + { + kWaitForHandshake_0_Id, + kWaitForHandshake_Tick_Id, + kWaitForHandshake_1_Id, + kWaitForHeartBeat_Id + } protoState_t; + + typedef struct + { + uint16_t position; + bool muteFl; + } ch_t; + + uint32_t _inetAddr; + unsigned _lastTickSeconds; + ch_t* _chArray; + unsigned _chN; + hostCallback_t _hostCbFunc; + void* _hostCbArg; + protoState_t _protoState; + unsigned char _mac[6]; + + void _send_response_0(); + void _send_heartbeat(); + void _send( const void* buf, unsigned bufByteN ); + void _on_fader_receive( uint16_t chanIdx, uint16_t position ); + void _on_mute_receive( uint16_t chanIdx, bool muteFl ); + + +}; + + +#endif