/*********************************************************************
*
* Zero Configuration (Zeroconf) Multicast DNS and
* Service Discovery Module for Microchip TCP/IP Stack
*
*********************************************************************
* FileName: ZeroconfMulticastDNS.h
* Dependencies: IP
* Processor: PIC24F, PIC24H, dsPIC30F, dsPIC33F, PIC32
* Compiler: Microchip C32 v1.05 or higher
* Microchip C30 v3.12 or higher
* Company: Microchip Technology, Inc.
*
* Software License Agreement
*
* Copyright (C) 2009-2010 Microchip Technology Inc. All rights
* reserved.
*
* Microchip licenses to you the right to use, modify, copy, and
* distribute:
* (i) the Software when embedded on a Microchip microcontroller or
* digital signal controller product ("Device") which is
* integrated into Licensee's product; or
* (ii) ONLY the Software driver source files ENC28J60.c, ENC28J60.h,
* ENCX24J600.c and ENCX24J600.h ported to a non-Microchip device
* used in conjunction with a Microchip ethernet controller for
* the sole purpose of interfacing with the ethernet controller.
*
* You should refer to the license agreement accompanying this
* Software for additional information regarding your rights and
* obligations.
*
* THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT
* WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
* LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF
* PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
* BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE
* THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER
* SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT
* (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
*
*
* Author Date Comment
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Pradeep Reddy 31 Mar 2009 Initial creation
* Pradeep Reddy 23 Apr 2009 DNS-SD implementation
* Pradeep Reddy 27 Apr 2009 Fixes and enhancements
* Pradeep Reddy 11 May 2009 Code documentation
* Pradeep Reddy 12 May 2009 Cleanup
* King Shaw 14 Sep 2009 Added message-compression code
* King Shaw 23 Sep 2009 Rewrite message-compression code to
* decompress in-place.
* King Shaw 10 Oct 2009 Rewrite responder code to pass conformance
* tests with small footprint.
* Brad Rex 05 Apr 2010 Updated for MRF24WB0M
********************************************************************/
#define __Zeroconf_Multicast_DNS_C
#include "TCPIP Stack/TCPIP.h"
#define TICK DWORD
//MDNS_STATIC should be static in production code
//For code size tuning, defined to be (blank) - so C30 can give us the size of each function.
//#define MDNS_STATIC static
#define MDNS_STATIC static
#if defined(STACK_USE_ZEROCONF_MDNS_SD)
#include "TCPIP Stack/ZeroconfMulticastDNS.h"
extern void DisplayIPValue(IP_ADDR IPVal);
#define MDNS_PORT 5353
#define MAX_HOST_NAME_SIZE 32 //31+'\0' Max Host name size
#define MAX_LABEL_SIZE 64 //63+'\0' Maximum size allowed for a label. RFC 1035 (2.3.4) == 63
#define MAX_RR_NAME_SIZE 256 //255+'\0' Max Resource Recd Name size. RFC 1035 (2.3.4) == 255
#define MAX_SRV_TYPE_SIZE 32 //31+'\0' eg. "_http._tcp.local". Max could be 255, but is an overkill.
#define MAX_SRV_NAME_SIZE 64 //63+'\0' eg. "My Web server". Max could be 255, but is an overkill.
#define MAX_TXT_DATA_SIZE 128 //127+'\0' eg. "path=/index.htm"
#define RESOURCE_RECORD_TTL_VAL 3600 // Time-To-Live for a Resource-Record in seconds.
#define MAX_RR_NUM 4 // for A, PTR, SRV, and TXT Max No.of Resource-Records/Service
/* Constants from mdns.txt (IETF Draft)*/
#define MDNS_PROBE_WAIT 750 // msecs (initial random delay)
#define MDNS_PROBE_INTERVAL 250 // msecs (maximum delay till repeated probe)
#define MDNS_PROBE_NUM 6 // (number of probe packets)
#define MDNS_MAX_PROBE_CONFLICT_NUM 30 // max num of conflicts before we insist and move on to announce ...
#define MDNS_ANNOUNCE_NUM 3 // (number of announcement packets)
#define MDNS_ANNOUNCE_INTERVAL 250 // msecs (time between announcement packets)
#define MDNS_ANNOUNCE_WAIT 250 // msecs (delay before announcing)
/* Resource-Record Types from RFC-1035 */
typedef enum {
QTYPE_A = 1,
QTYPE_NS = 2,
QTYPE_CNAME = 5,
QTYPE_PTR = 12,
QTYPE_TXT = 16,
QTYPE_SRV = 33,
QTYPE_ANY = 255,
}MDNS_QTYPE;
/* Indexes in Resource-record list */
#define QTYPE_A_INDEX 0
#define QTYPE_PTR_INDEX 1
#define QTYPE_SRV_INDEX 2
#define QTYPE_TXT_INDEX 3
/* MDNS Message Fomrat, which is common
* for Queries and Resource-Records. Taken
* from RFC 1035
*/
/* MDNS Message Header Flags */
typedef union _MDNS_MSG_HEADER_FLAGS {
struct {
BYTE rcode:4;
BYTE z:3;
BYTE ra:1;
BYTE rd:1;
BYTE tc:1;
BYTE aa:1;
BYTE opcode:4;
BYTE qr:1;
}bits;
WORD Val;
BYTE v[2];
} MDNS_MSG_HEADER_FLAGS;
/* MDNS Message-Header Format */
typedef struct _MDNS_MSG_HEADER
{
WORD_VAL query_id;
MDNS_MSG_HEADER_FLAGS flags;
WORD_VAL nQuestions;
WORD_VAL nAnswers;
WORD_VAL nAuthoritativeRecords;
WORD_VAL nAdditionalRecords;
} MDNS_MSG_HEADER;
/* DNS-Query Format, which is prepended by
* DNS-MESSAGE Header defined above */
struct question
{
unsigned char *name;
unsigned short int type, class;
};
/* DNS-Resource Record Format, which is
* prepended by DNS-MESSAGE Header
* defined above. This definition includes
* all resource-record data formats, to have
* small-memory foot print */
struct _mDNSProcessCtx_sd;// mdnsd_struct
struct _mDNSProcessCtx_common;
typedef struct _mDNSResourceRecord
{
BYTE *name;
WORD_VAL type;
WORD_VAL class;
DWORD_VAL ttl;
WORD_VAL rdlength;
union {
IP_ADDR ip; // for A record
struct {
WORD_VAL priority;
WORD_VAL weight;
WORD_VAL port;
} srv; // for SRV record
};
// DO NOT merge this into the union.
BYTE *rdata; // for PTR, SRV and TXT records.
/* House-Keeping Stuff */
// pointer to the header Ctx of the process that "owns" this resource record.
struct _mDNSProcessCtx_common *pOwnerCtx;
BYTE valid; /* indicates whether rr is valid */
BOOL bNameAndTypeMatched;
BOOL bResponseRequested;
BOOL bResponseSuppressed;
} mDNSResourceRecord;
/* DNS-SD Specific Data-Structures */
typedef enum _MDNS_STATE
{
MDNS_STATE_HOME = 0,
MDNS_STATE_INTF_NOT_CONNECTED,
MDNS_STATE_IPADDR_NOT_CONFIGURED,
MDNS_STATE_NOT_READY,
MDNS_STATE_INIT,
MDNS_STATE_PROBE,
MDNS_STATE_ANNOUNCE,
MDNS_STATE_DEFEND,
} MDNS_STATE;
typedef enum _MDNS_RR_GROUP
{
MDNS_RR_GROUP_QD,
MDNS_RR_GROUP_AN,
MDNS_RR_GROUP_NS,
MDNS_RR_GROUP_AR
} MDNS_RR_GROUP;
typedef struct _mDNSResponderCtx
{
mDNSResourceRecord rr_list[MAX_RR_NUM]; // Our resource records.
BOOL bLastMsgIsIncomplete; // Last DNS msg was truncated
WORD_VAL query_id; // mDNS Query transaction ID
IP_ADDR prev_ipaddr; // To keep track of changes in IP-addr
} mDNSResponderCtx;
typedef enum _MDNS_CTX_TYPE
{
MDNS_CTX_TYPE_HOST = 0,
MDNS_CTX_TYPE_SD
} MDNS_CTX_TYPE;
typedef struct _mDNSProcessCtx_common
{
MDNS_CTX_TYPE type; // Is owner mDNS ("HOST") or mDNS-SD ("SD")?
MDNS_STATE state; // PROBE, ANNOUNCE, DEFEND, ...
BYTE nProbeCount;
BYTE nProbeConflictCount;
BYTE nClaimCount;
BOOL bProbeConflictSeen;
BOOL bLateConflictSeen;
BOOL bConflictSeenInLastProbe;
BYTE nInstanceId;
TICK event_time; // Internal Timer, to keep track of events
BYTE time_recorded; // Flag to indicate event_time is loaded
TICK random_delay;
} mDNSProcessCtx_common;
typedef struct _mDNSProcessCtx_host
{
mDNSProcessCtx_common common;
mDNSResponderCtx *pResponderCtx;
// other host name related info
BYTE szUserChosenHostName[MAX_HOST_NAME_SIZE]; // user chosen host name
BYTE szHostName[MAX_HOST_NAME_SIZE]; // mDNS chosen Host-Name
} mDNSProcessCtx_host;
typedef struct _mDNSProcessCtx_sd
{
mDNSProcessCtx_common common;
mDNSResponderCtx *pResponderCtx;
// info specific to SD
BYTE srv_name[MAX_SRV_NAME_SIZE];
BYTE srv_type[MAX_SRV_TYPE_SIZE];
BYTE sd_qualified_name[MAX_RR_NAME_SIZE];
BYTE used; /* Spinlock to protect Race-cond. */
BYTE sd_auto_rename: 1, /* Flag to show auto-Rename is enabled */
sd_service_advertised: 1, /* Flag to show whether service is advertised */
service_registered: 1; /* Flag to indicate that user has registered this service */
WORD sd_port; /* Port number in Local-sys where Service is being offered */
BYTE sd_txt_rec[MAX_TXT_DATA_SIZE];
BYTE sd_txt_rec_len;
void (*sd_call_back)(char *, MDNSD_ERR_CODE , void *);
void *sd_context;
} mDNSProcessCtx_sd;
static mDNSProcessCtx_host gHostCtx;
static mDNSProcessCtx_sd gSDCtx;
static mDNSResponderCtx gResponderCtx;
/* DNS-SD State-Machine */
const BYTE *CONST_STR_local = (BYTE *) "local";
/* Multicast-DNS States defintion */
/************** Global Declarations ***************/
/* Remote Node info, which is Multicast-Node
* whose IP-address is 224.0.0.251 & MAC-Address
* is 01:00:5E:00:00:FB. Multicast-IP address for
* mDNS is specified by mdns.txt (IETF). IP is
* translated into Multicast-MAC address according
* rules specified in Std.
*/
static NODE_INFO mDNSRemote;
UDP_SOCKET mDNS_socket = INVALID_UDP_SOCKET; // Multicast-Socket Opened up for
// mDNS Server/Client (Responder/Qurier)
/* Global declaration to support Message-Compression
* defined in RFC-1035, Section 4.1.4 */
/* Forward declarations */
MDNS_STATIC void mDNSResponder(void);
MDNS_STATIC WORD mDNSDeCompress(WORD wPos, BYTE *pcString, BOOL bFollowPtr, BYTE cElement, BYTE cDepth);
static WORD g_mDNS_offset;
//#define MDNS_USE_LOCAL_STRLEN y
#define MDNS_USE_LOCAL_STRCPY y
//#define MDNS_USE_LOCAL_STRCMP y
//#define MDNS_USE_LOCAL_ITOA y //local version returns extra info. To be fixed.
#ifdef MDNS_USE_LOCAL_STRLEN
#define STRLEN_LOCAL(x) strlen_local(x)
#else
// use string functions provided by the SDK library.
#define STRLEN_LOCAL(x) strlen((char *)x)
#endif
#ifdef MDNS_USE_LOCAL_STRCPY
#define STRCPY_LOCAL(x,y) strcpy_local(x,y)
#else
// use string functions provided by the SDK library.
#define STRCPY_LOCAL(x,y) strcpy((char *)x,(char *)y)
#endif
#ifdef MDNS_USE_LOCAL_STRCMP
#define STRCMP_LOCAL(x,y) strcmp_local(x,y)
#else
// use string functions provided by the SDK library.
#define STRCMP_LOCAL(x,y) strcmp((char *)x,(char *)y)
#endif
#ifdef MDNS_USE_LOCAL_ITOA
#define ITOA_LOCAL(x,y) itoa_local(x,y)
#else
// use itoa functions provided by the SDK library.
#define ITOA_LOCAL(x,y) uitoa((WORD)x,y)
#endif
#ifdef MDNS_USE_LOCAL_STRLEN
MDNS_STATIC BYTE strlen_local(BYTE *str);
#endif
// Redirect all UDPPUT_LOCAL() calls to here.
// This seems to make all those 'call + nop' (2 instructions) into 'rcall' (1 instruction)
MDNS_STATIC void
UDPPUT_LOCAL(BYTE c)
{
UDPPut(c);
}
/***************************************************************
Function:
void DisplayHostName(BYTE *HostName)
Summary:
Writes an Host-Name to the LCD display and the UART
as available
Description:
This function takes a string and then displays it on LCD's
2nd half.
Parameters:
HostName - the Hos-name to be displayed.
Returns:
None
***************************************************************/
MDNS_STATIC void DisplayHostName(BYTE *HostName)
{
BYTE SingleChar;
BYTE i;
BYTE len;
#ifdef USE_LCD
BYTE LCDPos=0;
#endif
#if defined(STACK_USE_UART)
putrsUART((ROM char*)"\r\nZeroConf: Host = ");
putrsUART((ROM char*)HostName);
putrsUART((ROM char*)"\r\n");
#endif
len = STRLEN_LOCAL(HostName);
for(i = 0; (i < len) && (i < 16u); i++)
{
SingleChar = *HostName++;
if(SingleChar == '\0')
break;
#ifdef USE_LCD
LCDText[LCDPos++] = SingleChar;
#endif
}
#ifdef USE_LCD
if(LCDPos < 32u)
LCDText[LCDPos] = 0;
LCDUpdate();
#endif
}
/************* Local String Functions ******************/
#ifdef MDNS_USE_LOCAL_STRLEN
/***************************************************************
Function:
static BYTE strlen_local(BYTE *string)
Summary:
Finds the length of a string.
Description:
This function finds length a string. This is an base
implementation for small memory foot-print.
Parameters:
String - the string for which length to be calculated.
Returns:
Length of type BYTE.
**************************************************************/
MDNS_STATIC BYTE strlen_local(BYTE *str)
{
BYTE len=0;
while(*str++)
len++;
return len;
}
#endif // MDNS_USE_LOCAL_STRLEN
#ifdef MDNS_USE_LOCAL_STRCMP
/***************************************************************
Function:
static BYTE strcmp_local(BYTE *string_1, BYTE *string_2)
Summary:
Compares two strings
Parameters:
string_1 & string_2 - Two strings
Returns:
Zero: If two strings are equal.
Non-Zero: If both strings are not equal or on error case
**************************************************************/
MDNS_STATIC BYTE strcmp_local(BYTE *str_1, BYTE *str_2)
{
if(str_1 == NULL || str_2 == NULL)
{
return -1;
}
while(*str_1 && *str_2){
if(*str_1++ != *str_2++)
return 1;
}
if(*str_1 == '\0' && *str_2 == '\0')
return 0;
else
return 1;
}
#endif // MDNS_USE_LOCAL_STRCMP
/***************************************************************
Function:
static BYTE strcmp_local_ignore_case(BYTE *string_1, BYTE *string_2)
Summary:
Compares two strings by ignoring the case.
Parameters:
string_1 & string_2 - Two strings
Returns:
Zero: If two strings are equal.
Non-Zero: If both strings are not equal or on error case
**************************************************************/
MDNS_STATIC BYTE strcmp_local_ignore_case(BYTE *str_1, BYTE *str_2)
{
if(str_1 == NULL || str_2 == NULL)
{
WARN_MDNS_PRINT("strmcmp_local_ignore_case: String is NULL \r\n");
return -1;
}
while(*str_1 && *str_2){
if(*str_1 == *str_2 || (*str_1-32) == *str_2 ||
*str_1 == (*str_2-32))
{
str_1++;
str_2++;
continue;
}
else
return 1;
}
if(*str_1 == '\0' && *str_2 == '\0')
return 0;
else
return 1;
}
#ifdef MDNS_USE_LOCAL_STRCPY
/********************************************************************
Function:
static void strcpy_local(BYTE *dst_string, BYTE *src_string)
Summary:
Copies one string into other string. This is base
& optimized implementation without error-handling.
Goal is small memory foot print.
Parameters:
dst_string: Destination String
src_string: Source string
Returns:
None
**************************************************************/
MDNS_STATIC void strcpy_local(BYTE *dst, BYTE *src)
{
while((*dst++ = *src++));
}
#endif // MDNS_USE_LOCAL_STRCPY
#ifdef MDNS_USE_LOCAL_ITOA
/***************************************************************
Function:
static void strrev_local(BYTE *string)
Summary:
Reverses a string
Description:
This function reverses a string without copying string or
requiring additional memory. This is an Optimized
implementation for both processing & memory efficiency.
Parameters:
String - the string to be reversed.
Returns:
None
***************************************************************/
MDNS_STATIC void strrev_local(BYTE *str)
{
BYTE *first, *last;
BYTE ch;
first = last = str;
while(*last)
last++;
last--;
while(first<=last)
{
ch = *first;
*first = *last;
*last = ch;
first++;
last--;
}
}
/***************************************************************
Function:
static BYTE itoa_local(BYTE num, BYTE *string)
Summary:
Converts decimal (num) to String (string).
Parameters:
num: Decimal number, which needs to be converted to string
string: Memory where string needs to be stored.
(Output param)
Returns:
Count of number of characters present in String rep
**************************************************************/
MDNS_STATIC BYTE itoa_local(BYTE num, BYTE *str)
{
BYTE *tmp = str;
BYTE cnt = 0;
while(num>0)
{
cnt++;
*tmp++ = (num%10) + '0';
num = num/10;
}
*tmp = '\0';
strrev_local(str);
return cnt;
}
#endif // MDNS_USE_LOCAL_ITOA
MDNS_STATIC void
mDNSResetCounters(mDNSProcessCtx_common *pHeader, BOOL bResetProbeConflictCount)
{
if (bResetProbeConflictCount)
{
pHeader->nProbeConflictCount = 0;
}
pHeader->nProbeCount = 0;
pHeader->nClaimCount = 0;
pHeader->bLateConflictSeen = FALSE;
pHeader->bProbeConflictSeen = FALSE;
}
/***************************************************************
Function:
static void mDNSRename(BYTE *str, BYTE max_len)
Summary:
Renames a string with a numerical-extension.
Description:
This function is to rename host-name/Resource-Record Name,
when a name-conflict is detected on local-network.
For-Ex: "myhost" is chosen name and a conflict is detected
this function renames as "myhost-2". Also ensures that string
is properly formatted.
Precondition:
None
Parameters:
String - the string to be Renamed with Numerical-Extenstion.
max_len - Maximum Length allowed for String
Returns:
None
**************************************************************/
// strLabel: the user registered name.
// E.g., "Web Server", for service name (srv_name), or
// "My Host", for host name (taken from MY_DEFAULT_HOST_NAME)
// TODO: both names should be run-time configurable,
// through registration functions.
// nLabelId: instance number, to avoid conflict in the name space.
// strBase: the base name for the appropriate name space.
// E.g., "_http._tcp.local" for service name, or
// "local" for host name.
// strTarget: where the newly constructed fully-qualified-name will be stored.
// nMaxLen: max length for the newly constructed label, which is the first portion of the
// fully-qualified-name
//
// ("Web Server", 3, "_http._tcp.local", strTarget, 63) =>
// stores "Web Server-3._http._tcp.local" to *strTarget.
// ("MyHost", 2, "local", strTarget, 63) =>
// stores "MyHost-2.local" to *strTarget
//
MDNS_STATIC void mDNSRename(BYTE *strLabel, BYTE nLabelId, BYTE *strBase, BYTE *strTarget, BYTE nMaxLen)
{
BYTE n = nLabelId;
#define mDNSRename_ID_LEN 6
BYTE str_n[mDNSRename_ID_LEN]; //enough for "-255." + '\0'.
BYTE i = mDNSRename_ID_LEN - 1 ;
str_n[i--] = 0;
str_n[i--] = '.';
// construct str_n from n
while (i != 0)
{
str_n[i--] = '0'+ n%10;
if (n < 10) break;
n = n/10;
}
str_n[i] = '-';
STRCPY_LOCAL(strTarget, strLabel);
STRCPY_LOCAL(strTarget+STRLEN_LOCAL(strTarget), &(str_n[i]));
// nMaxLen not checked, but here is how it should be:
#ifdef MDNS_WARN
if ( STRLEN_LOCAL(strTarget) > nMaxLen )
{
MDNS_WARN("mDNSRename: label too long\r\n");
}
#endif
STRCPY_LOCAL(strTarget+STRLEN_LOCAL(strTarget), strBase);
}
/***************************************************************
Function:
static void mDNSPutString(BYTE* String)
Summary:
Writes a string to the Multicast-DNS socket.
Description:
This function writes a string to the Multicast-DNS socket,
ensuring that it is properly formatted.
Precondition:
UDP socket is obtained and ready for writing.
Parameters:
String - the string to write to the UDP socket.
Returns:
None
**************************************************************/
MDNS_STATIC void mDNSPutString(BYTE* string)
{
BYTE *right_ptr,*label_ptr;
BYTE label[MAX_LABEL_SIZE];
BYTE i;
BYTE len;
right_ptr = string;
while(1)
{
label_ptr = label;
len = 0;
while(*right_ptr)
{
i = *right_ptr;
if(i == '.' || i == '/' ||
i == ',' || i == '>' || i == '\\')
{
/* Formatted Serv-Instance will have '\.'
* instead of just '.' */
if(i == '\\')
{
right_ptr++;
}
else
break;
}
*label_ptr++ = *right_ptr;
len++;
right_ptr++;
}
i = *right_ptr++;
// Put the length and data
// Also, skip over the '.' in the input string
UDPPUT_LOCAL(len);
UDPPutArray(label, len);
string = right_ptr;
if(i == 0x00u || i == '/' || i == ',' || i == '>')
break;
}
// Put the string null terminator character
UDPPUT_LOCAL(0x00);
}
/***************************************************************
Function:
static BOOL mDNSSendQuery(BYTE* name, BYTE record_type)
Summary:
Sends out a Multicast-DNS-Query to Multicast-Address
through mDNS_socket.
Description:
This function is used in Probing-phase to check the
uniqueness of chosen Host-Name/ Resoruce-Record-Name.
Selected Name and Type of Query are put into
Multicast UDP socket.
Precondition:
UDP socket (mDNS_socket) is obtained and ready for writing.
Parameters:
name - Chosen Host-Name/Resource-Record-Name, checking
for uniqueness.
type - Type of Query
Returns:
TRUE - On Success
FALSE - On Failure (If UDP-Socket is invalid)
**************************************************************/
//MDNS_STATIC BOOL mDNSSendQuery(BYTE* name, BYTE record_type, BYTE cFlush, BYTE probe_type)
MDNS_STATIC BOOL mDNSProbe(mDNSProcessCtx_common *pCtx)
{
MDNS_MSG_HEADER mDNS_header;
// Abort operation if no UDP sockets are available
// If this ever happens, incrementing MAX_UDP_SOCKETS in
// StackTsk.h may help (at the expense of more global memory
// resources).
if(mDNS_socket == INVALID_UDP_SOCKET)
{
WARN_MDNS_PRINT("mDNSSendQuery: Opening UDP Socket Failed \r\n");
return FALSE;
}
// Make certain the socket can be written to
while(!UDPIsPutReady(mDNS_socket));
// Put DNS query here
gResponderCtx.query_id.Val++;
/*
UDPPUT_LOCAL(gResponderCtx.query_id.v[1]);// User chosen transaction ID
UDPPUT_LOCAL(gResponderCtx.query_id.v[0]);
UDPPUT_LOCAL(0x00); // Standard query with recursion
UDPPUT_LOCAL(0x00);
UDPPUT_LOCAL(0x00); // 1 entry in the question section
UDPPUT_LOCAL(0x01);
UDPPUT_LOCAL(0x00); // 0 entry in the answer section
UDPPUT_LOCAL(0x00);
UDPPUT_LOCAL(0x00); // 1 entry in name server section
UDPPUT_LOCAL(0x01);
UDPPUT_LOCAL(0x00); // 0 entry in additional records section
UDPPUT_LOCAL(0x00);
*/
mDNS_header.query_id.Val = swaps(gResponderCtx.query_id.Val); // User chosen transaction ID
mDNS_header.flags.Val = 0; // Standard query with recursion
mDNS_header.nQuestions.Val = swaps(((WORD)1u)); // 1 entry in the question section
mDNS_header.nAnswers.Val = 0; // 0 entry in the answer section
mDNS_header.nAuthoritativeRecords.Val = swaps(((WORD)1u)); // 1 entry in name server section
mDNS_header.nAdditionalRecords.Val = 0; // 0 entry in additional records section
// Put out the mDNS message header
UDPPutArray((BYTE *) &mDNS_header, sizeof(MDNS_MSG_HEADER));
// Start of the QD section
switch (pCtx->type)
{
case MDNS_CTX_TYPE_HOST:
mDNSPutString(gResponderCtx.rr_list[QTYPE_A_INDEX].name);
break;
case MDNS_CTX_TYPE_SD:
mDNSPutString(gResponderCtx.rr_list[QTYPE_SRV_INDEX].name);
break;
}
UDPPUT_LOCAL(0x00); // Type: Always QTYPE_ANY
UDPPUT_LOCAL(QTYPE_ANY);
UDPPUT_LOCAL(0x80); // Class: Cache-Flush
UDPPUT_LOCAL(0x01); // IN (Internet)
// Start of the NS section
switch (pCtx->type)
{
case MDNS_CTX_TYPE_HOST:
mDNSPutString(gResponderCtx.rr_list[QTYPE_A_INDEX].name);
break;
case MDNS_CTX_TYPE_SD:
mDNSPutString(gResponderCtx.rr_list[QTYPE_SRV_INDEX].name);
break;
}
UDPPUT_LOCAL(0x00); // Type: A or SRV
switch (pCtx->type)
{
case MDNS_CTX_TYPE_HOST:
UDPPUT_LOCAL(QTYPE_A);
break;
case MDNS_CTX_TYPE_SD:
UDPPUT_LOCAL(QTYPE_SRV);
break;
}
UDPPUT_LOCAL(0x00); // Class: Cache-Flush bit MUST NOT be set
UDPPUT_LOCAL(0x01); // IN (Internet)
UDPPUT_LOCAL(0x00); // 0x00000078 Time To Live, 2 minutes
UDPPUT_LOCAL(0x00);
UDPPUT_LOCAL(0x00);
UDPPUT_LOCAL(0x78);
switch (pCtx->type)
{
case MDNS_CTX_TYPE_HOST:
{
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_A_INDEX].rdlength.v[1]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_A_INDEX].rdlength.v[0]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_A_INDEX].ip.v[0]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_A_INDEX].ip.v[1]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_A_INDEX].ip.v[2]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_A_INDEX].ip.v[3]);
break;
}
case MDNS_CTX_TYPE_SD:
{
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_SRV_INDEX].rdlength.v[1]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_SRV_INDEX].rdlength.v[0]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_SRV_INDEX].srv.priority.v[1]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_SRV_INDEX].srv.priority.v[0]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_SRV_INDEX].srv.weight.v[1]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_SRV_INDEX].srv.weight.v[0]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_SRV_INDEX].srv.port.v[1]);
UDPPUT_LOCAL(gResponderCtx.rr_list[QTYPE_SRV_INDEX].srv.port.v[0]);
mDNSPutString(gResponderCtx.rr_list[QTYPE_SRV_INDEX].rdata);
break;
}
}
UDPFlush();
return TRUE;
}
/***************************************************************
Function:
static BOOL mDNSSendRR(struct mDNSResourceRecord *record,
BYTE record_type, DWORD ttl_val,WORD query_id)
Summary:
Sends out a Multicast-DNS-Answer (Resource-Record) to
Multicast-Address through mDNS_socket (UDP Socket).
Description:
This function is used in Announce-phase & Defend-Phase.
In announce-phase the Host-Name or Resource-Record (Service)
will be announced in local-network, so that neighbors can
detect new-service or update their caches with new host-name
to IP-Address mapping.
In Defend-Phase, when mDNSResponder receives a query for
Host-name or Resounce-record for which this holds authority.
Precondition:
UDP socket (mDNS_socket) is obtained and ready for writing.
Parameters:
record - Resource-Record filled up with required info
type - Type of Res-Rec
ttl_val - Time-To-Live value for Res-Record
query_id - Query-ID for which this mDNS-answer (Res-Rec)
corresponds to
Returns:
TRUE - On Success
FALSE - On Failure (If UDP-Socket is invalid)
**************************************************************/
MDNS_STATIC BOOL
mDNSSendRR(mDNSResourceRecord *pRecord,
WORD query_id,
BYTE cFlush,
WORD nAnswersInMsg,
BOOL bIsFirstRR,
BOOL bIsLastRR)
{
MDNS_MSG_HEADER mDNS_header;
DWORD_VAL ttl;
BYTE rec_length;
BYTE record_type;
record_type = pRecord->type.Val;
DEBUG0_MDNS_MESG(zeroconf_dbg_msg, "tx RR: (%d)\r\n", record_type);
DEBUG0_MDNS_PRINT(zeroconf_dbg_msg);
if(mDNS_socket == INVALID_UDP_SOCKET)
{
WARN_MDNS_PRINT("mDNSSendRR: Opening UDP Socket Failed \r\n");
return FALSE;
}
while(!UDPIsPutReady(mDNS_socket));
if (bIsFirstRR)
{
memset(&mDNS_header, 0, sizeof(MDNS_MSG_HEADER));
mDNS_header.query_id.Val = swaps(query_id);
mDNS_header.flags.bits.qr = 1; // this is a Response,
mDNS_header.flags.bits.aa = 1; // and we are authoritative
mDNS_header.flags.Val = swaps(mDNS_header.flags.Val);
mDNS_header.nAnswers.Val = swaps(nAnswersInMsg);
// Put out the mDNS message header
UDPPutArray((BYTE *) &mDNS_header, sizeof(MDNS_MSG_HEADER));
}
ttl.Val = pRecord->ttl.Val;
//rex#if 0
//rex if ( name != NULL)
//rex {
//rex mDNSPutString(name);
//rex }
//rex else
//rex#endif
//rex {
mDNSPutString(pRecord->name);
//rex }
UDPPUT_LOCAL(0x00); UDPPUT_LOCAL(record_type); // Resource Record Type
/* MSB of Upper-byte in Class filed acts as
* Cache-Flush bit to notify all Neighbors to
* flush their caches and fill with this new
* information */
if (UDPSocketInfo[mDNS_socket].remotePort == MDNS_PORT)
{
UDPPUT_LOCAL(cFlush); UDPPUT_LOCAL(0x01); // Class
}
else
{
// Legacy/Unicast DNS response should not set the Cache-Flush bit.
UDPPUT_LOCAL(0x00); UDPPUT_LOCAL(0x01); // Class
}
UDPPUT_LOCAL(ttl.v[3]); // Time To Live
UDPPUT_LOCAL(ttl.v[2]);
UDPPUT_LOCAL(ttl.v[1]);
UDPPUT_LOCAL(ttl.v[0]);
switch (record_type)
{
case QTYPE_A:
UDPPUT_LOCAL(0x00); // 0x0004 Data length
UDPPUT_LOCAL(0x04);
UDPPUT_LOCAL(pRecord->ip.v[0]); // Put out IP address
UDPPUT_LOCAL(pRecord->ip.v[1]); // AppConfig.MyIPAddr.v[1]
UDPPUT_LOCAL(pRecord->ip.v[2]);
UDPPUT_LOCAL(pRecord->ip.v[3]);
break;
case QTYPE_PTR:
/* 2 bytes extra. One for Prefix Length for first-label.
* Other one for NULL terminator */
pRecord->rdlength.Val = STRLEN_LOCAL(pRecord->rdata) + 2 ;
UDPPUT_LOCAL(pRecord->rdlength.v[1]);
UDPPUT_LOCAL(pRecord->rdlength.v[0]); // Res-Data Length
mDNSPutString(((mDNSProcessCtx_sd *) (pRecord->pOwnerCtx))->sd_qualified_name);
break;
case QTYPE_SRV:
/* 2 bytes extra. One for Prefix Length for first-label.
* Other one for NULL terminator */
pRecord->rdlength.Val = STRLEN_LOCAL(pRecord->rdata) + 2;
pRecord->rdlength.Val += 6; // for priority, weight, and port
UDPPUT_LOCAL(pRecord->rdlength.v[1]);
UDPPUT_LOCAL(pRecord->rdlength.v[0]); // Res-Data Length
UDPPUT_LOCAL(pRecord->srv.priority.v[1]); // Put Priority
UDPPUT_LOCAL(pRecord->srv.priority.v[0]);
UDPPUT_LOCAL(pRecord->srv.weight.v[1]);
UDPPUT_LOCAL(pRecord->srv.weight.v[0]);
UDPPUT_LOCAL(pRecord->srv.port.v[1]);
UDPPUT_LOCAL(pRecord->srv.port.v[0]);
mDNSPutString(pRecord->rdata);
break;
case QTYPE_TXT:
rec_length = STRLEN_LOCAL(pRecord->rdata);
pRecord->rdlength.Val = rec_length + 1;
UDPPUT_LOCAL(pRecord->rdlength.v[1]);
UDPPUT_LOCAL(pRecord->rdlength.v[0]); // Res-Data Length
UDPPUT_LOCAL(pRecord->rdlength.Val-1); // As of now only single TXT string supported!!
UDPPutArray(pRecord->rdata,rec_length);
break;
default:
WARN_MDNS_PRINT("RR Type not supported \n");
}
if (bIsLastRR)
{
UDPFlush();
}
return TRUE;
}
/***************************************************************
Function:
void mDNSSDFormatServiceInstance(BYTE *string)
Summary:
Formats the Service-Instance name according to DNS-SD standard
specification.
Description:
This function is used to format the Service-Instance name, if
it contains 'dots' and 'backslashes'
As the service-instance name will be merged with service-type &
to distinguish the 'dots' seperating the service-type words and
'dots' within service-instance name, the 'dots' within service-
instance name will be replaced with '\.' in place of '.' Even the
'\' are replaced with '\\'.
When the resource-record containing service-instance name is
pushed out, the formatted dots '\.' are sentout as '.' and the
'dots' sperating the service-type & service-instances are replaced
with length bytes, as specified in RFC 1035.
Precondition:
None
Parameters:
String - Service-Instance name to be formatted
Returns:
None
**************************************************************/
void mDNSSDFormatServiceInstance(BYTE *string)
{
BYTE *temp;
BYTE output[MAX_LABEL_SIZE];
BYTE i;
BYTE *right_ptr,*str_token;
BYTE len;
temp = output;
right_ptr = string;
str_token = string;
while(1)
{
do
{
i = *right_ptr++;
} while((i != 0x00u) && (i != '\\') && (i != '.') );
/* Prefix '\' for every occurance of '.' & '\' */
len = (BYTE)(right_ptr-str_token-1);
memcpy(temp,str_token,len);
temp += len;
str_token += len;
if(i == '.' || i == '\\')
{
*temp = '\\';
temp++;
*temp++ = i;
str_token += 1;
}
else if(i == 0x00u || i == '/' || i == ',' || i == '>')
break;
}
*temp++ = '\0';
STRCPY_LOCAL(string,output);
}
/***************************************************************
Function:
void mDNSSDFillResRecords(mdnsd_struct *sd)
Summary:
Fills the resource-records with the information received from
sd structure-instance, in which the information is filled from
user input.
Description:
This function is used to fill the resource-records according to
format specified in RFC 1035.
In this context Service-Instance + Service-Type is called fully
qualified name. For ex: Dummy HTTP Web-Server._http._tcp.local
where Dummy HTTP Web-Server is Service-instance name
and _http._tcp.local is Service-Type
Each service-instance that needs to be advertised contains three
resource-reocrds.
1) PTR Resource-Record: This is a shared record, with service-type
as rr-name and fully-qualified name as
rr-data.
2) SRV Resource-Record: This is a unique record, with fully-
qualified name as rr-name and Host-name,
port-num as rr-data.
3) TXT Resource-Record: This is a unique record, with fully-
qualified name as rr-name and additional
information as rr-data like default-page
name (For ex: "/index.htm")
Precondition:
None
Parameters:
sd - Service-Discovery structure instance for which Resource-
records to be filled.
Returns:
None
**************************************************************/
void mDNSSDFillResRecords(mDNSProcessCtx_sd *sd)
{
BYTE *qualified_name;
BYTE srv_name_len,srv_type_len;
mDNSResourceRecord *rr_list = &(gResponderCtx.rr_list[QTYPE_PTR_INDEX]);
srv_name_len = STRLEN_LOCAL(sd->srv_name);
srv_type_len = STRLEN_LOCAL(sd->srv_type);
memset(&(gResponderCtx.rr_list[QTYPE_PTR_INDEX]),0,(sizeof(mDNSResourceRecord)));
memset(&(gResponderCtx.rr_list[QTYPE_SRV_INDEX]),0,(sizeof(mDNSResourceRecord)));
memset(&(gResponderCtx.rr_list[QTYPE_TXT_INDEX]),0,(sizeof(mDNSResourceRecord)));
/* Formatting Service-Instance name.
* And preparing a fully qualified
* Service-instance name. */
STRCPY_LOCAL(sd->sd_qualified_name,sd->srv_name);
mDNSSDFormatServiceInstance(sd->sd_qualified_name);
qualified_name = sd->sd_qualified_name + STRLEN_LOCAL(sd->sd_qualified_name);
*qualified_name++ = '.';
STRCPY_LOCAL(qualified_name,sd->srv_type);
DEBUG_MDNS_MESG(zeroconf_dbg_msg,"Fully Qualified Name: %s \r\n",sd->sd_qualified_name);
DEBUG_MDNS_PRINT(zeroconf_dbg_msg);
/* Fill-up PTR Record */
rr_list->type.Val = QTYPE_PTR;
rr_list->name = (BYTE *) (sd->srv_type);
/* Res Record Name is
* Service_Instance_name._srv-type._proto.domain */
rr_list->rdata = (BYTE *) (sd->sd_qualified_name);
qualified_name = rr_list->rdata + srv_name_len;
*qualified_name++ = '.';
STRCPY_LOCAL(qualified_name, sd->srv_type);
qualified_name = rr_list->rdata; /* Save it for later access */
/* 3 bytes extra. One for dot added between
* Serv-Name and Serv-Type. One for length byte.
* added for first-label in fully qualified name
* Other one for NULL terminator */
rr_list->rdlength.Val = srv_name_len+ srv_type_len + 3;
rr_list->ttl.Val = RESOURCE_RECORD_TTL_VAL; /* Seconds. Not sure ! Need to check */
rr_list->pOwnerCtx = (mDNSProcessCtx_common *) sd; /* Save back ptr */
rr_list->valid = 1; /* Mark as valid */
rr_list = &gResponderCtx.rr_list[QTYPE_SRV_INDEX]; /* Move onto next entry */
/* Fill-up SRV Record */
rr_list->name = (BYTE *) (sd->sd_qualified_name);
rr_list->type.Val = QTYPE_SRV;
//CLASS???
rr_list->ttl.Val = RESOURCE_RECORD_TTL_VAL;
//rdlength is calculated/assigned last
rr_list->srv.priority.Val = 0;
rr_list->srv.weight.Val = 0;
rr_list->srv.port.Val = sd->sd_port;
/* Res Record Name is
* Service_Instance_name._srv-type._proto.domain */
rr_list->rdata = (BYTE *) gHostCtx.szHostName;
/* 2 bytes extra. One for Prefix Length for first-label.
* Other one for NULL terminator */
// then, add 6-byte extra: for priority, weight, and port
rr_list->rdlength.Val = STRLEN_LOCAL(rr_list->rdata)+2+6;
rr_list->pOwnerCtx = (mDNSProcessCtx_common *) sd; /* Save back ptr */
rr_list->valid = 1; /* Mark as valid */
rr_list = &gResponderCtx.rr_list[QTYPE_TXT_INDEX]; /* Move onto next entry */
/* Fill-up TXT Record with NULL data*/
rr_list->type.Val = QTYPE_TXT;
rr_list->name = (BYTE *) (sd->sd_qualified_name);
/* Res Record data is what defined by the user */
rr_list->rdata = (BYTE *) (sd->sd_txt_rec);
/* Extra byte for Length-Byte of TXT string */
rr_list->rdlength.Val = gSDCtx.sd_txt_rec_len+1;
rr_list->ttl.Val = RESOURCE_RECORD_TTL_VAL;
rr_list->pOwnerCtx = (mDNSProcessCtx_common *) sd; /* Save back ptr */
rr_list->valid = 1; /* Mark as valid */
}
/***************************************************************
Function:
MDNSD_ERR_CODE mDNSServiceUpdate(
WORD port,
WORD txt_len,
const BYTE *txt_record)
Summary:
DNS-Service Discovery API for end-user to update the service
-advertisement, which was previously registered with
mDNSServiceRegister
Description:
This API is used by end-user application to update its service
which was previously registered. With this API end-user app
update the port number on which the service is running. It can
update the additional information of service. For example: the
default page can be updated to new page and corresponding page
name can be input to this API to update all peer machines. The
modified service will be announced with new contents on local
network.
This is an optional API and hsould be invoked only if it is
necessary.
Precondition:
mDNSServiceRegister must be invoked before this call.
Parameters:
port - Port number on which service is running
txt_len - For additional information about service like
default page (eg "index.htm") for HTTP-service.
Length of such additional information
txt_record - String of additional information (eg "index.htm")
for HTTP-service.
Returns:
MDNSD_ERR_CODE - Returns Error-code to indicate registration is
success or not.
1) MDNSD_SUCCESS - returns on success of call
2) MDNSD_ERR_INVAL - When the input parameters are invalid or
if the API is invoked in invalid state
**************************************************************/
MDNSD_ERR_CODE
mDNSServiceUpdate(WORD port,
const BYTE *txt_record)
{
mDNSProcessCtx_sd *sd = &gSDCtx;
if( sd->used)
{
sd->service_registered = 0;
sd->sd_port = port;
/* Update Port Value in SRV Resource-record */
gResponderCtx.rr_list[QTYPE_SRV_INDEX].srv.port.Val = port;
if(txt_record != NULL)
{
STRCPY_LOCAL(sd->sd_txt_rec, (BYTE *) txt_record);
sd->sd_txt_rec_len = STRLEN_LOCAL(sd->sd_txt_rec);
/* Update Resource-records for this
* Service-instance, in MDNS-SD state-
* -machine */
mDNSSDFillResRecords(sd);
sd->common.state = MDNS_STATE_NOT_READY;
}
/* Notify MDNS Stack about Service-Registration
* to get a time-slot for its own processing */
sd->service_registered = 1;
return MDNSD_SUCCESS;
}
else
return MDNSD_ERR_INVAL;
}
/***************************************************************
Function:
MDNSD_ERR_CODE mDNSServiceDeRegister()
Summary:
DNS-Service Discovery API for end-user to De-register a
service-advertisement, which was previously registered with
mDNSServiceRegister API.
Description:
This API is used by end-user application to de-register its
service-advertisement on local network. When this gets invoked
by end-user DNS-SD stack sends out Good-Bye packets to update
all peer machines that service will no longer be present. All
peer machines remove the corresponding entry from Browser list.
This is the last API that needs to be invoked by end-user
application to free-up the DNS-SD stack for some other app.
Precondition:
mDNSServiceRegister must be invoked before this call.
Parameters:
None
Returns:
MDNSD_ERR_CODE - Returns Error-code to indicate registration is
success or not.
1) MDNSD_SUCCESS - returns on success of call
2) MDNSD_ERR_INVAL - When the input parameters are invalid or
if the API is invoked in invalid state
**************************************************************/
MDNSD_ERR_CODE mDNSServiceDeRegister()
{
mDNSProcessCtx_sd *sd = &gSDCtx;
if(sd->used)
{
if(sd->sd_service_advertised == 1)
{
/* Send GoodBye Packet */
gResponderCtx.rr_list[QTYPE_PTR_INDEX].ttl.Val = 0;
gResponderCtx.rr_list[QTYPE_SRV_INDEX].ttl.Val = 0;
gResponderCtx.rr_list[QTYPE_SRV_INDEX].ttl.Val = 0;
mDNSSendRR(&gResponderCtx.rr_list[QTYPE_PTR_INDEX], 0, 0x00, 3, TRUE,FALSE);
mDNSSendRR(&gResponderCtx.rr_list[QTYPE_SRV_INDEX], 0, 0x80, 3, FALSE,FALSE);
mDNSSendRR(&gResponderCtx.rr_list[QTYPE_SRV_INDEX], 0, 0x80, 3, FALSE,TRUE);
}
/* Clear gSDCtx struct */
sd->service_registered = 0;
memset(sd,0,sizeof(mDNSProcessCtx_sd));
return MDNSD_SUCCESS;
}
else
return MDNSD_ERR_INVAL; /* Invalid Parameter */
}
/***************************************************************
Function:
MDNSD_ERR_CODE mDNSServiceRegister( ...)
Summary:
DNS-Service Discovery API for end-user to register for a
service-advertisement.
Description:
This API is used by end-user application to announce its
service on local network. All peer machines that are compliant
with Multicast-DNS & DNS-Service Discovery protocol can detect
the announcement and lists out an entry in Service-Browser list.
End-User selects an entry to connect to this service. So
ultimately this is an aid to end-user to discover any services
that he is interested in, on a local network.
This is the first API that needs to be invoked by end-user
application. Presently only Multicast-DNS & Service-discovery
stack supports only single service-advertisement. Once the
application wants to terminate the service it has to invoke
mDNSServiceDeRegister() API to free-up the DNS-SD stack for
some other application.
Precondition:
None
Parameters:
srv_name - Service Name, which is being advertised
srv_type - For a HTTP-Service its "_http._tcp.local"
_http: is application protocol preceeded with
under-score
_tcp: is lower-layer protocol on which service runs
local: is to represent service is on local-network
For a iTunes Music Sharing "_daap._tcp.local"
For a Priniting Service "_ipp._tcp.local"
Refer to http://www.dns-sd.org/ServiceTypes.html
for more service types
port - Port number on which service is running
txt_len - For additional information about service like
default page (eg "index.htm") for HTTP-service.
Length of such additional information
txt_record - String of additional information (eg "index.htm")
for HTTP-service.
auto_rename- A flag to indicate DNS-SD stack, whether to rename
the service automatically or not.
If this is set to '0' Callback parameter will be used
to indicate the conflict error and user has to select
different name and re-register with this API.
If this is set to '1' service-name will be automatically
renamed with numerical suffix.
callback - Callback function, which is user-application defined.
This callback gets invoked on completion of service-
advertisement. If an service name-conflit error is
detected and auto_rename is set to '0' callback gets
invoked with MDNSD_ERR_CONFLICT as error-code.
context - Opaque context (pointer to opaque data), which needs
to be used in callback function.
Returns:
MDNSD_ERR_CODE - Returns Error-code to indicate registration is
success or not.
1) MDNSD_SUCCESS - returns on success of call
2) MDNSD_ERR_BUSY - When already some other service is being
advertised using this DNS-SD stack
**************************************************************/
MDNSD_ERR_CODE
mDNSServiceRegister(const char *srv_name,
const char *srv_type,
WORD port,
const BYTE *txt_record,
BYTE auto_rename,
void (*call_back)(char *name, MDNSD_ERR_CODE err, void *context),
void *context)
{
if(gSDCtx.used)
{
WARN_MDNS_PRINT("mDNSServiceRegister: Some Other Service is registered" \
"Currently only One Serv-Reg is allowed \r\n");
return MDNSD_ERR_BUSY;
}
if ( (srv_name == NULL) || (srv_type == NULL) || (txt_record == NULL) )
{
return MDNSD_ERR_INVAL; // Invalid Parameter
}
/* Clear the State-Machine */
memset(&gSDCtx,0,sizeof(mDNSProcessCtx_sd));
gSDCtx.used = 1; /* Mark it as used */
gSDCtx.sd_auto_rename = auto_rename;
gSDCtx.sd_port = port;
gSDCtx.sd_service_advertised = 0;
STRCPY_LOCAL(gSDCtx.srv_name, (BYTE *)srv_name);
STRCPY_LOCAL(gSDCtx.srv_type, (BYTE *)srv_type);
gSDCtx.sd_call_back = call_back;
gSDCtx.sd_context = context;
STRCPY_LOCAL(gSDCtx.sd_txt_rec, (BYTE *) txt_record);
gSDCtx.sd_txt_rec_len = STRLEN_LOCAL(gSDCtx.sd_txt_rec);
/* Fill up Resource-records for this
* Service-instance, in MDNS-SD state-
* -machine */
mDNSSDFillResRecords(&gSDCtx);
gSDCtx.common.type = MDNS_CTX_TYPE_SD;
gSDCtx.common.state = MDNS_STATE_NOT_READY;
gSDCtx.common.nInstanceId = 0;
/* Notify MDNS Stack about Service-Registration
* to get a time-slot for its own processing */
gSDCtx.service_registered = 1;
return MDNSD_SUCCESS;
}
/***************************************************************
Function:
static void mDNSSDStateMachineReset(mdnsd_struct *sd)
Summary:
Resets DNS-SD state-machine
Description:
This function is used to reset all state-variables related
to DNS-SD state-machine.
Precondition:
None
Parameters:
sd - Service Discovery structure instance
Returns:
None
**************************************************************/
/***************************************************************
Function:
void mDNSSDProbe(mdnsd_struct *sd)
Summary:
Sends out Multicast-DNS SD probe-packet with chosen service-name
The Query-type is of SRV.
Description:
This function is used to send out mDNS-probe packet for
checking uniqueness of selected service-instance-name. This
function makes use of mDNSSendQuery to send out DNS-Query with
chosen service-name to Multicast-Address. The type of such query
is QTYPE_SRV.
If any other machine is using same service-name, it responds with
a reply and this host has to select different service-name.
Precondition:
None
Parameters:
sd - Service Discovery structure instance
Returns:
None
**************************************************************/
/***************************************************************
Function:
void mDNSSDAnnounce(mdnsd_struct *sd)
Summary:
Sends out Multicast-DNS SD packet with SRV Resource-Record.
Description:
This function is used to send out DNS-SD SRV resource-record
Announce packet for announcing the service-name on local network.
This function makes use of mDNSSendRR to send out DNS-Resource-
Record with chosen service-name+service-type as rr-name and the
host-name, port-number as rr-data.
This announcement updates DNS-caches of neighbor machines on
the local network.
Precondition:
None
Parameters:
sd - Service Discovery structure instance
Returns:
None
**************************************************************/
void mDNSAnnounce(mDNSResourceRecord *pRR)
{
if( FALSE ==
mDNSSendRR(
pRR,
0,
0x80,
1,
TRUE,
TRUE
)
)
{
WARN_MDNS_PRINT("mDNSAnnounce: Error in sending out Announce pkt \r\n");
}
}
/***************************************************************
Function:
void mDNSSDTask(mdnsd_struct *sd, struct mDNSResourceRecord *rr,
MDNS_MSG_HEADER *mDNS_header)
Summary:
Processes the received Resource-Record, which can either be
a query or response.
Description:
This function is an extension function mDNSResponder. If incoming
query/response's resource-record doesn't match with Host-Name then
the corresponding resource-record will be forwarded to DNS-SD state
machine for further processing. This is invoked from mDNSResponder
if the service is marked as registered.
This routine compares it with existing resource-records and checks
for any conflict. If it is a query and there's match with the
publihed resource-records then response will be sent back with
that resource-reocrd.
Precondition:
None
Parameters:
sd - Service Discovery structure instance
rr - Resource-Record filled with either DNS-query/
response
mDNS_header - Multicast-DNS Message header filled with incoming
query/response information
Returns:
None
**************************************************************/
/***************************************************************
Function:
void mDNSSDProcess(mdnsd_struct *sd)
Summary:
Main routine for DNS-SD state-machine.
Description:
This function is main routine of DNS-SD state-machine and invoked
from Multicast-DNS state-machine routine. Based on current state
and the additional flag-bits the coresponding actions will be taken
Once the user selected service-name is finalized the user-callback
registered with DNS-SD will be invoked to notify that the service
is advertised successfully. Two gracious announcement packets with
service-information will be sent out to update Neighbor caches and
existing service-browsers.
Precondition:
None
Parameters:
sd - Service Discovery structure instance
Returns:
None
**************************************************************/
#ifdef DEBUG_MDNS
MDNS_MSG_HEADER *dbg_p_mDNS_header;
mDNSResourceRecord *dbg_p_res_rec;
#endif
MDNS_STATIC WORD mDNSFetch(WORD wOffset, WORD wLen, BYTE *pcString)
{
WORD rc;
UDPSetRxBuffer(wOffset);
rc = UDPGetArray(pcString, wLen);
return rc;
}
/***************************************************************
Function:
static WORD mDNSDeCompress(WORD wPos,
BYTE *pcString,
BOOL bFollowPtr,
BYTE cElement,
BYTE cDepth)
Summary:
Read a string from a resource record, from the Multicast-DNS socket buffer.
Description:
This function reads a string to the Multicast-DNS socket,
ensuring that it is properly formatted.
String may be reconstructed through decompression if necessary.
Decompression pointer traversal is done in place, recursively, in UDP's RxBuffer.
cDepth represents the recursion depth, for debugging purpose.
cElement represents the number of elements in the string. For example,
"ezconfig._http._tcp.local" has 4 elements.
bFollowPtr indicates if DNS compression offset needs to be followed. That is, if
we should reconstruct a compressed string.
The reconstructed string is placed in pcString, if it is not NULL.
For DNS message compression format, see RFC 1035, section 4.1.4.
Precondition:
UDP socket is obtained and ready for writing.
wPos correctly reflect the current position in the UDP RxBuffer.
Parameters:
String - the string to write to the UDP socket.
Returns:
Number of bytes in THIS resource record field (in RFC 1035's term, NAME or RDATA).
UDP RxBuffer pointer is repositioned to the place right after THIS resource record field.
**************************************************************/
MDNS_STATIC WORD mDNSDeCompress(WORD wPos, BYTE *pcString, BOOL bFollowPtr, BYTE cElement, BYTE cDepth)
{
WORD rr_len = 0; // As is in the packet. Could be in compressed format.
WORD startOffset, endOffset, currOffset;
BYTE i, tmp;
WORD offset_in_ptr;
WORD len;
BYTE substr_len;
currOffset = startOffset = wPos;
while (1)
{
rr_len++;
if(!UDPGet(&substr_len))
goto mDNSDeCompress_done;
if(substr_len == 0u)
{
if (pcString)
{
*pcString++ = '\0';
}
goto mDNSDeCompress_done;
}
if((substr_len & 0xC0) == 0xC0) // b'11 at MSb indicates compression ptr
{
offset_in_ptr = substr_len & 0x3F; // the rest of 6 bits is part of offset_in_ptr.
offset_in_ptr = offset_in_ptr << 8;
/* Remove label-ptr byte */
rr_len++;
UDPGet(&i);
offset_in_ptr += i;
if (bFollowPtr)
{
cDepth++;
DEBUG_MDNS_MESG(zeroconf_dbg_msg, "follow ptr: h'%X, ", offset_in_ptr);
DEBUG_MDNS_PRINT(zeroconf_dbg_msg);
DEBUG_MDNS_MESG(zeroconf_dbg_msg, "%d\r\n", cDepth);
DEBUG_MDNS_PRINT(zeroconf_dbg_msg);
UDPSetRxBuffer(offset_in_ptr);
len = mDNSDeCompress(offset_in_ptr, pcString, bFollowPtr, cElement, cDepth);
// compressed ptr is always the last element
goto mDNSDeCompress_done;
}
goto mDNSDeCompress_done;
}
else
{
if (pcString)
{
if (cElement > 0)
{
// not the first element in name
*pcString++ = '.';
}
UDPGetArray(pcString, substr_len);
pcString += substr_len;
}
else
{
i = substr_len;
while (i--)
{
UDPGet(&tmp);
}
}
cElement++;
rr_len += substr_len;
}
}
mDNSDeCompress_done:
endOffset = startOffset + rr_len;
UDPSetRxBuffer(endOffset);
return rr_len;
}
/***************************************************************
Function:
void mDNSResponder(void)
Summary:
Acts as Multicast-DNS respoder & replies when it receives
a query.
Description:
This function is used as mDNS-Responder. On initialization of
Multicast-DNS stack, this function Opens up mDNS_socket
(UDP-Socket) for Mulitcast-Address (224.0.0.251).
This function gets polled from mDNSProcess for every iteration.
mDNSResponder constantly monitors the packets being sent to
Multicast-Address, to check whether it is a conflict with
its own host-name/resource-record names. It also verifies
whether incoming query is for its own Host-name/Resource-
Record, in which case it sends back a reply with corresponding
Resource-Record.
Precondition:
UDP socket (mDNS_socket) is obtained and ready for writing.
A UDP socket must be available before this function is called.
MAX_UDP_SOCKETS may need to be increased if other modules use
UDP sockets.
Parameters:
None
Returns:
None
**************************************************************/
MDNS_STATIC BOOL
mDNSTieBreaker(mDNSResourceRecord *their, mDNSResourceRecord *our)
{
BOOL WeWonTheTieBreaker = TRUE;
BYTE i;
if (their->type.Val == QTYPE_A)
{
for (i = 0; i<= 3; i++)
{
if (their->ip.v[i] < our->ip.v[i])
{
WeWonTheTieBreaker = TRUE;
break;
}
else if (their->ip.v[i] > our->ip.v[i])
{
WeWonTheTieBreaker = FALSE;
break;
}
}
}
else if (their->type.Val == QTYPE_SRV)
{
if (their->srv.port.Val >= our->srv.port.Val)
{
WeWonTheTieBreaker = FALSE;
}
}
DEBUG0_MDNS_PRINT( (char *) (WeWonTheTieBreaker ? " tie-breaker won\r\n" : " tie-breaker lost\r\n") );
return WeWonTheTieBreaker;
}
MDNS_STATIC BYTE
mDNSProcessIncomingRR(MDNS_RR_GROUP tag,
MDNS_MSG_HEADER *pmDNSMsgHeader,
WORD idxGroup,
WORD idxRR)
{
mDNSResourceRecord res_rec;
BYTE name[MAX_RR_NAME_SIZE];
BYTE i,j;
WORD len;
BYTE tmp;
mDNSProcessCtx_common *pOwnerCtx;
mDNSResourceRecord *pMyRR;
BOOL WeWonTheTieBreaker = FALSE;
BOOL bMsgIsAQuery; // QUERY or RESPNSE ?
BOOL bSenderHasAuthority; // Sender has the authority ?
bMsgIsAQuery = (pmDNSMsgHeader->flags.bits.qr == 0);
bSenderHasAuthority = (pmDNSMsgHeader->flags.bits.qr == 1);
res_rec.name = name; // for temporary name storage.
#ifdef DEBUG_MDNS
dbg_p_mDNS_header = &mDNS_header;
dbg_p_res_rec = &res_rec;
#endif
DEBUG0_MDNS_MESG(
zeroconf_dbg_msg,
" rec [%d:%d]\t",
idxGroup, idxRR);
DEBUG0_MDNS_PRINT(zeroconf_dbg_msg);
// NAME
memset(name, 0, MAX_RR_NAME_SIZE);
len = mDNSDeCompress(g_mDNS_offset, name, TRUE, 0, 0);
g_mDNS_offset += len;
// TYPE & CLASS
UDPGet(&res_rec.type.v[1]);
UDPGet(&res_rec.type.v[0]);
UDPGet(&res_rec.class.v[1]);
UDPGet(&res_rec.class.v[0]);
g_mDNS_offset += 4;
DEBUG0_MDNS_PRINT("Name: ");
DEBUG0_MDNS_PRINT((char *) name);
DEBUG0_MDNS_MESG(zeroconf_dbg_msg," Type: %d", res_rec.type.Val);
DEBUG0_MDNS_PRINT((char*)zeroconf_dbg_msg);
DEBUG0_MDNS_PRINT("\r\n");
// Do the first round name check
for (i = 0; i < MAX_RR_NUM; i++)
{
gResponderCtx.rr_list[i].bNameAndTypeMatched = FALSE;
if (
!strcmp_local_ignore_case((void *)name, gResponderCtx.rr_list[i].name)
&&
((res_rec.type.Val == QTYPE_ANY) ||
(res_rec.type.Val == gResponderCtx.rr_list[i].type.Val))
)
{
gResponderCtx.rr_list[i].bNameAndTypeMatched = TRUE;
}
else if (
(tag == MDNS_RR_GROUP_QD)
&&
!strcmp_local_ignore_case(name,(BYTE *) "_services._dns-sd._udp.local")
&&
(res_rec.type.Val == QTYPE_PTR)
)
{
gResponderCtx.rr_list[i].bNameAndTypeMatched = TRUE;
}
}
// Only AN, NS, AR records have extra fields
if ( tag == MDNS_RR_GROUP_QD )
{
goto ReviewStage;
}
// Now retrieve those extra fields
UDPGet(&res_rec.ttl.v[3]); // Time to live
UDPGet(&res_rec.ttl.v[2]);
UDPGet(&res_rec.ttl.v[1]);
UDPGet(&res_rec.ttl.v[0]);
UDPGet(&res_rec.rdlength.v[1]); // Response length
UDPGet(&res_rec.rdlength.v[0]);
g_mDNS_offset += 6;
// The rest is record type dependent
switch (res_rec.type.Val)
{
case QTYPE_A:
UDPGet(&res_rec.ip.v[0]); // Read out IP address
UDPGet(&res_rec.ip.v[1]);
UDPGet(&res_rec.ip.v[2]);
UDPGet(&res_rec.ip.v[3]);
g_mDNS_offset += 4;
DEBUG_MDNS_MESG(zeroconf_dbg_msg, " [A]: TTL=%ld\r\n", res_rec.ttl.Val);
DEBUG_MDNS_PRINT((char*)zeroconf_dbg_msg);
break;
case QTYPE_PTR:
memset(name, 0 , MAX_RR_NAME_SIZE);
len = mDNSDeCompress(g_mDNS_offset, name, TRUE, 0, 0);
g_mDNS_offset += len;
DEBUG_MDNS_MESG(zeroconf_dbg_msg, " [PTR]: TTL=%ld RDATA=%s\r\n", res_rec.ttl.Val,name);
DEBUG_MDNS_PRINT((char*)zeroconf_dbg_msg);
break;
case QTYPE_SRV:
UDPGet(&res_rec.srv.priority.v[1]); // Put Priority, weight, port
UDPGet(&res_rec.srv.priority.v[0]);
UDPGet(&res_rec.srv.weight.v[1]);
UDPGet(&res_rec.srv.weight.v[0]);
UDPGet(&res_rec.srv.port.v[1]);
UDPGet(&res_rec.srv.port.v[0]);
g_mDNS_offset += 6;
memset(name, 0 , MAX_RR_NAME_SIZE);
len = mDNSDeCompress(g_mDNS_offset, name, TRUE, 0, 0);
g_mDNS_offset += len;
DEBUG_MDNS_MESG(zeroconf_dbg_msg, " [SRV]: TTL=%ld RDATA=%s\r\n", res_rec.ttl.Val,name);
DEBUG_MDNS_PRINT((char*)zeroconf_dbg_msg);
break;
case QTYPE_TXT:
i = res_rec.rdlength.Val;
while (i--)
{
UDPGet(&tmp);
}
g_mDNS_offset += res_rec.rdlength.Val;
DEBUG_MDNS_MESG(zeroconf_dbg_msg, " [TXT]: (%d bytes)\r\n", res_rec.rdlength.Val);
DEBUG_MDNS_PRINT((char*)zeroconf_dbg_msg);
break;
default:
// Still needs to read it off
i = res_rec.rdlength.Val;
while (i--)
{
UDPGet(&tmp);
}
g_mDNS_offset += res_rec.rdlength.Val;
DEBUG_MDNS_MESG(zeroconf_dbg_msg, " [*]: (%d bytes)\r\n", res_rec.rdlength.Val);
DEBUG_MDNS_PRINT((char*)zeroconf_dbg_msg);
break;
}
// We now have all info about this received RR.
ReviewStage:
// Do the second round
for (i = 0; i < MAX_RR_NUM; i++)
{
pMyRR = &(gResponderCtx.rr_list[i]);
pOwnerCtx = gResponderCtx.rr_list[i].pOwnerCtx;
if ( (!pMyRR->bNameAndTypeMatched) || (pOwnerCtx == NULL) )
{
// do nothing
}
else if (
bMsgIsAQuery &&
(tag == MDNS_RR_GROUP_QD) &&
(pOwnerCtx->state == MDNS_STATE_DEFEND)
)
{
// Simple reply to an incoming DNS query.
// Mark all of our RRs for reply.
for (j = 0; j < MAX_RR_NUM; j++)
{
gResponderCtx.rr_list[j].bResponseRequested = TRUE;
}
}
else if (
bMsgIsAQuery &&
(tag == MDNS_RR_GROUP_AN) &&
(pOwnerCtx->state == MDNS_STATE_DEFEND)
)
{
// An answer in the incoming DNS query.
// Look for possible duplicate (known) answers suppression.
if ((((res_rec.type.Val == QTYPE_PTR) && (res_rec.ip.Val == gResponderCtx.rr_list[i].ip.Val))
||
(!strcmp_local_ignore_case(name, gResponderCtx.rr_list[i].rdata)))
&&
(res_rec.ttl.Val > (gResponderCtx.rr_list[i].ttl.Val/2))
)
{
gResponderCtx.rr_list[i].bResponseSuppressed = TRUE;
DEBUG_MDNS_PRINT(" rr suppressed\r\n");
}
}
else if (
bMsgIsAQuery &&
(tag == MDNS_RR_GROUP_NS) &&
((pOwnerCtx->state == MDNS_STATE_PROBE) ||
(pOwnerCtx->state == MDNS_STATE_ANNOUNCE))
)
{
// Simultaneous probes by us and sender of this DNS query.
// Mark as a conflict ONLY IF we lose the Tie-Breaker.
WeWonTheTieBreaker = mDNSTieBreaker(&res_rec,
&(gResponderCtx.rr_list[i]));
if (!WeWonTheTieBreaker)
{
pOwnerCtx->bProbeConflictSeen = TRUE;
pOwnerCtx->nProbeConflictCount++;
}
UDPDiscard();
return 0;
}
else if (
!bMsgIsAQuery &&
bSenderHasAuthority &&
(tag == MDNS_RR_GROUP_AN) &&
((pOwnerCtx->state == MDNS_STATE_PROBE) ||
(pOwnerCtx->state == MDNS_STATE_ANNOUNCE))
)
{
// An authoritative DNS response to our probe/announcement.
// Mark as a conflict. Effect a re-name, followed by a
// re-probe.
pOwnerCtx->bProbeConflictSeen = TRUE;
pOwnerCtx->nProbeConflictCount++;
UDPDiscard();
return 0;
}
else if (
bMsgIsAQuery &&
(tag == MDNS_RR_GROUP_NS) &&
(pOwnerCtx->state == MDNS_STATE_DEFEND)
)
{
// A probe by the sender conflicts with our established record.
// Need to defend our record. Effect a DNS response.
INFO_MDNS_PRINT("Defending RR: \r\n");
pMyRR->bResponseRequested = TRUE;
UDPDiscard();
return 0;
}
else if (
!bMsgIsAQuery &&
bSenderHasAuthority &&
(tag == MDNS_RR_GROUP_AN) &&
(pMyRR->type.Val != QTYPE_PTR ) && // No one can claim authority on shared RR
(pOwnerCtx->state == MDNS_STATE_DEFEND)
)
{
// Sender claims that it also has the authority on
// a unique (non-shared) record that we have already established authority.
// Effect a re-probe.
pOwnerCtx->bLateConflictSeen = TRUE;
UDPDiscard();
return 0;
}
}
return 0;
}
MDNS_STATIC void mDNSResponder(void)
{
MDNS_MSG_HEADER mDNS_header;
static enum {
MDNS_RESPONDER_INIT = 0,
MDNS_RESPONDER_LISTEN,
} mDNS_responder_state= MDNS_RESPONDER_INIT;
WORD len;
WORD i,j,count;
WORD rr_count[4];
MDNS_RR_GROUP rr_group[4];
BOOL bMsgIsComplete;
g_mDNS_offset = 0;
if(mDNS_socket == INVALID_UDP_SOCKET)
{
mDNS_responder_state = MDNS_RESPONDER_INIT;
}
switch(mDNS_responder_state)
{
case MDNS_RESPONDER_INIT:
/* Open a UDP socket for inbound and outbound transmission
* Since we expect to only receive multicast packets and
* only send multicast packets the remote NodeInfo
* parameter is initialized to Multicast-IP (224.0.0.251)
* corresponding Multicast MAC-Address (01:00:5E:00:00:FB) */
mDNSRemote.IPAddr.v[0] = 0xE0;
mDNSRemote.IPAddr.v[1] = 0;
mDNSRemote.IPAddr.v[2] = 0;
mDNSRemote.IPAddr.v[3] = 0xFB;
mDNSRemote.MACAddr.v[0]=0x01;
mDNSRemote.MACAddr.v[1]=0x00;
mDNSRemote.MACAddr.v[2]=0x5E;
mDNSRemote.MACAddr.v[3]=0x00;
mDNSRemote.MACAddr.v[4]=0x00;
mDNSRemote.MACAddr.v[5]=0xFB;
mDNS_socket = UDPOpen(MDNS_PORT, &mDNSRemote, MDNS_PORT);
if(mDNS_socket == INVALID_UDP_SOCKET)
{
WARN_MDNS_PRINT("mDNSResponder: Can't open Multicast-DNS UDP-Socket \r\n");
return;
}
else
mDNS_responder_state = MDNS_RESPONDER_LISTEN ;
/* Called from mDNSInitialize. So return immediately */
break;
case MDNS_RESPONDER_LISTEN:
// Do nothing if no data is waiting
if(!UDPIsGetReady(mDNS_socket))
return;
if ( UDPSocketInfo[mDNS_socket].remotePort != MDNS_PORT )
{
// If the remote port (sender's src port)
// is not MDNS_PORT (5353), then it is a multicast query
// reqeusting a unicast response (even though the packet
// was sent to the multicast group IP:MDNS_PORT ).
// The response needs to be unicast, and sent
// to sender:port, NOT to the multicast group IP:MDSN_PORT
// (i.e., 224.0.0.251:5353).
// See section 8.5, draft-cheshire-dnsext-multicastdns-08.txt.
}
else
{
/* Reset the Remote-node information in UDP-socket */
memcpy((void*)&UDPSocketInfo[mDNS_socket].remoteNode,
(const void*)&mDNSRemote, sizeof(mDNSRemote));
UDPSocketInfo[mDNS_socket].remotePort = MDNS_PORT;
UDPSocketInfo[mDNS_socket].localPort = MDNS_PORT;
}
// Retrieve the mDNS header
len = mDNSFetch(0, sizeof(mDNS_header), (BYTE *) &mDNS_header);
mDNS_header.query_id.Val = swaps(mDNS_header.query_id.Val);
mDNS_header.flags.Val = swaps(mDNS_header.flags.Val);
mDNS_header.nQuestions.Val = swaps(mDNS_header.nQuestions.Val);
mDNS_header.nAnswers.Val = swaps(mDNS_header.nAnswers.Val);
mDNS_header.nAuthoritativeRecords.Val = swaps(mDNS_header.nAuthoritativeRecords.Val);
mDNS_header.nAdditionalRecords.Val = swaps(mDNS_header.nAdditionalRecords.Val);
g_mDNS_offset += len; // MUST BE 12
if ( (mDNS_header.flags.bits.qr == 0) )
{
DEBUG0_MDNS_PRINT("rx QUERY \r\n");
}
else
{
DEBUG0_MDNS_PRINT("rx RESPONSE \r\n");
}
bMsgIsComplete = (mDNS_header.flags.bits.tc == 0); // Message is not truncated.
rr_count[0] = mDNS_header.nQuestions.Val;
rr_group[0] = MDNS_RR_GROUP_QD;
rr_count[1] = mDNS_header.nAnswers.Val;
rr_group[1] = MDNS_RR_GROUP_AN;
rr_count[2] = mDNS_header.nAuthoritativeRecords.Val;
rr_group[2] = MDNS_RR_GROUP_NS;
rr_count[3] = mDNS_header.nAdditionalRecords.Val;
rr_group[3] = MDNS_RR_GROUP_AR;
for (i = 0; i < MAX_RR_NUM; i++)
{
// Reset flags
gResponderCtx.rr_list[i].bNameAndTypeMatched = FALSE;
if (gResponderCtx.bLastMsgIsIncomplete)
{
// Do nothing.
// Whether a reply is needed is determined only when all parts
// of the message are received.
// Ideally, we want to verify that the current message is the
// continuation of the previous message.
// Don't have a cost-effective way to do this yet.
}
else
{
// Start of a new message
gResponderCtx.rr_list[i].bResponseRequested = FALSE;
gResponderCtx.rr_list[i].bResponseSuppressed = FALSE;
}
}
for (i=0; i<4; i++) // for all 4 groups: QD, AN, NS, AR
{
for(j=0; j < rr_count[i]; j++) // RR_count = {#QD, #AN, #NS, #AR}
{
mDNSProcessIncomingRR
(
rr_group[i],
&mDNS_header,
i,
j
);
}
}
// Record the fact, for the next incoming message.
gResponderCtx.bLastMsgIsIncomplete = (bMsgIsComplete == FALSE);
// Do not reply any answer if the current message is not the last part of
// the complete message.
// Future parts of the message may request some answers be suppressed.
if (!bMsgIsComplete)
{
DEBUG0_MDNS_PRINT(" truncated msg.\r\n");
return;
}
// Count all RRs marked as "reply needed".
count = 0;
for (i = 0; i < MAX_RR_NUM; i++)
{
if ((gResponderCtx.rr_list[i].pOwnerCtx != NULL) &&
(gResponderCtx.rr_list[i].pOwnerCtx->state == MDNS_STATE_DEFEND) &&
(gResponderCtx.rr_list[i].bResponseRequested == TRUE) &&
(gResponderCtx.rr_list[i].bResponseSuppressed == FALSE)
)
{
count++;
}
}
// Send all RRs marked as "reply needed".
j = 1;
for (i = 0; (count > 0) && (i < MAX_RR_NUM); i++)
{
if ((gResponderCtx.rr_list[i].pOwnerCtx != NULL) &&
(gResponderCtx.rr_list[i].pOwnerCtx->state == MDNS_STATE_DEFEND) &&
(gResponderCtx.rr_list[i].bResponseRequested == TRUE) &&
(gResponderCtx.rr_list[i].bResponseSuppressed == FALSE) )
{
mDNSSendRR(
&gResponderCtx.rr_list[i],
mDNS_header.query_id.Val,
(gResponderCtx.rr_list[i].type.Val == QTYPE_PTR)?
(0x00):(0x80), // flush, except for PTR; for Conformance Test.
count, // MAX_RR_NUM answers;
(j==1)?TRUE:FALSE, // Is this the first RR?
(j==count)?TRUE:FALSE // Is this the last RR?
);
j++;
}
}
// end of MDNS_RESPONDER_LISTEN
break;
default:
break;
}
return;
}
/***************************************************************
Function:
static void mDNSStateMachineReset(BOOL bResetProbeCount)
Summary:
Resets Multicast-DNS state-machine
Description:
This function is used to reset all global-variables related
to Multicast-DNS state-machine.
Precondition:
None
Parameters:
None
Returns:
None
**************************************************************/
/***************************************************************
Function:
static void mDNSProbe(BYTE *name, MDNS_QTYPE q_type)
Summary:
Sends out Multicast-DNS probe packet with Host-name
Description:
This function is used to send out mDNS-probe packet for
checking uniqueness of selected host-name. This function makes
use of mDNSSendQuery to send out DNS-Query with chosen host-name
to Multicast-Address.
If any other machine is using same host-name, it responds with
a reply and this host has to select different name.
Precondition:
None
Parameters:
name - Selected Host-Name
Type - Query Type, which is of TYPE_A
Returns:
None
**************************************************************/
/***************************************************************
Function:
static void mDNSAnnounce(BYTE *name, MDNS_QTYPE q_type)
Summary:
Sends out Multicast-DNS Announce/Claim packet with Host-name
Description:
This function is used to send out mDNS-Announce packet for
announcing the selected host-name. This function makes
use of mDNSSendRR to send out DNS-Resource-Rec with chosen
host-name to Multicast-Address.
This announcement updates DNS-caches of neighbor machines on
the local network.
Precondition:
None
Parameters:
name - Selected Host-Name
Type - Query Type, which is of TYPE_A
Returns:
None
**************************************************************/
/***************************************************************
Function:
void mDNSInitialize(void)
Summary:
Initialization routine for Multicast-DNS (mDNS) state-machine.
Description:
This is initialization function for mDNS and invoked from init
portion of Main-function.
This initalizes the Multicast-DNS Responder (mDNSResponder) by
opening up Multicast UDP socket on which mDNSResponder keeps on
listening for incoming queries/responses from neighbor machines.
The host-name chosen is initially seeded from DEFUALT_HOST_NAME
defined in TCPIPConfig.h.
Precondition:
None
Parameters:
None
Returns:
None
**************************************************************/
void mDNSFillHostRecord(void)
{
BYTE i;
// Fill the type A resource record
gResponderCtx.rr_list[QTYPE_A_INDEX].name = gHostCtx.szHostName;
gResponderCtx.rr_list[QTYPE_A_INDEX].type.Val = QTYPE_A;
// CLASS???
gResponderCtx.rr_list[QTYPE_A_INDEX].ttl.Val = RESOURCE_RECORD_TTL_VAL;
gResponderCtx.rr_list[QTYPE_A_INDEX].rdlength.Val = 4u; // 4-byte for IP address
for (i=0; i<=3; i++)
gResponderCtx.rr_list[QTYPE_A_INDEX].ip.v[i] = AppConfig.MyIPAddr.v[i];
gResponderCtx.rr_list[QTYPE_A_INDEX].valid = 1;
gResponderCtx.rr_list[QTYPE_A_INDEX].pOwnerCtx = (mDNSProcessCtx_common *) &gHostCtx;
}
MDNSD_ERR_CODE mDNSHostRegister(const char *host_name)
{
if (host_name != NULL)
{
STRCPY_LOCAL(gHostCtx.szUserChosenHostName, (BYTE *) host_name);
}
else
{
#ifdef MY_DEFAULT_HOST_NAME
STRCPY_LOCAL(gHostCtx.szUserChosenHostName, (BYTE *) MY_DEFAULT_HOST_NAME);
#else
STRCPY_LOCAL(gHostCtx.szUserChosenHostName, (BYTE *) "MyHost");
#endif
}
STRCPY_LOCAL(gHostCtx.szHostName, gHostCtx.szUserChosenHostName);
STRCPY_LOCAL(gHostCtx.szHostName+STRLEN_LOCAL(gHostCtx.szHostName), (BYTE *) ".");
STRCPY_LOCAL(gHostCtx.szHostName+STRLEN_LOCAL(gHostCtx.szHostName), (BYTE *) CONST_STR_local);
mDNSResetCounters((mDNSProcessCtx_common *) &gHostCtx, TRUE);
gHostCtx.common.type = MDNS_CTX_TYPE_HOST;
gHostCtx.common.state = MDNS_STATE_INIT;
gHostCtx.common.nInstanceId = 0;
mDNSFillHostRecord();
gResponderCtx.bLastMsgIsIncomplete = FALSE;
gHostCtx.common.state = MDNS_STATE_INIT;
return MDNSD_SUCCESS;
}
void mDNSInitialize(const char *szHostName)
{
gResponderCtx.query_id.Val = 0;
gResponderCtx.prev_ipaddr.Val = AppConfig.MyIPAddr.Val;
/* Initial Host-Name is seeded from DEFUALT_HOST_NAME
* configured in TCPIPConfig.h. Later on if a name-conflict
* is detected it'll be automatically renamed with a
* numerical-label extenstion */
if ( szHostName != NULL )
{
mDNSHostRegister(szHostName);
}
else
{
#ifdef MY_DEFAULT_HOST_NAME
mDNSHostRegister((const char *) MY_DEFAULT_HOST_NAME); // TODO: pick it up from names stored in EEPROM
#else
mDNSHostRegister((const char *) "ZCHOST"); // Hardcoded default.
#endif
}
if(!MACIsLinked())
{
gHostCtx.common.state = MDNS_STATE_INTF_NOT_CONNECTED;
return;
}
/* Initialize MDNS-Responder by opening up
* Multicast-UDP-Socket */
mDNSResponder();
gHostCtx.common.state = MDNS_STATE_INIT;
return;
}
/***************************************************************
Function:
void mDNSProcess(void)
Summary:
Main routine for Multicast-DNS (mDNS) state-machine.
Description:
This function is polled from Main-Routine & Designed to support
co-operative multi-tasking. This has to retrun to Main, if we
have to wait for longer durations.
This is the main routine of mDNS state-machine. Based on current
state and additional flag-bits the coresponding actions will be
taken.
Once the chosen host-name is finalized two gracious announcement
packets with host-name will be sent out to update Neighbor DNS-
-caches
Precondition:
mDNSInitialize should have been called before.
Parameters:
None
Returns:
None
**************************************************************/
void mDNSProcessInternal(mDNSProcessCtx_common *pCtx)
{
BOOL bIsHost = (((void *) pCtx) == ((void *) &gHostCtx));
switch(pCtx->state)
{
case MDNS_STATE_HOME:
DEBUG_MDNS_PRINT("MDNS_STATE_HOME: Wrong state \r\n");
break;
case MDNS_STATE_NOT_READY: // SD starts from here. SD only.
if(gHostCtx.common.state != MDNS_STATE_DEFEND)
{
/* Multicast DNS is not ready */
return;
}
else
{
/* Multicast DNS is ready now */
pCtx->state = MDNS_STATE_INIT;
pCtx->time_recorded = 0;
}
INFO_MDNS_PRINT("MDNS_STATE_NOT_READY --> MDNS_STATE_INIT \r\n");
break;
case MDNS_STATE_INTF_NOT_CONNECTED: // HOST starts from here. HOST only.
if(!MACIsLinked())
return;
else
{
/* Interface is connected now */
pCtx->state = MDNS_STATE_IPADDR_NOT_CONFIGURED;
pCtx->time_recorded = 0;
}
// No break. Fall through
case MDNS_STATE_IPADDR_NOT_CONFIGURED: // HOST only.
{
// Wait until IP addr is configured ...
if (AppConfig.MyIPAddr.Val == 0)
break;
pCtx->state = MDNS_STATE_INIT;
pCtx->time_recorded = 0;
INFO_MDNS_PRINT("MDNS_STATE_IPADDR_NOT_CONFIGURED --> MDNS_STATE_INIT \r\n");
// No break. Fall through
}
case MDNS_STATE_INIT:
{
/* DEBUG_MDNS_MESG(zeroconf_dbg_msg,"MDNS_STATE_INIT \r\n");
DEBUG_MDNS_PRINT((char*)zeroconf_dbg_msg); */
pCtx->bConflictSeenInLastProbe = FALSE;
switch ( zgzc_wait_for(&(pCtx->random_delay), &(pCtx->event_time), &(pCtx->time_recorded)) )
{
case ZGZC_STARTED_WAITING:
// Need to choose Random time between 0-MDNS_PROBE_WAIT msec
pCtx->random_delay = (TICK)((rand()% (MDNS_PROBE_WAIT) * (TICK_SECOND/1000)));
DEBUG_MDNS_MESG(zeroconf_dbg_msg,"MDNS_PROBE_WAIT Random Delay: %ld ticks\r\n",
pCtx->random_delay);
DEBUG_MDNS_PRINT((char*)zeroconf_dbg_msg);
// Intentional fall-through
case ZGZC_KEEP_WAITING:
// Not Completed the delay proposed
return;
}
// Completed the delay required
DEBUG_MDNS_MESG(zeroconf_dbg_msg,"MDNS_PROBE_WAIT Random Delay: %ld ticks" \
" Completed \r\n",
pCtx->random_delay);
DEBUG_MDNS_PRINT((char *)zeroconf_dbg_msg);
// Clear all counters
mDNSResetCounters(pCtx, TRUE);
pCtx->state = MDNS_STATE_PROBE;
INFO_MDNS_PRINT("MDNS_STATE_INIT --> MDNS_STATE_PROBE \r\n");
// No break. Fall through
}
case MDNS_STATE_PROBE:
case MDNS_STATE_ANNOUNCE:
{
//DEBUG_MDNS_PRINT("MDNS_STATE_PROBE \n");
// or
//DEBUG_MDNS_PRINT("MDNS_CLAIM \n");
if(pCtx->bProbeConflictSeen)
{
pCtx->bConflictSeenInLastProbe = TRUE;
INFO_MDNS_PRINT("Conflict detected. Will rename\r\n");
/* Conflict with selected name */
pCtx->state = MDNS_STATE_PROBE;
// Do not reset nProbeConflictCount if in PROBE state
mDNSResetCounters(
pCtx,
(pCtx->state == MDNS_STATE_PROBE)?FALSE:TRUE
);
if ( bIsHost )
{
// Rename host name
mDNSRename(
gHostCtx.szUserChosenHostName,
++gHostCtx.common.nInstanceId,
(BYTE *) CONST_STR_local,
gHostCtx.szHostName,
MAX_HOST_NAME_SIZE);
INFO_MDNS_MESG(zeroconf_dbg_msg,"New host name : %s \r\n",
gHostCtx.szHostName);
INFO_MDNS_PRINT(zeroconf_dbg_msg);
}
else
{
// Rename service instance name
if(gSDCtx.sd_auto_rename)
{
mDNSRename(
gSDCtx.srv_name,
++gSDCtx.common.nInstanceId,
gSDCtx.srv_type,
gSDCtx.sd_qualified_name,
MAX_LABEL_SIZE);
/* Reset Multicast-UDP socket */
UDPClose(mDNS_socket);
mDNS_socket = INVALID_UDP_SOCKET;
mDNSResponder();
}
else
{
gSDCtx.service_registered = 0;
gSDCtx.used = 0;
if ( gSDCtx.sd_call_back != NULL)
{
gSDCtx.sd_call_back((char *)gSDCtx.srv_name,
MDNSD_ERR_CONFLICT,
gSDCtx.sd_context);
}
}
}
break;
}
SET_PROBE_ANNOUNCE_TIMER:
switch ( zgzc_wait_for(&(pCtx->random_delay), &(pCtx->event_time), &(pCtx->time_recorded)) )
{
case ZGZC_STARTED_WAITING:
if (pCtx->state == MDNS_STATE_PROBE)
{
if (((pCtx->nProbeCount >= MDNS_PROBE_NUM) && !pCtx->bConflictSeenInLastProbe) ||
(pCtx->nProbeConflictCount >= MDNS_MAX_PROBE_CONFLICT_NUM))
{
/* Move onto Announce Step */
pCtx->state = MDNS_STATE_ANNOUNCE;
pCtx->bConflictSeenInLastProbe = FALSE;
INFO_MDNS_PRINT("MDNS_STATE_PROBE --> MDNS_STATE_ANNOUNCE \r\n");
//Shall we mDNSResetCounters(pCtx, TRUE)?
return;
}
}
else
{
// We are in MDNS_STATE_ANNOUNCE
if (pCtx->nClaimCount >= MDNS_ANNOUNCE_NUM)
{
/* Finalize mDNS Host-name, Announced */
pCtx->state = MDNS_STATE_DEFEND;
if ( bIsHost )
{
// The rest of the MCHP system knows its name through
// AppConfig.NetBIOSName, so update it here.
STRCPY_LOCAL(AppConfig.NetBIOSName, gHostCtx.szHostName);
INFO_MDNS_MESG(zeroconf_dbg_msg,"\r\n********* Taken Host-Name: %s ********* \r\n",
gHostCtx.szHostName);
INFO_MDNS_PRINT((char *)zeroconf_dbg_msg);
INFO_MDNS_PRINT("MDNS_STATE_ANNOUNCE --> MDNS_STATE_DEFEND \r\n");
DisplayHostName(gHostCtx.szHostName);
DisplayIPValue(AppConfig.MyIPAddr);
}
else
{
#if defined(STACK_USE_UART)
putrsUART((ROM char*)"\r\nZeroConf: Service = ");
putrsUART((ROM char*)gSDCtx.sd_qualified_name);
putrsUART((ROM char*)"\r\n");
#endif
INFO_MDNS_MESG(zeroconf_dbg_msg,"\r\n******** Taken Service-Name: %s ********\r\n",
gSDCtx.sd_qualified_name);
INFO_MDNS_PRINT((char *)zeroconf_dbg_msg);
INFO_MDNS_PRINT("MDNS_STATE_ANNOUNCE --> MDNS_STATE_DEFEND \r\n");
mDNSSendRR(&gResponderCtx.rr_list[QTYPE_PTR_INDEX],
0,
0x00,
1,
TRUE,TRUE);
gSDCtx.sd_service_advertised = 1;
if (gSDCtx.sd_call_back != NULL)
{
gSDCtx.sd_call_back((char *)gSDCtx.srv_name, MDNSD_SUCCESS, gSDCtx.sd_context);
}
}
mDNSResetCounters(pCtx, TRUE);
return;
}
}
if (pCtx->state == MDNS_STATE_PROBE)
{
// Send out Probe packet
mDNSProbe(pCtx);
pCtx->nProbeCount++;
pCtx->bConflictSeenInLastProbe = FALSE;
/* Need to set timeout for MDNS_PROBE_INTERVAL msec */
if (pCtx->nProbeConflictCount < 9) // less-than-10 is required to pass Bonjour Conformance test.
{
pCtx->random_delay = (TICK)( MDNS_PROBE_INTERVAL * (TICK_SECOND/1000));
}
else
{
pCtx->random_delay = (TICK) (TICKS_PER_SECOND);
}
DEBUG_MDNS_MESG(
zeroconf_dbg_msg,"MDNS_PROBE_INTERVAL Delay: %ld ticks (%d)\r\n",
pCtx->random_delay, pCtx->nProbeCount);
DEBUG_MDNS_PRINT((char *)zeroconf_dbg_msg);
return;
}
// We are in MDNS_STATE_ANNOUNCE
/* Announce Name chosen on Local Network */
mDNSAnnounce(&gResponderCtx.rr_list[(bIsHost?QTYPE_A_INDEX:QTYPE_SRV_INDEX)]);
pCtx->nClaimCount++;
// Need to set timeout: ANNOUNCE_WAIT or INTERVAL
if (pCtx->nClaimCount == 1)
{
/* Setup a delay of MDNS_ANNOUNCE_WAIT before announcing */
/* Need to wait for time MDNS_ANNOUNCE_WAIT msec */
pCtx->random_delay = (TICK) ( MDNS_ANNOUNCE_WAIT * (TICK_SECOND/1000));
}
else
{
pCtx->random_delay = (TICK)(MDNS_ANNOUNCE_INTERVAL * (TICK_SECOND/1000));
}
// Intenional fall-through
case ZGZC_KEEP_WAITING:
// Not Completed the delay proposed
return;
}
// Completed the delay required
DEBUG_MDNS_MESG(zeroconf_dbg_msg,"Probe/Announce delay completed : %ld ticks\r\n",
random_delay);
DEBUG_MDNS_PRINT((char *)zeroconf_dbg_msg);
/* Set the timer for next announce */
goto SET_PROBE_ANNOUNCE_TIMER;
}
case MDNS_STATE_DEFEND:
{
//DEBUG_MDNS_PRINT("MDNS_STATE_DEFEND \n");
/* On detection of Conflict Move back to PROBE step */
if(pCtx->bLateConflictSeen)
{
/* Clear the Flag */
pCtx->bLateConflictSeen = FALSE;
INFO_MDNS_PRINT("CONFLICT DETECTED !!! \r\n");
INFO_MDNS_PRINT("Re-probing the Host-Name because of Conflict \r\n");
pCtx->state = MDNS_STATE_INIT;
pCtx->time_recorded = 0;
INFO_MDNS_PRINT("MDNS_STATE_DEFEND --> MDNS_STATE_INIT \r\n");
}
else
return;
}
default:
break;
}
}
void mDNSProcess(void)
{
if(!MACIsLinked())
{
gHostCtx.common.state = MDNS_STATE_INTF_NOT_CONNECTED;
return;
}
if(AppConfig.MyIPAddr.Val == 0x00)
{
return;
}
else if (AppConfig.MyIPAddr.Val != gResponderCtx.prev_ipaddr.Val) {
// IP address has been changed outside of Zeroconf.
// Such change could be due to static IP assignment, or
// a new dynamic IP lease.
// Need to restart state-machine
INFO_MDNS_PRINT("IP-Address change is detected \r\n");
gResponderCtx.prev_ipaddr.Val = AppConfig.MyIPAddr.Val;
gHostCtx.common.state = MDNS_STATE_IPADDR_NOT_CONFIGURED;
// Do not change the nInstanceId.
// Change of IP does not imply prior name conflicts can be avoided.
// Change of host name does.
mDNSFillHostRecord();
}
/* Poll mDNSResponder to allow it to check for
* incoming mDNS Quries/Responses */
mDNSResponder();
if(gSDCtx.service_registered)
{
// Application has registered some services.
// We now need to start the service probe/announce/defend process.
if (gHostCtx.common.state != MDNS_STATE_DEFEND)
{
gSDCtx.common.state = MDNS_STATE_NOT_READY;
}
else
{
mDNSProcessInternal((mDNSProcessCtx_common *) &gSDCtx);
}
}
mDNSProcessInternal((mDNSProcessCtx_common *) &gHostCtx);
}
/**
* Zeroconf uses 224.0.0.251
* => E0.00.00.FB
* => 1110_0000.0000_0000.0000_0000.1111_1011
* Lower 23 bits are mapped to the multicast MAC address, prepended by 01:00:5E.
* This becomes 0000_0001:0000_0000:0101_1110:0000_0000:0000_0000:1111_1011
* => 01:00:5E:00:00:FB
*/
MDNSD_ERR_CODE
mDNSMulticastFilterRegister(void)
{
// Register an RX MAC fitler for the IP multicast group 224.0.0.251,
// which is mapped to 01:00:5E:00:00:FB
UINT8 mcast_addr[6] = {0x01, 0x00, 0x5E, 0x00, 0x00, 0xFB};
WF_SetMultiCastFilter(WF_MULTICAST_FILTER_1, mcast_addr);
return MDNSD_SUCCESS;
}
//#if defined(DEBUG_MDNS) || defined(INFO_MDNS)
void mDNSDumpInfo(void)
{
BYTE tmp[8];
putsUART(" Host registered: "); putsUART(gHostCtx.szUserChosenHostName); putsUART("\r\n");
putsUART(" qualified: "); putsUART(gHostCtx.szHostName); putsUART("\r\n");
putsUART("Service registered: "); putsUART(gSDCtx.srv_name); putsUART("\r\n");
putsUART(" qualified: "); putsUART(gSDCtx.sd_qualified_name); putsUART("\r\n");
sprintf((char *) tmp, "%d", gSDCtx.sd_port);
putsUART(" port: "); putsUART(tmp); putsUART("\r\n");
putsUART(" TXT registered: "); putsUART(gSDCtx.sd_txt_rec); putsUART("\r\n");
}
//#endif
#endif //#if defined(STACK_USE_ZEROCONF_MDNS_SD)
|