dns_sd/ : Initial commit.

This commit is contained in:
kpl 2020-02-12 13:43:25 -05:00
parent a6692e3365
commit 969f1778fd
7 changed files with 699 additions and 0 deletions

307
dns_sd/dns_sd.cpp Normal file
View File

@ -0,0 +1,307 @@
#include "dns_sd.h"
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arpa/inet.h>
#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);
}

69
dns_sd/dns_sd.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef dns_sd_h
#define dns_sd_h
#include <stdint.h>
#include <stdlib.h>
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

25
dns_sd/dns_sd_const.h Normal file
View File

@ -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

87
dns_sd/dns_sd_print.cpp Normal file
View File

@ -0,0 +1,87 @@
#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>
#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<s[0]; ++i)
{
printf("%c",s[i+1]);
if( incrFl )
++n;
}
s += s[0]+1;
n += 1;
if(*s)
{
printf(".");
}
}
}
return n;
}
void dns_sd_print( const void* buf, unsigned bufByteN )
{
const uint16_t* u = (uint16_t*)buf;
const char* b = (const char*)(u+6);
printf("%s ", ntohs(u[1]) & 0x8000 ? "Response " : "Question ");
unsigned n = _print_name(b,(const char*)buf);
printf(" slen:%i ",n);
u = (uint16_t*)(b + n+1); // advance past name
switch( ntohs(u[0]) )
{
case kA_DnsTId: printf("A ");
break;
case kPTR_DnsTId: printf("PTR ");
break;
case kTXT_DnsTId: printf("TXT ");
break;
case kSRV_DnsTId: printf("SRV ");
break;
case kAAAA_DnsTId:printf("AAAA ");
break;
case kOPT_DnsTId: printf("OPT ");
break;
case kNSEC_DnsTId:printf("NSEC "); break;
case kANY_DnsTId: printf("ANY "); break;
default:
printf("<unk> 0x%2x",ntohs(u[0])); break;
}
if( ntohs(u[1]) & 0x80 )
printf("flush ");
printf("\n");
}

7
dns_sd/dns_sd_print.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef print_dns_sd_h
#define print_dns_sd_h
void dns_sd_print( const void* buf, unsigned bufByteN );
#endif

134
dns_sd/fader.cpp Normal file
View File

@ -0,0 +1,134 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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; i<chN; ++i)
{
_chArray[i].position = 0;
_chArray[i].muteFl = false;
}
}
fader::~fader()
{
delete[] _chArray;
}
fader::rc_t fader::host_receive( const void* buf, unsigned bufByteN )
{
rc_t rc = kOkRC;
printf("FDR:%i\n",bufByteN);
switch( _protoState )
{
case kWaitForHandshake_0_Id:
if( bufByteN>0 && ((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 )
{
}

70
dns_sd/fader.h Normal file
View File

@ -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