147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/* -*- Mode: C; tab-width: 4 -*-
247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Copyright (c) 2004, Apple Computer, Inc. All rights reserved.
447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Redistribution and use in source and binary forms, with or without
647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * modification, are permitted provided that the following conditions are met:
747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * 1.  Redistributions of source code must retain the above copyright notice,
947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *     this list of conditions and the following disclaimer.
1047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * 2.  Redistributions in binary form must reproduce the above copyright notice,
1147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *     this list of conditions and the following disclaimer in the documentation
1247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *     and/or other materials provided with the distribution.
1347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
1447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *     contributors may be used to endorse or promote products derived from this
1547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *     software without specific prior written permission.
1647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
1747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
1847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
2147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */
2847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
2947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <stdlib.h>
3047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include <string.h>
3147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
3247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "dns_sd.h"
3347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
3447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY
3547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#pragma export on
3647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#endif
3747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
3847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#if defined(_WIN32)
3947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// disable warning "conversion from <data> to uint16_t"
4047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#pragma warning(disable:4244)
4147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#define strncasecmp _strnicmp
4247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#define strcasecmp _stricmp
4347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#endif
4447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
4547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/*********************************************************************************************
4647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
4747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *  Supporting Functions
4847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
4947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *********************************************************************************************/
5047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
5147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#define mDNSIsDigit(X)     ((X) >= '0' && (X) <= '9')
5247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
5347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// DomainEndsInDot returns 1 if name ends with a dot, 0 otherwise
5447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// (DNSServiceConstructFullName depends this returning 1 for true, rather than any non-zero value meaning true)
5547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
5647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic int DomainEndsInDot(const char *dom)
5747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
5847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	while (dom[0] && dom[1])
5947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
6047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (dom[0] == '\\') // advance past escaped byte sequence
6147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			{
6247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			if (mDNSIsDigit(dom[1]) && mDNSIsDigit(dom[2]) && mDNSIsDigit(dom[3]))
6347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				dom += 4;			// If "\ddd"    then skip four
6447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			else dom += 2;			// else if "\x" then skip two
6547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			}
6647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		else dom++;					// else goto next character
6747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
6847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return (dom[0] == '.');
6947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
7047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
7147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic uint8_t *InternalTXTRecordSearch
7247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(
7347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t         txtLen,
7447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const void       *txtRecord,
7547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char       *key,
7647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	unsigned long    *keylen
7747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	)
7847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
7947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t *p = (uint8_t*)txtRecord;
8047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t *e = p + txtLen;
8147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	*keylen = (unsigned long) strlen(key);
8247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	while (p<e)
8347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
8447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		uint8_t *x = p;
8547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		p += 1 + p[0];
8647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (p <= e && *keylen <= x[0] && !strncasecmp(key, (char*)x+1, *keylen))
8747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			if (*keylen == x[0] || x[1+*keylen] == '=') return(x);
8847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
8947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return(NULL);
9047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
9147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
9247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/*********************************************************************************************
9347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
9447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *  General Utility Functions
9547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
9647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *********************************************************************************************/
9747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
9847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// Note: Need to make sure we don't write more than kDNSServiceMaxDomainName (1009) bytes to fullName
9947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// In earlier builds this constant was defined to be 1005, so to avoid buffer overruns on clients
10047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// compiled with that constant we'll actually limit the output to 1005 bytes.
10147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
10247e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltDNSServiceErrorType DNSSD_API DNSServiceConstructFullName
10347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(
10447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	char       *const fullName,
10547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char *const service,      // May be NULL
10647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char *const regtype,
10747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char *const domain
10847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	)
10947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
11047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const size_t len = !regtype ? 0 : strlen(regtype) - DomainEndsInDot(regtype);
11147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	char       *fn   = fullName;
11247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	char *const lim  = fullName + 1005;
11347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char *s    = service;
11447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char *r    = regtype;
11547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char *d    = domain;
11647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
11747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	// regtype must be at least "x._udp" or "x._tcp"
11847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (len < 6 || !domain || !domain[0]) return kDNSServiceErr_BadParam;
11947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (strncasecmp((regtype + len - 4), "_tcp", 4) && strncasecmp((regtype + len - 4), "_udp", 4)) return kDNSServiceErr_BadParam;
12047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
12147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (service && *service)
12247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
12347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		while (*s)
12447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			{
12547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			unsigned char c = *s++;				// Needs to be unsigned, or values like 0xFF will be interpreted as < 32
12647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			if (c <= ' ')						// Escape non-printable characters
12747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				{
12847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				if (fn+4 >= lim) goto fail;
12947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				*fn++ = '\\';
13047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				*fn++ = '0' + (c / 100);
13147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				*fn++ = '0' + (c /  10) % 10;
13247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				c     = '0' + (c      ) % 10;
13347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				}
13447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			else if (c == '.' || (c == '\\'))	// Escape dot and backslash literals
13547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				{
13647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				if (fn+2 >= lim) goto fail;
13747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				*fn++ = '\\';
13847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				}
13947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			else
14047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt				if (fn+1 >= lim) goto fail;
14147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			*fn++ = (char)c;
14247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			}
14347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		*fn++ = '.';
14447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
14547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
14647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	while (*r) if (fn+1 >= lim) goto fail; else *fn++ = *r++;
14747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (!DomainEndsInDot(regtype)) { if (fn+1 >= lim) goto fail; else *fn++ = '.'; }
14847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
14947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	while (*d) if (fn+1 >= lim) goto fail; else *fn++ = *d++;
15047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (!DomainEndsInDot(domain)) { if (fn+1 >= lim) goto fail; else *fn++ = '.'; }
15147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
15247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	*fn = '\0';
15347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return kDNSServiceErr_NoError;
15447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
15547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltfail:
15647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	*fn = '\0';
15747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return kDNSServiceErr_BadParam;
15847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
15947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
16047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/*********************************************************************************************
16147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
16247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *   TXT Record Construction Functions
16347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
16447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *********************************************************************************************/
16547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
16647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalttypedef struct _TXTRecordRefRealType
16747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
16847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t  *buffer;		// Pointer to data
16947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t buflen;		// Length of buffer
17047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t datalen;		// Length currently in use
17147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t malloced;	// Non-zero if buffer was allocated via malloc()
17247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	} TXTRecordRefRealType;
17347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
17447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#define txtRec ((TXTRecordRefRealType*)txtRecord)
17547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
17647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// The opaque storage defined in the public dns_sd.h header is 16 bytes;
17747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// make sure we don't exceed that.
17847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstruct CompileTimeAssertionCheck_dnssd_clientlib
17947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
18047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1];
18147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	};
18247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
18347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltvoid DNSSD_API TXTRecordCreate
18447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(
18547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	TXTRecordRef     *txtRecord,
18647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t         bufferLen,
18747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	void             *buffer
18847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	)
18947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
19047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	txtRec->buffer   = buffer;
19147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	txtRec->buflen   = buffer ? bufferLen : (uint16_t)0;
19247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	txtRec->datalen  = 0;
19347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	txtRec->malloced = 0;
19447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
19547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
19647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltvoid DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord)
19747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
19847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (txtRec->malloced) free(txtRec->buffer);
19947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
20047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
20147e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltDNSServiceErrorType DNSSD_API TXTRecordSetValue
20247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(
20347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	TXTRecordRef     *txtRecord,
20447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char       *key,
20547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t          valueSize,
20647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const void       *value
20747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	)
20847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
20947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t *start, *p;
21047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char *k;
21147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	unsigned long keysize, keyvalsize;
21247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
21347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	for (k = key; *k; k++) if (*k < 0x20 || *k > 0x7E || *k == '=') return(kDNSServiceErr_Invalid);
21447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	keysize = (unsigned long)(k - key);
21547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	keyvalsize = 1 + keysize + (value ? (1 + valueSize) : 0);
21647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (keysize < 1 || keyvalsize > 255) return(kDNSServiceErr_Invalid);
21747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(void)TXTRecordRemoveValue(txtRecord, key);
21847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (txtRec->datalen + keyvalsize > txtRec->buflen)
21947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
22047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		unsigned char *newbuf;
22147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		unsigned long newlen = txtRec->datalen + keyvalsize;
22247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (newlen > 0xFFFF) return(kDNSServiceErr_Invalid);
22347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		newbuf = malloc((size_t)newlen);
22447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (!newbuf) return(kDNSServiceErr_NoMemory);
22547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		memcpy(newbuf, txtRec->buffer, txtRec->datalen);
22647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (txtRec->malloced) free(txtRec->buffer);
22747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		txtRec->buffer = newbuf;
22847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		txtRec->buflen = (uint16_t)(newlen);
22947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		txtRec->malloced = 1;
23047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
23147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	start = txtRec->buffer + txtRec->datalen;
23247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	p = start + 1;
23347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	memcpy(p, key, keysize);
23447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	p += keysize;
23547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (value)
23647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
23747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		*p++ = '=';
23847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		memcpy(p, value, valueSize);
23947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		p += valueSize;
24047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
24147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	*start = (uint8_t)(p - start - 1);
24247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	txtRec->datalen += p - start;
24347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return(kDNSServiceErr_NoError);
24447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
24547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
24647e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltDNSServiceErrorType DNSSD_API TXTRecordRemoveValue
24747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(
24847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	TXTRecordRef     *txtRecord,
24947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char       *key
25047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	)
25147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
25247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	unsigned long keylen, itemlen, remainder;
25347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t *item = InternalTXTRecordSearch(txtRec->datalen, txtRec->buffer, key, &keylen);
25447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (!item) return(kDNSServiceErr_NoSuchKey);
25547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	itemlen   = (unsigned long)(1 + item[0]);
25647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	remainder = (unsigned long)((txtRec->buffer + txtRec->datalen) - (item + itemlen));
25747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	// Use memmove because memcpy behaviour is undefined for overlapping regions
25847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	memmove(item, item + itemlen, remainder);
25947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	txtRec->datalen -= itemlen;
26047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return(kDNSServiceErr_NoError);
26147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
26247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
26347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltuint16_t DNSSD_API TXTRecordGetLength  (const TXTRecordRef *txtRecord) { return(txtRec->datalen); }
26447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltconst void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); }
26547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
26647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/*********************************************************************************************
26747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
26847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *   TXT Record Parsing Functions
26947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
27047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *********************************************************************************************/
27147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
27247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltint DNSSD_API TXTRecordContainsKey
27347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(
27447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t         txtLen,
27547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const void       *txtRecord,
27647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char       *key
27747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	)
27847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
27947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	unsigned long keylen;
28047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0);
28147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
28247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
28347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltconst void * DNSSD_API TXTRecordGetValuePtr
28447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(
28547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t         txtLen,
28647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const void       *txtRecord,
28747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const char       *key,
28847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t          *valueLen
28947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	)
29047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
29147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	unsigned long keylen;
29247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen);
29347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (!item || item[0] <= keylen) return(NULL);	// If key not found, or found with no value, return NULL
29447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	*valueLen = (uint8_t)(item[0] - (keylen + 1));
29547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return (item + 1 + keylen + 1);
29647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
29747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
29847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltuint16_t DNSSD_API TXTRecordGetCount
29947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(
30047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t         txtLen,
30147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const void       *txtRecord
30247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	)
30347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
30447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t count = 0;
30547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t *p = (uint8_t*)txtRecord;
30647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t *e = p + txtLen;
30747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	while (p<e) { p += 1 + p[0]; count++; }
30847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return((p>e) ? (uint16_t)0 : count);
30947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
31047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
31147e4cebad7397422144bb03a21f3f7682c062c4aRobert GreenwaltDNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
31247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	(
31347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t         txtLen,
31447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const void       *txtRecord,
31547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t         itemIndex,
31647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t         keyBufLen,
31747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	char             *key,
31847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t          *valueLen,
31947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	const void       **value
32047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	)
32147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	{
32247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint16_t count = 0;
32347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t *p = (uint8_t*)txtRecord;
32447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	uint8_t *e = p + txtLen;
32547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	while (p<e && count<itemIndex) { p += 1 + p[0]; count++; }	// Find requested item
32647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	if (p<e && p + 1 + p[0] <= e)	// If valid
32747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		{
32847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		uint8_t *x = p+1;
32947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		unsigned long len = 0;
33047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		e = p + 1 + p[0];
33147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		while (x+len<e && x[len] != '=') len++;
33247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (len >= keyBufLen) return(kDNSServiceErr_NoMemory);
33347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		memcpy(key, x, len);
33447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		key[len] = 0;
33547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		if (x+len<e)		// If we found '='
33647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			{
33747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			*value = x + len + 1;
33847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			*valueLen = (uint8_t)(p[0] - (len + 1));
33947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			}
34047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		else
34147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			{
34247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			*value = NULL;
34347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			*valueLen = 0;
34447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt			}
34547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		return(kDNSServiceErr_NoError);
34647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt		}
34747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	return(kDNSServiceErr_Invalid);
34847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt	}
34947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
35047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/*********************************************************************************************
35147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
35247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *   SCCS-compatible version string
35347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *
35447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *********************************************************************************************/
35547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
35647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// For convenience when using the "strings" command, this is the last thing in the file
35747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
35847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
35947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
36047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// To expand "version" to its value before making the string, use STRINGIFY(version) instead
36147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
36247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
36347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt
36447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// NOT static -- otherwise the compiler may optimize it out
36547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt// The "@(#) " pattern is a special prefix the "what" command looks for
36647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltconst char VersionString_SCCS_libdnssd[] = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
367