diff options
Diffstat (limited to 'usr/src/lib/libdns_sd')
47 files changed, 8978 insertions, 0 deletions
diff --git a/usr/src/lib/libdns_sd/Makefile b/usr/src/lib/libdns_sd/Makefile new file mode 100644 index 0000000000..21a4e1aa1d --- /dev/null +++ b/usr/src/lib/libdns_sd/Makefile @@ -0,0 +1,59 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.lib + +HDR = dns_sd.h +HDRDIR = common +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +clean := TARGET = clean +clobber := TARGET = clobber +install := TARGET = install +lint := TARGET = lint + +.KEEP_STATE: + +all install: install_h $(SUBDIRS) .WAIT java + +clean clobber: $(SUBDIRS) + +ROOTHDRDIR= $(ROOT)/usr/include +ROOTHDRS= $(HDR:%=$(ROOTHDRDIR)/%) + +install_h: $(ROOTHDRS) + +check: + +lint: $(SUBDIRS) + +$(SUBDIRS) java: FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/lib/libdns_sd/Makefile.com b/usr/src/lib/libdns_sd/Makefile.com new file mode 100644 index 0000000000..4b6fe00f9b --- /dev/null +++ b/usr/src/lib/libdns_sd/Makefile.com @@ -0,0 +1,51 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# + +LIBRARY = libdns_sd.a +VERS = .1 +OBJECTS = dnssd_clientlib.o dnssd_clientstub.o dnssd_ipc.o + +include ../../Makefile.lib + +LIBS = $(DYNLIB) $(LINTLIB) +$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC) + +SRCDIR = ../common + +LDLIBS += -lsocket -lc + +C99MODE = $(C99_ENABLE) +CFLAGS += -erroff=E_ASSIGNMENT_TYPE_MISMATCH +CPPFLAGS += -I$(SRCDIR) -DNOT_HAVE_SA_LEN + +.PARALLEL = $(OBJECTS) +.KEEP_STATE: + +lint: lintcheck + +all: $(LIBS) + +include ../../Makefile.targ diff --git a/usr/src/lib/libdns_sd/README b/usr/src/lib/libdns_sd/README new file mode 100644 index 0000000000..a7deae9108 --- /dev/null +++ b/usr/src/lib/libdns_sd/README @@ -0,0 +1,65 @@ +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +#ident "%Z%%M% %I% %E% SMI" +# + +Multicast DNS and Service Discovery support in Solaris using the +Apple Bonjour source code (v107.6). Apple Bonjour source can be +downloaded from: + http://developer.apple.com/networking/bonjour/download/ +The following components are integrated from the Apple Bonjour +source in Solaris: + libdns_sd: usr/src/lib/libdns_sd <dns_sd.h> + libjdns_sd: usr/src/lib/libdns_sd/java/common + dnssd.jar: usr/src/lib/libdns_sd/java/com (incl. examples) + mdnsd: usr/src/cmd/cmd-inet/usr.lib/mdnsd + dns-sd: usr/src/cmd/cmd-inet/usr.bin/dns-sd.c + +Following fixes have been made to the Apple Bonjour source +integrated in Solaris: +* 64-bit support by adding pad bytes in ipc_msg_hdr_struct +* 64-bit support in libjdns_sd, dnssd.jar (JNISupport.c, DNSSD.java) +* mdnsd switches to user 'noaccess' and not 'nobody' after init +* Fixes to support IPv6 (mDNSPosix.c, mDNSUNP.c) +* Fix error raised when uDNS.c is compiled with Sun Studio compiler +* Fix in dnssd_clientstub.c to not check errno when recvmsg returns 0 +* mDNSDebug.c modified to not send msgs directly to console when + syslog call returns an error. Logs the messages at LOG_INFO level + and not LOG_ERR + +In addition the project introduces the following changes: +* A new nss_mdns module is introduced to use Multicast DNS (mdns) + for resolving link-local hostnames and is located at: + usr/src/lib/nsswitch/mdns +* snoop updated to decode mDNS packets +* updated /etc/services to include mdns +* <netinet/in.h> updated to include mdns +* svc:/network/dns/multicast:default introduced to manage mDNS daemon +* solaris.smf.manage.mdns & solaris.smf.value.mdns authorizations + to modify nss_mdns configuration in svc:/network/dns/multicast:default + Both authorizations added in network management execution profile. +* Default nsswitch.dns includes mdns as source for hosts & ipnodes +* nscd daemon updated to support mdns +* SUNWdsdu and SUNWdsdr packages deliver all the new mDNS + service discovery components. diff --git a/usr/src/lib/libdns_sd/amd64/Makefile b/usr/src/lib/libdns_sd/amd64/Makefile new file mode 100644 index 0000000000..2fa1329472 --- /dev/null +++ b/usr/src/lib/libdns_sd/amd64/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com +include ../../Makefile.lib.64 + +CFLAGS64 += -erroff=E_ASSIGNMENT_TYPE_MISMATCH +install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64) diff --git a/usr/src/lib/libdns_sd/common/dns_sd.h b/usr/src/lib/libdns_sd/common/dns_sd.h new file mode 100644 index 0000000000..7ffe6da198 --- /dev/null +++ b/usr/src/lib/libdns_sd/common/dns_sd.h @@ -0,0 +1,1725 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef _DNS_SD_H +#define _DNS_SD_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* standard calling convention under Win32 is __stdcall */ +/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */ +/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */ +#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64) +#define DNSSD_API __stdcall +#else +#define DNSSD_API +#endif + +/* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */ +#if defined(__FreeBSD__) && (__FreeBSD__ < 5) +#include <sys/types.h> + +/* Likewise, on Sun, standard integer types are in sys/types.h */ +#elif defined(__sun__) +#include <sys/types.h> + +/* EFI does not have stdint.h, or anything else equivalent */ +#elif defined(EFI32) || defined(EFI64) +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; + +/* Windows has its own differences */ +#elif defined(_WIN32) +#include <windows.h> +#define _UNUSED +#define bzero(a, b) memset(a, 0, b) +#ifndef _MSL_STDINT_H +typedef UINT8 uint8_t; +typedef INT8 int8_t; +typedef UINT16 uint16_t; +typedef INT16 int16_t; +typedef UINT32 uint32_t; +typedef INT32 int32_t; +#endif + +/* All other Posix platforms use stdint.h */ +#else +#include <stdint.h> +#include <strings.h> +#endif + +/* DNSServiceRef, DNSRecordRef + * + * Opaque internal data types. + * Note: client is responsible for serializing access to these structures if + * they are shared between concurrent threads. + */ + +typedef struct _DNSServiceRef_t *DNSServiceRef; +typedef struct _DNSRecordRef_t *DNSRecordRef; + +/* General flags used in functions defined below */ +enum + { + kDNSServiceFlagsMoreComing = 0x1, + /* MoreComing indicates to a callback that at least one more result is + * queued and will be delivered following immediately after this one. + * Applications should not update their UI to display browse + * results when the MoreComing flag is set, because this would + * result in a great deal of ugly flickering on the screen. + * Applications should instead wait until until MoreComing is not set, + * and then update their UI. + * When MoreComing is not set, that doesn't mean there will be no more + * answers EVER, just that there are no more answers immediately + * available right now at this instant. If more answers become available + * in the future they will be delivered as usual. + */ + + kDNSServiceFlagsAdd = 0x2, + kDNSServiceFlagsDefault = 0x4, + /* Flags for domain enumeration and browse/query reply callbacks. + * "Default" applies only to enumeration and is only valid in + * conjuction with "Add". An enumeration callback with the "Add" + * flag NOT set indicates a "Remove", i.e. the domain is no longer + * valid. + */ + + kDNSServiceFlagsNoAutoRename = 0x8, + /* Flag for specifying renaming behavior on name conflict when registering + * non-shared records. By default, name conflicts are automatically handled + * by renaming the service. NoAutoRename overrides this behavior - with this + * flag set, name conflicts will result in a callback. The NoAutorename flag + * is only valid if a name is explicitly specified when registering a service + * (i.e. the default name is not used.) + */ + + kDNSServiceFlagsShared = 0x10, + kDNSServiceFlagsUnique = 0x20, + /* Flag for registering individual records on a connected + * DNSServiceRef. Shared indicates that there may be multiple records + * with this name on the network (e.g. PTR records). Unique indicates that the + * record's name is to be unique on the network (e.g. SRV records). + */ + + kDNSServiceFlagsBrowseDomains = 0x40, + kDNSServiceFlagsRegistrationDomains = 0x80, + /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains. + * BrowseDomains enumerates domains recommended for browsing, RegistrationDomains + * enumerates domains recommended for registration. + */ + + kDNSServiceFlagsLongLivedQuery = 0x100, + /* Flag for creating a long-lived unicast query for the DNSServiceQueryRecord call. */ + + kDNSServiceFlagsAllowRemoteQuery = 0x200, + /* Flag for creating a record for which we will answer remote queries + * (queries from hosts more than one hop away; hosts not directly connected to the local link). + */ + + kDNSServiceFlagsForceMulticast = 0x400, + /* Flag for signifying that a query or registration should be performed exclusively via multicast DNS, + * even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS. + */ + + kDNSServiceFlagsReturnCNAME = 0x800 + /* Flag for returning CNAME records in the DNSServiceQueryRecord call. CNAME records are + * normally followed without indicating to the client that there was a CNAME record. + */ + }; + +/* + * The values for DNS Classes and Types are listed in RFC 1035, and are available + * on every OS in its DNS header file. Unfortunately every OS does not have the + * same header file containing DNS Class and Type constants, and the names of + * the constants are not consistent. For example, BIND 8 uses "T_A", + * BIND 9 uses "ns_t_a", Windows uses "DNS_TYPE_A", etc. + * For this reason, these constants are also listed here, so that code using + * the DNS-SD programming APIs can use these constants, so that the same code + * can compile on all our supported platforms. + */ + +enum + { + kDNSServiceClass_IN = 1 /* Internet */ + }; + +enum + { + kDNSServiceType_A = 1, /* Host address. */ + kDNSServiceType_NS = 2, /* Authoritative server. */ + kDNSServiceType_MD = 3, /* Mail destination. */ + kDNSServiceType_MF = 4, /* Mail forwarder. */ + kDNSServiceType_CNAME = 5, /* Canonical name. */ + kDNSServiceType_SOA = 6, /* Start of authority zone. */ + kDNSServiceType_MB = 7, /* Mailbox domain name. */ + kDNSServiceType_MG = 8, /* Mail group member. */ + kDNSServiceType_MR = 9, /* Mail rename name. */ + kDNSServiceType_NULL = 10, /* Null resource record. */ + kDNSServiceType_WKS = 11, /* Well known service. */ + kDNSServiceType_PTR = 12, /* Domain name pointer. */ + kDNSServiceType_HINFO = 13, /* Host information. */ + kDNSServiceType_MINFO = 14, /* Mailbox information. */ + kDNSServiceType_MX = 15, /* Mail routing information. */ + kDNSServiceType_TXT = 16, /* One or more text strings. */ + kDNSServiceType_RP = 17, /* Responsible person. */ + kDNSServiceType_AFSDB = 18, /* AFS cell database. */ + kDNSServiceType_X25 = 19, /* X_25 calling address. */ + kDNSServiceType_ISDN = 20, /* ISDN calling address. */ + kDNSServiceType_RT = 21, /* Router. */ + kDNSServiceType_NSAP = 22, /* NSAP address. */ + kDNSServiceType_NSAP_PTR = 23, /* Reverse NSAP lookup (deprecated). */ + kDNSServiceType_SIG = 24, /* Security signature. */ + kDNSServiceType_KEY = 25, /* Security key. */ + kDNSServiceType_PX = 26, /* X.400 mail mapping. */ + kDNSServiceType_GPOS = 27, /* Geographical position (withdrawn). */ + kDNSServiceType_AAAA = 28, /* IPv6 Address. */ + kDNSServiceType_LOC = 29, /* Location Information. */ + kDNSServiceType_NXT = 30, /* Next domain (security). */ + kDNSServiceType_EID = 31, /* Endpoint identifier. */ + kDNSServiceType_NIMLOC = 32, /* Nimrod Locator. */ + kDNSServiceType_SRV = 33, /* Server Selection. */ + kDNSServiceType_ATMA = 34, /* ATM Address */ + kDNSServiceType_NAPTR = 35, /* Naming Authority PoinTeR */ + kDNSServiceType_KX = 36, /* Key Exchange */ + kDNSServiceType_CERT = 37, /* Certification record */ + kDNSServiceType_A6 = 38, /* IPv6 Address (deprecated) */ + kDNSServiceType_DNAME = 39, /* Non-terminal DNAME (for IPv6) */ + kDNSServiceType_SINK = 40, /* Kitchen sink (experimentatl) */ + kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */ + kDNSServiceType_TKEY = 249, /* Transaction key */ + kDNSServiceType_TSIG = 250, /* Transaction signature. */ + kDNSServiceType_IXFR = 251, /* Incremental zone transfer. */ + kDNSServiceType_AXFR = 252, /* Transfer zone of authority. */ + kDNSServiceType_MAILB = 253, /* Transfer mailbox records. */ + kDNSServiceType_MAILA = 254, /* Transfer mail agent records. */ + kDNSServiceType_ANY = 255 /* Wildcard match. */ + }; + + +/* possible error code values */ +enum + { + kDNSServiceErr_NoError = 0, + kDNSServiceErr_Unknown = -65537, /* 0xFFFE FFFF */ + kDNSServiceErr_NoSuchName = -65538, + kDNSServiceErr_NoMemory = -65539, + kDNSServiceErr_BadParam = -65540, + kDNSServiceErr_BadReference = -65541, + kDNSServiceErr_BadState = -65542, + kDNSServiceErr_BadFlags = -65543, + kDNSServiceErr_Unsupported = -65544, + kDNSServiceErr_NotInitialized = -65545, + kDNSServiceErr_AlreadyRegistered = -65547, + kDNSServiceErr_NameConflict = -65548, + kDNSServiceErr_Invalid = -65549, + kDNSServiceErr_Firewall = -65550, + kDNSServiceErr_Incompatible = -65551, /* client library incompatible with daemon */ + kDNSServiceErr_BadInterfaceIndex = -65552, + kDNSServiceErr_Refused = -65553, + kDNSServiceErr_NoSuchRecord = -65554, + kDNSServiceErr_NoAuth = -65555, + kDNSServiceErr_NoSuchKey = -65556, + kDNSServiceErr_NATTraversal = -65557, + kDNSServiceErr_DoubleNAT = -65558, + kDNSServiceErr_BadTime = -65559 + /* mDNS Error codes are in the range + * FFFE FF00 (-65792) to FFFE FFFF (-65537) */ + }; + + +/* Maximum length, in bytes, of a service name represented as a */ +/* literal C-String, including the terminating NULL at the end. */ + +#define kDNSServiceMaxServiceName 64 + +/* Maximum length, in bytes, of a domain name represented as an *escaped* C-String */ +/* including the final trailing dot, and the C-String terminating NULL at the end. */ + +#define kDNSServiceMaxDomainName 1005 + +/* + * Notes on DNS Name Escaping + * -- or -- + * "Why is kDNSServiceMaxDomainName 1005, when the maximum legal domain name is 255 bytes?" + * + * All strings used in DNS-SD are UTF-8 strings. + * With few exceptions, most are also escaped using standard DNS escaping rules: + * + * '\\' represents a single literal '\' in the name + * '\.' represents a single literal '.' in the name + * '\ddd', where ddd is a three-digit decimal value from 000 to 255, + * represents a single literal byte with that value. + * A bare unescaped '.' is a label separator, marking a boundary between domain and subdomain. + * + * The exceptions, that do not use escaping, are the routines where the full + * DNS name of a resource is broken, for convenience, into servicename/regtype/domain. + * In these routines, the "servicename" is NOT escaped. It does not need to be, since + * it is, by definition, just a single literal string. Any characters in that string + * represent exactly what they are. The "regtype" portion is, technically speaking, + * escaped, but since legal regtypes are only allowed to contain letters, digits, + * and hyphens, there is nothing to escape, so the issue is moot. The "domain" + * portion is also escaped, though most domains in use on the public Internet + * today, like regtypes, don't contain any characters that need to be escaped. + * As DNS-SD becomes more popular, rich-text domains for service discovery will + * become common, so software should be written to cope with domains with escaping. + * + * The servicename may be up to 63 bytes of UTF-8 text (not counting the C-String + * terminating NULL at the end). The regtype is of the form _service._tcp or + * _service._udp, where the "service" part is 1-14 characters, which may be + * letters, digits, or hyphens. The domain part of the three-part name may be + * any legal domain, providing that the resulting servicename+regtype+domain + * name does not exceed 255 bytes. + * + * For most software, these issues are transparent. When browsing, the discovered + * servicenames should simply be displayed as-is. When resolving, the discovered + * servicename/regtype/domain are simply passed unchanged to DNSServiceResolve(). + * When a DNSServiceResolve() succeeds, the returned fullname is already in + * the correct format to pass to standard system DNS APIs such as res_query(). + * For converting from servicename/regtype/domain to a single properly-escaped + * full DNS name, the helper function DNSServiceConstructFullName() is provided. + * + * The following (highly contrived) example illustrates the escaping process. + * Suppose you have an service called "Dr. Smith\Dr. Johnson", of type "_ftp._tcp" + * in subdomain "4th. Floor" of subdomain "Building 2" of domain "apple.com." + * The full (escaped) DNS name of this service's SRV record would be: + * Dr\.\032Smith\\Dr\.\032Johnson._ftp._tcp.4th\.\032Floor.Building\0322.apple.com. + */ + + +/* + * Constants for specifying an interface index + * + * Specific interface indexes are identified via a 32-bit unsigned integer returned + * by the if_nametoindex() family of calls. + * + * If the client passes 0 for interface index, that means "do the right thing", + * which (at present) means, "if the name is in an mDNS local multicast domain + * (e.g. 'local.', '254.169.in-addr.arpa.', '{8,9,A,B}.E.F.ip6.arpa.') then multicast + * on all applicable interfaces, otherwise send via unicast to the appropriate + * DNS server." Normally, most clients will use 0 for interface index to + * automatically get the default sensible behaviour. + * + * If the client passes a positive interface index, then for multicast names that + * indicates to do the operation only on that one interface. For unicast names the + * interface index is ignored unless kDNSServiceFlagsForceMulticast is also set. + * + * If the client passes kDNSServiceInterfaceIndexLocalOnly when registering + * a service, then that service will be found *only* by other local clients + * on the same machine that are browsing using kDNSServiceInterfaceIndexLocalOnly + * or kDNSServiceInterfaceIndexAny. + * If a client has a 'private' service, accessible only to other processes + * running on the same machine, this allows the client to advertise that service + * in a way such that it does not inadvertently appear in service lists on + * all the other machines on the network. + * + * If the client passes kDNSServiceInterfaceIndexLocalOnly when browsing + * then it will find *all* records registered on that same local machine. + * Clients explicitly wishing to discover *only* LocalOnly services can + * accomplish this by inspecting the interfaceIndex of each service reported + * to their DNSServiceBrowseReply() callback function, and discarding those + * where the interface index is not kDNSServiceInterfaceIndexLocalOnly. + */ + +#define kDNSServiceInterfaceIndexAny 0 +#define kDNSServiceInterfaceIndexLocalOnly ( (uint32_t) -1 ) + + +typedef uint32_t DNSServiceFlags; +typedef int32_t DNSServiceErrorType; + + +/********************************************************************************************* + * + * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions + * + *********************************************************************************************/ + + +/* DNSServiceRefSockFD() + * + * Access underlying Unix domain socket for an initialized DNSServiceRef. + * The DNS Service Discovery implmementation uses this socket to communicate between + * the client and the mDNSResponder daemon. The application MUST NOT directly read from + * or write to this socket. Access to the socket is provided so that it can be used as a + * run loop source, or in a select() loop: when data is available for reading on the socket, + * DNSServiceProcessResult() should be called, which will extract the daemon's reply from + * the socket, and pass it to the appropriate application callback. By using a run loop or + * select(), results from the daemon can be processed asynchronously. Without using these + * constructs, DNSServiceProcessResult() will block until the response from the daemon arrives. + * The client is responsible for ensuring that the data on the socket is processed in a timely + * fashion - the daemon may terminate its connection with a client that does not clear its + * socket buffer. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. + * + * return value: The DNSServiceRef's underlying socket descriptor, or -1 on + * error. + */ + +int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); + + +/* DNSServiceProcessResult() + * + * Read a reply from the daemon, calling the appropriate application callback. This call will + * block until the daemon's response is received. Use DNSServiceRefSockFD() in + * conjunction with a run loop or select() to determine the presence of a response from the + * server before calling this function to process the reply without blocking. Call this function + * at any point if it is acceptable to block until the daemon's response arrives. Note that the + * client is responsible for ensuring that DNSServiceProcessResult() is called whenever there is + * a reply from the daemon - the daemon may terminate its connection with a client that does not + * process the daemon's responses. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls + * that take a callback parameter. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred. + */ + +DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); + + +/* DNSServiceRefDeallocate() + * + * Terminate a connection with the daemon and free memory associated with the DNSServiceRef. + * Any services or records registered with this DNSServiceRef will be deregistered. Any + * Browse, Resolve, or Query operations called with this reference will be terminated. + * + * Note: If the reference's underlying socket is used in a run loop or select() call, it should + * be removed BEFORE DNSServiceRefDeallocate() is called, as this function closes the reference's + * socket. + * + * Note: If the reference was initialized with DNSServiceCreateConnection(), any DNSRecordRefs + * created via this reference will be invalidated by this call - the resource records are + * deregistered, and their DNSRecordRefs may not be used in subsequent functions. Similarly, + * if the reference was initialized with DNSServiceRegister, and an extra resource record was + * added to the service via DNSServiceAddRecord(), the DNSRecordRef created by the Add() call + * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent + * functions. + * + * Note: This call is to be used only with the DNSServiceRef defined by this API. It is + * not compatible with dns_service_discovery_ref objects defined in the legacy Mach-based + * DNSServiceDiscovery.h API. + * + * sdRef: A DNSServiceRef initialized by any of the DNSService calls. + * + */ + +void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); + + +/********************************************************************************************* + * + * Domain Enumeration + * + *********************************************************************************************/ + +/* DNSServiceEnumerateDomains() + * + * Asynchronously enumerate domains available for browsing and registration. + * + * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains + * are to be found. + * + * Note that the names returned are (like all of DNS-SD) UTF-8 strings, + * and are escaped using standard DNS escaping rules. + * (See "Notes on DNS Name Escaping" earlier in this file for more details.) + * A graphical browser displaying a hierarchical tree-structured view should cut + * the names at the bare dots to yield individual labels, then de-escape each + * label according to the escaping rules, and then display the resulting UTF-8 text. + * + * DNSServiceDomainEnumReply Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceEnumerateDomains(). + * + * flags: Possible values are: + * kDNSServiceFlagsMoreComing + * kDNSServiceFlagsAdd + * kDNSServiceFlagsDefault + * + * interfaceIndex: Specifies the interface on which the domain exists. (The index for a given + * interface is determined via the if_nametoindex() family of calls.) + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise indicates + * the failure that occurred (other parameters are undefined if errorCode is nonzero). + * + * replyDomain: The name of the domain. + * + * context: The context pointer passed to DNSServiceEnumerateDomains. + * + */ + +typedef void (DNSSD_API *DNSServiceDomainEnumReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *replyDomain, + void *context + ); + + +/* DNSServiceEnumerateDomains() Parameters: + * + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the enumeration operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Possible values are: + * kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing. + * kDNSServiceFlagsRegistrationDomains to enumerate domains recommended + * for registration. + * + * interfaceIndex: If non-zero, specifies the interface on which to look for domains. + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to enumerate domains on + * all interfaces. See "Constants for specifying an interface index" for more details. + * + * callBack: The function to be called when a domain is found or the call asynchronously + * fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is not invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, + void *context /* may be NULL */ + ); + + +/********************************************************************************************* + * + * Service Registration + * + *********************************************************************************************/ + +/* Register a service that is discovered via Browse() and Resolve() calls. + * + * + * DNSServiceRegisterReply() Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceRegister(). + * + * flags: Currently unused, reserved for future use. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts, + * if the kDNSServiceFlagsNoAutoRename flag was used when registering.) + * Other parameters are undefined if errorCode is nonzero. + * + * name: The service name registered (if the application did not specify a name in + * DNSServiceRegister(), this indicates what name was automatically chosen). + * + * regtype: The type of service registered, as it was passed to the callout. + * + * domain: The domain on which the service was registered (if the application did not + * specify a domain in DNSServiceRegister(), this indicates the default domain + * on which the service was registered). + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceRegisterReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + const char *name, + const char *regtype, + const char *domain, + void *context + ); + + +/* DNSServiceRegister() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the registration will remain active indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * interfaceIndex: If non-zero, specifies the interface on which to register the service + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to register on all + * available interfaces. See "Constants for specifying an interface index" for more details. + * + * flags: Indicates the renaming behavior on name conflict (most applications + * will pass 0). See flag definitions above for details. + * + * name: If non-NULL, specifies the service name to be registered. + * Most applications will not specify a name, in which case the computer + * name is used (this name is communicated to the client via the callback). + * If a name is specified, it must be 1-63 bytes of UTF-8 text. + * If the name is longer than 63 bytes it will be automatically truncated + * to a legal length, unless the NoAutoRename flag is set, + * in which case kDNSServiceErr_BadParam will be returned. + * + * regtype: The service type followed by the protocol, separated by a dot + * (e.g. "_ftp._tcp"). The service type must be an underscore, followed + * by 1-14 characters, which may be letters, digits, or hyphens. + * The transport protocol must be "_tcp" or "_udp". New service types + * should be registered at <http://www.dns-sd.org/ServiceTypes.html>. + * + * domain: If non-NULL, specifies the domain on which to advertise the service. + * Most applications will not specify a domain, instead automatically + * registering in the default domain(s). + * + * host: If non-NULL, specifies the SRV target host name. Most applications + * will not specify a host, instead automatically using the machine's + * default host name(s). Note that specifying a non-NULL host does NOT + * create an address record for that host - the application is responsible + * for ensuring that the appropriate address record exists, or creating it + * via DNSServiceRegisterRecord(). + * + * port: The port, in network byte order, on which the service accepts connections. + * Pass 0 for a "placeholder" service (i.e. a service that will not be discovered + * by browsing, but will cause a name conflict if another client tries to + * register that same name). Most clients will not use placeholder services. + * + * txtLen: The length of the txtRecord, in bytes. Must be zero if the txtRecord is NULL. + * + * txtRecord: The TXT record rdata. A non-NULL txtRecord MUST be a properly formatted DNS + * TXT record, i.e. <length byte> <data> <length byte> <data> ... + * Passing NULL for the txtRecord is allowed as a synonym for txtLen=1, txtRecord="", + * i.e. it creates a TXT record of length one containing a single empty string. + * RFC 1035 doesn't allow a TXT record to contain *zero* strings, so a single empty + * string is the smallest legal DNS TXT record. + * As with the other parameters, the DNSServiceRegister call copies the txtRecord + * data; e.g. if you allocated the storage for the txtRecord parameter with malloc() + * then you can safely free that memory right after the DNSServiceRegister call returns. + * + * callBack: The function to be called when the registration completes or asynchronously + * fails. The client MAY pass NULL for the callback - The client will NOT be notified + * of the default values picked on its behalf, and the client will NOT be notified of any + * asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration + * of the service. The client may NOT pass the NoAutoRename flag if the callback is NULL. + * The client may still deregister the service at any time via DNSServiceRefDeallocate(). + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceRegister + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, /* may be NULL */ + const char *regtype, + const char *domain, /* may be NULL */ + const char *host, /* may be NULL */ + uint16_t port, + uint16_t txtLen, + const void *txtRecord, /* may be NULL */ + DNSServiceRegisterReply callBack, /* may be NULL */ + void *context /* may be NULL */ + ); + + +/* DNSServiceAddRecord() + * + * Add a record to a registered service. The name of the record will be the same as the + * registered service's name. + * The record can later be updated or deregistered by passing the RecordRef initialized + * by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * + * Note that the DNSServiceAddRecord/UpdateRecord/RemoveRecord are *NOT* thread-safe + * with respect to a single DNSServiceRef. If you plan to have multiple threads + * in your program simultaneously add, update, or remove records from the same + * DNSServiceRef, then it's the caller's responsibility to use a mutext lock + * or take similar appropriate precautions to serialize those calls. + * + * + * Parameters; + * + * sdRef: A DNSServiceRef initialized by DNSServiceRegister(). + * + * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this + * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * If the above DNSServiceRef is passed to DNSServiceRefDeallocate(), RecordRef is also + * invalidated and may not be used further. + * + * flags: Currently ignored, reserved for future use. + * + * rrtype: The type of the record (e.g. kDNSServiceType_TXT, kDNSServiceType_SRV, etc) + * + * rdlen: The length, in bytes, of the rdata. + * + * rdata: The raw rdata to be contained in the added resource record. + * + * ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred (the RecordRef is not initialized). + */ + +DNSServiceErrorType DNSSD_API DNSServiceAddRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ); + + +/* DNSServiceUpdateRecord + * + * Update a registered resource record. The record must either be: + * - The primary txt record of a service registered via DNSServiceRegister() + * - A record added to a registered service via DNSServiceAddRecord() + * - An individual record registered by DNSServiceRegisterRecord() + * + * + * Parameters: + * + * sdRef: A DNSServiceRef that was initialized by DNSServiceRegister() + * or DNSServiceCreateConnection(). + * + * RecordRef: A DNSRecordRef initialized by DNSServiceAddRecord, or NULL to update the + * service's primary txt record. + * + * flags: Currently ignored, reserved for future use. + * + * rdlen: The length, in bytes, of the new rdata. + * + * rdata: The new rdata to be contained in the updated resource record. + * + * ttl: The time to live of the updated resource record, in seconds. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred. + */ + +DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, /* may be NULL */ + DNSServiceFlags flags, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ); + + +/* DNSServiceRemoveRecord + * + * Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister + * an record registered individually via DNSServiceRegisterRecord(). + * + * Parameters: + * + * sdRef: A DNSServiceRef initialized by DNSServiceRegister() (if the + * record being removed was registered via DNSServiceAddRecord()) or by + * DNSServiceCreateConnection() (if the record being removed was registered via + * DNSServiceRegisterRecord()). + * + * recordRef: A DNSRecordRef initialized by a successful call to DNSServiceAddRecord() + * or DNSServiceRegisterRecord(). + * + * flags: Currently ignored, reserved for future use. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns an + * error code indicating the error that occurred. + */ + +DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags + ); + + +/********************************************************************************************* + * + * Service Discovery + * + *********************************************************************************************/ + +/* Browse for instances of a service. + * + * + * DNSServiceBrowseReply() Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceBrowse(). + * + * flags: Possible values are kDNSServiceFlagsMoreComing and kDNSServiceFlagsAdd. + * See flag definitions for details. + * + * interfaceIndex: The interface on which the service is advertised. This index should + * be passed to DNSServiceResolve() when resolving the service. + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * serviceName: The discovered service name. This name should be displayed to the user, + * and stored for subsequent use in the DNSServiceResolve() call. + * + * regtype: The service type, which is usually (but not always) the same as was passed + * to DNSServiceBrowse(). One case where the discovered service type may + * not be the same as the requested service type is when using subtypes: + * The client may want to browse for only those ftp servers that allow + * anonymous connections. The client will pass the string "_ftp._tcp,_anon" + * to DNSServiceBrowse(), but the type of the service that's discovered + * is simply "_ftp._tcp". The regtype for each discovered service instance + * should be stored along with the name, so that it can be passed to + * DNSServiceResolve() when the service is later resolved. + * + * domain: The domain of the discovered service instance. This may or may not be the + * same as the domain that was passed to DNSServiceBrowse(). The domain for each + * discovered service instance should be stored along with the name, so that + * it can be passed to DNSServiceResolve() when the service is later resolved. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceBrowseReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *serviceName, + const char *regtype, + const char *replyDomain, + void *context + ); + + +/* DNSServiceBrowse() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the browse operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: If non-zero, specifies the interface on which to browse for services + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Most applications will pass 0 to browse on all available + * interfaces. See "Constants for specifying an interface index" for more details. + * + * regtype: The service type being browsed for followed by the protocol, separated by a + * dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + * + * domain: If non-NULL, specifies the domain on which to browse for services. + * Most applications will not specify a domain, instead browsing on the + * default domain(s). + * + * callBack: The function to be called when an instance of the service being browsed for + * is found, or if the call asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is not invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceBrowse + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *regtype, + const char *domain, /* may be NULL */ + DNSServiceBrowseReply callBack, + void *context /* may be NULL */ + ); + + +/* DNSServiceResolve() + * + * Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and + * txt record. + * + * Note: Applications should NOT use DNSServiceResolve() solely for txt record monitoring - use + * DNSServiceQueryRecord() instead, as it is more efficient for this task. + * + * Note: When the desired results have been returned, the client MUST terminate the resolve by calling + * DNSServiceRefDeallocate(). + * + * Note: DNSServiceResolve() behaves correctly for typical services that have a single SRV record + * and a single TXT record. To resolve non-standard services with multiple SRV or TXT records, + * DNSServiceQueryRecord() should be used. + * + * DNSServiceResolveReply Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceResolve(). + * + * flags: Currently unused, reserved for future use. + * + * interfaceIndex: The interface on which the service was resolved. + * + * errorCode: Will be kDNSServiceErr_NoError (0) on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * the errorCode is nonzero. + * + * fullname: The full service domain name, in the form <servicename>.<protocol>.<domain>. + * (This name is escaped following standard DNS rules, making it suitable for + * passing to standard system DNS APIs such as res_query(), or to the + * special-purpose functions included in this API that take fullname parameters. + * See "Notes on DNS Name Escaping" earlier in this file for more details.) + * + * hosttarget: The target hostname of the machine providing the service. This name can + * be passed to functions like gethostbyname() to identify the host's IP address. + * + * port: The port, in network byte order, on which connections are accepted for this service. + * + * txtLen: The length of the txt record, in bytes. + * + * txtRecord: The service's primary txt record, in standard txt record format. + * + * context: The context pointer that was passed to the callout. + * + * NOTE: In earlier versions of this header file, the txtRecord parameter was declared "const char *" + * This is incorrect, since it contains length bytes which are values in the range 0 to 255, not -128 to +127. + * Depending on your compiler settings, this change may cause signed/unsigned mismatch warnings. + * These should be fixed by updating your own callback function definition to match the corrected + * function signature using "const unsigned char *txtRecord". Making this change may also fix inadvertent + * bugs in your callback function, where it could have incorrectly interpreted a length byte with value 250 + * as being -6 instead, with various bad consequences ranging from incorrect operation to software crashes. + * If you need to maintain portable code that will compile cleanly with both the old and new versions of + * this header file, you should update your callback function definition to use the correct unsigned value, + * and then in the place where you pass your callback function to DNSServiceResolve(), use a cast to eliminate + * the compiler warning, e.g.: + * DNSServiceResolve(sd, flags, index, name, regtype, domain, (DNSServiceResolveReply)MyCallback, context); + * This will ensure that your code compiles cleanly without warnings (and more importantly, works correctly) + * with both the old header and with the new corrected version. + * + */ + +typedef void (DNSSD_API *DNSServiceResolveReply) + ( + DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + const char *hosttarget, + uint16_t port, + uint16_t txtLen, + const unsigned char *txtRecord, + void *context + ); + + +/* DNSServiceResolve() Parameters + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the resolve operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Currently ignored, reserved for future use. + * + * interfaceIndex: The interface on which to resolve the service. If this resolve call is + * as a result of a currently active DNSServiceBrowse() operation, then the + * interfaceIndex should be the index reported in the DNSServiceBrowseReply + * callback. If this resolve call is using information previously saved + * (e.g. in a preference file) for later use, then use interfaceIndex 0, because + * the desired service may now be reachable via a different physical interface. + * See "Constants for specifying an interface index" for more details. + * + * name: The name of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * regtype: The type of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * domain: The domain of the service instance to be resolved, as reported to the + * DNSServiceBrowseReply() callback. + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceResolve + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + DNSServiceResolveReply callBack, + void *context /* may be NULL */ + ); + + +/********************************************************************************************* + * + * Special Purpose Calls (most applications will not use these) + * + *********************************************************************************************/ + +/* DNSServiceCreateConnection() + * + * Create a connection to the daemon allowing efficient registration of + * multiple individual records. + * + * + * Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating + * the reference (via DNSServiceRefDeallocate()) severs the + * connection and deregisters all records registered on this connection. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred (in which + * case the DNSServiceRef is not initialized). + */ + +DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); + + +/* DNSServiceRegisterRecord + * + * Register an individual resource record on a connected DNSServiceRef. + * + * Note that name conflicts occurring for records registered via this call must be handled + * by the client in the callback. + * + * + * DNSServiceRegisterRecordReply() parameters: + * + * sdRef: The connected DNSServiceRef initialized by + * DNSServiceCreateConnection(). + * + * RecordRef: The DNSRecordRef initialized by DNSServiceRegisterRecord(). If the above + * DNSServiceRef is passed to DNSServiceRefDeallocate(), this DNSRecordRef is + * invalidated, and may not be used further. + * + * flags: Currently unused, reserved for future use. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred (including name conflicts.) + * Other parameters are undefined if errorCode is nonzero. + * + * context: The context pointer that was passed to the callout. + * + */ + + typedef void (DNSSD_API *DNSServiceRegisterRecordReply) + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags, + DNSServiceErrorType errorCode, + void *context + ); + + +/* DNSServiceRegisterRecord() Parameters: + * + * sdRef: A DNSServiceRef initialized by DNSServiceCreateConnection(). + * + * RecordRef: A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this + * call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord(). + * (To deregister ALL records registered on a single connected DNSServiceRef + * and deallocate each of their corresponding DNSServiceRecordRefs, call + * DNSServiceRefDealloocate()). + * + * flags: Possible values are kDNSServiceFlagsShared or kDNSServiceFlagsUnique + * (see flag type definitions for details). + * + * interfaceIndex: If non-zero, specifies the interface on which to register the record + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the record to be registered on all interfaces. + * See "Constants for specifying an interface index" for more details. + * + * fullname: The full domain name of the resource record. + * + * rrtype: The numerical type of the resource record (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN) + * + * rdlen: Length, in bytes, of the rdata. + * + * rdata: A pointer to the raw rdata, as it is to appear in the DNS record. + * + * ttl: The time to live of the resource record, in seconds. Pass 0 to use a default value. + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails (e.g. because of a name conflict.) + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSRecordRef is + * not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, + void *context /* may be NULL */ + ); + + +/* DNSServiceQueryRecord + * + * Query for an arbitrary DNS record. + * + * + * DNSServiceQueryRecordReply() Callback Parameters: + * + * sdRef: The DNSServiceRef initialized by DNSServiceQueryRecord(). + * + * flags: Possible values are kDNSServiceFlagsMoreComing and + * kDNSServiceFlagsAdd. The Add flag is NOT set for PTR records + * with a ttl of 0, i.e. "Remove" events. + * + * interfaceIndex: The interface on which the query was resolved (the index for a given + * interface is determined via the if_nametoindex() family of calls). + * See "Constants for specifying an interface index" for more details. + * + * errorCode: Will be kDNSServiceErr_NoError on success, otherwise will + * indicate the failure that occurred. Other parameters are undefined if + * errorCode is nonzero. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * rdlen: The length, in bytes, of the resource record rdata. + * + * rdata: The raw rdata of the resource record. + * + * ttl: The resource record's time to live, in seconds. + * + * context: The context pointer that was passed to the callout. + * + */ + +typedef void (DNSSD_API *DNSServiceQueryRecordReply) + ( + DNSServiceRef DNSServiceRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + void *context + ); + + +/* DNSServiceQueryRecord() Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. If the call succeeds + * then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError, + * and the query operation will run indefinitely until the client + * terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate(). + * + * flags: Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast + * query in a non-local domain. Without setting this flag, unicast queries + * will be one-shot - that is, only answers available at the time of the call + * will be returned. By setting this flag, answers (including Add and Remove + * events) that become available after the initial call is made will generate + * callbacks. This flag has no effect on link-local multicast queries. + * + * interfaceIndex: If non-zero, specifies the interface on which to issue the query + * (the index for a given interface is determined via the if_nametoindex() + * family of calls.) Passing 0 causes the name to be queried for on all + * interfaces. See "Constants for specifying an interface index" for more details. + * + * fullname: The full domain name of the resource record to be queried for. + * + * rrtype: The numerical type of the resource record to be queried for + * (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * callBack: The function to be called when a result is found, or if the call + * asynchronously fails. + * + * context: An application context pointer which is passed to the callback function + * (may be NULL). + * + * return value: Returns kDNSServiceErr_NoError on succeses (any subsequent, asynchronous + * errors are delivered to the callback), otherwise returns an error code indicating + * the error that occurred (the callback is never invoked and the DNSServiceRef + * is not initialized.) + */ + +DNSServiceErrorType DNSSD_API DNSServiceQueryRecord + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, + void *context /* may be NULL */ + ); + + +/* DNSServiceReconfirmRecord + * + * Instruct the daemon to verify the validity of a resource record that appears to + * be out of date (e.g. because tcp connection to a service's target failed.) + * Causes the record to be flushed from the daemon's cache (as well as all other + * daemons' caches on the network) if the record is determined to be invalid. + * + * Parameters: + * + * flags: Currently unused, reserved for future use. + * + * interfaceIndex: If non-zero, specifies the interface of the record in question. + * Passing 0 causes all instances of this record to be reconfirmed. + * + * fullname: The resource record's full domain name. + * + * rrtype: The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc) + * + * rrclass: The class of the resource record (usually kDNSServiceClass_IN). + * + * rdlen: The length, in bytes, of the resource record rdata. + * + * rdata: The raw rdata of the resource record. + * + */ + +DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord + ( + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata + ); + + +/********************************************************************************************* + * + * General Utility Functions + * + *********************************************************************************************/ + +/* DNSServiceConstructFullName() + * + * Concatenate a three-part domain name (as returned by the above callbacks) into a + * properly-escaped full domain name. Note that callbacks in the above functions ALREADY ESCAPE + * strings where necessary. + * + * Parameters: + * + * fullName: A pointer to a buffer that where the resulting full domain name is to be written. + * The buffer must be kDNSServiceMaxDomainName (1005) bytes in length to + * accommodate the longest legal domain name without buffer overrun. + * + * service: The service name - any dots or backslashes must NOT be escaped. + * May be NULL (to construct a PTR record name, e.g. + * "_ftp._tcp.apple.com."). + * + * regtype: The service type followed by the protocol, separated by a dot + * (e.g. "_ftp._tcp"). + * + * domain: The domain name, e.g. "apple.com.". Literal dots or backslashes, + * if any, must be escaped, e.g. "1st\. Floor.apple.com." + * + * return value: Returns 0 on success, -1 on error. + * + */ + +int DNSSD_API DNSServiceConstructFullName + ( + char *fullName, + const char *service, /* may be NULL */ + const char *regtype, + const char *domain + ); + + +/********************************************************************************************* + * + * TXT Record Construction Functions + * + *********************************************************************************************/ + +/* + * A typical calling sequence for TXT record construction is something like: + * + * Client allocates storage for TXTRecord data (e.g. declare buffer on the stack) + * TXTRecordCreate(); + * TXTRecordSetValue(); + * TXTRecordSetValue(); + * TXTRecordSetValue(); + * ... + * DNSServiceRegister( ... TXTRecordGetLength(), TXTRecordGetBytesPtr() ... ); + * TXTRecordDeallocate(); + * Explicitly deallocate storage for TXTRecord data (if not allocated on the stack) + */ + + +/* TXTRecordRef + * + * Opaque internal data type. + * Note: Represents a DNS-SD TXT record. + */ + +typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef; + + +/* TXTRecordCreate() + * + * Creates a new empty TXTRecordRef referencing the specified storage. + * + * If the buffer parameter is NULL, or the specified storage size is not + * large enough to hold a key subsequently added using TXTRecordSetValue(), + * then additional memory will be added as needed using malloc(). + * + * On some platforms, when memory is low, malloc() may fail. In this + * case, TXTRecordSetValue() will return kDNSServiceErr_NoMemory, and this + * error condition will need to be handled as appropriate by the caller. + * + * You can avoid the need to handle this error condition if you ensure + * that the storage you initially provide is large enough to hold all + * the key/value pairs that are to be added to the record. + * The caller can precompute the exact length required for all of the + * key/value pairs to be added, or simply provide a fixed-sized buffer + * known in advance to be large enough. + * A no-value (key-only) key requires (1 + key length) bytes. + * A key with empty value requires (1 + key length + 1) bytes. + * A key with non-empty value requires (1 + key length + 1 + value length). + * For most applications, DNS-SD TXT records are generally + * less than 100 bytes, so in most cases a simple fixed-sized + * 256-byte buffer will be more than sufficient. + * Recommended size limits for DNS-SD TXT Records are discussed in + * <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt> + * + * Note: When passing parameters to and from these TXT record APIs, + * the key name does not include the '=' character. The '=' character + * is the separator between the key and value in the on-the-wire + * packet format; it is not part of either the key or the value. + * + * txtRecord: A pointer to an uninitialized TXTRecordRef. + * + * bufferLen: The size of the storage provided in the "buffer" parameter. + * + * buffer: Optional caller-supplied storage used to hold the TXTRecord data. + * This storage must remain valid for as long as + * the TXTRecordRef. + */ + +void DNSSD_API TXTRecordCreate + ( + TXTRecordRef *txtRecord, + uint16_t bufferLen, + void *buffer + ); + + +/* TXTRecordDeallocate() + * + * Releases any resources allocated in the course of preparing a TXT Record + * using TXTRecordCreate()/TXTRecordSetValue()/TXTRecordRemoveValue(). + * Ownership of the buffer provided in TXTRecordCreate() returns to the client. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + */ + +void DNSSD_API TXTRecordDeallocate + ( + TXTRecordRef *txtRecord + ); + + +/* TXTRecordSetValue() + * + * Adds a key (optionally with value) to a TXTRecordRef. If the "key" already + * exists in the TXTRecordRef, then the current value will be replaced with + * the new value. + * Keys may exist in four states with respect to a given TXT record: + * - Absent (key does not appear at all) + * - Present with no value ("key" appears alone) + * - Present with empty value ("key=" appears in TXT record) + * - Present with non-empty value ("key=value" appears in TXT record) + * For more details refer to "Data Syntax for DNS-SD TXT Records" in + * <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt> + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * key: A null-terminated string which only contains printable ASCII + * values (0x20-0x7E), excluding '=' (0x3D). Keys should be + * 8 characters or less (not counting the terminating null). + * + * valueSize: The size of the value. + * + * value: Any binary value. For values that represent + * textual data, UTF-8 is STRONGLY recommended. + * For values that represent textual data, valueSize + * should NOT include the terminating null (if any) + * at the end of the string. + * If NULL, then "key" will be added with no value. + * If non-NULL but valueSize is zero, then "key=" will be + * added with empty value. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_Invalid if the "key" string contains + * illegal characters. + * Returns kDNSServiceErr_NoMemory if adding this key would + * exceed the available storage. + */ + +DNSServiceErrorType DNSSD_API TXTRecordSetValue + ( + TXTRecordRef *txtRecord, + const char *key, + uint8_t valueSize, /* may be zero */ + const void *value /* may be NULL */ + ); + + +/* TXTRecordRemoveValue() + * + * Removes a key from a TXTRecordRef. The "key" must be an + * ASCII string which exists in the TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * key: A key name which exists in the TXTRecordRef. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_NoSuchKey if the "key" does not + * exist in the TXTRecordRef. + */ + +DNSServiceErrorType DNSSD_API TXTRecordRemoveValue + ( + TXTRecordRef *txtRecord, + const char *key + ); + + +/* TXTRecordGetLength() + * + * Allows you to determine the length of the raw bytes within a TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * return value: Returns the size of the raw bytes inside a TXTRecordRef + * which you can pass directly to DNSServiceRegister() or + * to DNSServiceUpdateRecord(). + * Returns 0 if the TXTRecordRef is empty. + */ + +uint16_t DNSSD_API TXTRecordGetLength + ( + const TXTRecordRef *txtRecord + ); + + +/* TXTRecordGetBytesPtr() + * + * Allows you to retrieve a pointer to the raw bytes within a TXTRecordRef. + * + * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). + * + * return value: Returns a pointer to the raw bytes inside the TXTRecordRef + * which you can pass directly to DNSServiceRegister() or + * to DNSServiceUpdateRecord(). + */ + +const void * DNSSD_API TXTRecordGetBytesPtr + ( + const TXTRecordRef *txtRecord + ); + + +/********************************************************************************************* + * + * TXT Record Parsing Functions + * + *********************************************************************************************/ + +/* + * A typical calling sequence for TXT record parsing is something like: + * + * Receive TXT record data in DNSServiceResolve() callback + * if (TXTRecordContainsKey(txtLen, txtRecord, "key")) then do something + * val1ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key1", &len1); + * val2ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key2", &len2); + * ... + * bcopy(val1ptr, myval1, len1); + * bcopy(val2ptr, myval2, len2); + * ... + * return; + * + * If you wish to retain the values after return from the DNSServiceResolve() + * callback, then you need to copy the data to your own storage using bcopy() + * or similar, as shown in the example above. + * + * If for some reason you need to parse a TXT record you built yourself + * using the TXT record construction functions above, then you can do + * that using TXTRecordGetLength and TXTRecordGetBytesPtr calls: + * TXTRecordGetValue(TXTRecordGetLength(x), TXTRecordGetBytesPtr(x), key, &len); + * + * Most applications only fetch keys they know about from a TXT record and + * ignore the rest. + * However, some debugging tools wish to fetch and display all keys. + * To do that, use the TXTRecordGetCount() and TXTRecordGetItemAtIndex() calls. + */ + +/* TXTRecordContainsKey() + * + * Allows you to determine if a given TXT Record contains a specified key. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * key: A null-terminated ASCII string containing the key name. + * + * return value: Returns 1 if the TXT Record contains the specified key. + * Otherwise, it returns 0. + */ + +int DNSSD_API TXTRecordContainsKey + ( + uint16_t txtLen, + const void *txtRecord, + const char *key + ); + + +/* TXTRecordGetValuePtr() + * + * Allows you to retrieve the value for a given key from a TXT Record. + * + * txtLen: The size of the received TXT Record + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * key: A null-terminated ASCII string containing the key name. + * + * valueLen: On output, will be set to the size of the "value" data. + * + * return value: Returns NULL if the key does not exist in this TXT record, + * or exists with no value (to differentiate between + * these two cases use TXTRecordContainsKey()). + * Returns pointer to location within TXT Record bytes + * if the key exists with empty or non-empty value. + * For empty value, valueLen will be zero. + * For non-empty value, valueLen will be length of value data. + */ + +const void * DNSSD_API TXTRecordGetValuePtr + ( + uint16_t txtLen, + const void *txtRecord, + const char *key, + uint8_t *valueLen + ); + + +/* TXTRecordGetCount() + * + * Returns the number of keys stored in the TXT Record. The count + * can be used with TXTRecordGetItemAtIndex() to iterate through the keys. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * return value: Returns the total number of keys in the TXT Record. + * + */ + +uint16_t DNSSD_API TXTRecordGetCount + ( + uint16_t txtLen, + const void *txtRecord + ); + + +/* TXTRecordGetItemAtIndex() + * + * Allows you to retrieve a key name and value pointer, given an index into + * a TXT Record. Legal index values range from zero to TXTRecordGetCount()-1. + * It's also possible to iterate through keys in a TXT record by simply + * calling TXTRecordGetItemAtIndex() repeatedly, beginning with index zero + * and increasing until TXTRecordGetItemAtIndex() returns kDNSServiceErr_Invalid. + * + * On return: + * For keys with no value, *value is set to NULL and *valueLen is zero. + * For keys with empty value, *value is non-NULL and *valueLen is zero. + * For keys with non-empty value, *value is non-NULL and *valueLen is non-zero. + * + * txtLen: The size of the received TXT Record. + * + * txtRecord: Pointer to the received TXT Record bytes. + * + * index: An index into the TXT Record. + * + * keyBufLen: The size of the string buffer being supplied. + * + * key: A string buffer used to store the key name. + * On return, the buffer contains a null-terminated C string + * giving the key name. DNS-SD TXT keys are usually + * 8 characters or less. To hold the maximum possible + * key name, the buffer should be 256 bytes long. + * + * valueLen: On output, will be set to the size of the "value" data. + * + * value: On output, *value is set to point to location within TXT + * Record bytes that holds the value data. + * + * return value: Returns kDNSServiceErr_NoError on success. + * Returns kDNSServiceErr_NoMemory if keyBufLen is too short. + * Returns kDNSServiceErr_Invalid if index is greater than + * TXTRecordGetCount()-1. + */ + +DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex + ( + uint16_t txtLen, + const void *txtRecord, + uint16_t index, + uint16_t keyBufLen, + char *key, + uint8_t *valueLen, + const void **value + ); + +#ifdef __APPLE_API_PRIVATE + +/* + * Mac OS X specific functionality + * 3rd party clients of this API should not depend on future support or availability of this routine + */ + +/* DNSServiceSetDefaultDomainForUser() + * + * Set the default domain for the caller's UID. Future browse and registration + * calls by this user that do not specify an explicit domain will browse and + * register in this wide-area domain in addition to .local. In addition, this + * domain will be returned as a Browse domain via domain enumeration calls. + * + * + * Parameters: + * + * flags: Pass kDNSServiceFlagsAdd to add a domain for a user. Call without + * this flag set to clear a previously added domain. + * + * domain: The domain to be used for the caller's UID. + * + * return value: Returns kDNSServiceErr_NoError on succeses, otherwise returns + * an error code indicating the error that occurred + */ + +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser + ( + DNSServiceFlags flags, + const char *domain + ); + +#endif //__APPLE_API_PRIVATE + +// Some C compiler cleverness. We can make the compiler check certain things for us, +// and report errors at compile-time if anything is wrong. The usual way to do this would +// be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but +// then you don't find out what's wrong until you run the software. This way, if the assertion +// condition is false, the array size is negative, and the complier complains immediately. + +struct DNS_SD_CompileTimeAssertionChecks + { + char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1]; + }; + +#ifdef __cplusplus + } +#endif + +#endif /* _DNS_SD_H */ diff --git a/usr/src/lib/libdns_sd/common/dnssd_clientlib.c b/usr/src/lib/libdns_sd/common/dnssd_clientlib.c new file mode 100644 index 0000000000..b5a45c8b63 --- /dev/null +++ b/usr/src/lib/libdns_sd/common/dnssd_clientlib.c @@ -0,0 +1,373 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2004, Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Change History (most recent first): + +$Log: dnssd_clientlib.c,v $ +Revision 1.11 2006/08/14 23:05:53 cheshire +Added "tab-width" emacs header line + +Revision 1.10 2005/04/06 02:06:56 shersche +Add DNSSD_API macro to TXTRecord API calls + +Revision 1.9 2004/10/06 02:22:19 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.8 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.7 2004/06/26 03:16:34 shersche +clean up warning messages on Win32 platform + +Submitted by: herscher + +Revision 1.6 2004/06/12 01:09:45 cheshire +To be callable from the broadest range of clients on Windows (e.g. Visual Basic, C#, etc.) +API routines have to be declared as "__stdcall", instead of the C default, "__cdecl" + +Revision 1.5 2004/05/25 18:29:33 cheshire +Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c, +so that it's also accessible to dnssd_clientshim.c (single address space) clients. + +Revision 1.4 2004/05/25 17:08:55 cheshire +Fix compiler warning (doesn't make sense for function return type to be const) + +Revision 1.3 2004/05/21 21:41:35 cheshire +Add TXT record building and parsing APIs + +Revision 1.2 2004/05/20 22:22:21 cheshire +Enable code that was bracketed by "#if 0" + +Revision 1.1 2004/03/12 21:30:29 cheshire +Build a System-Context Shared Library from mDNSCore, for the benefit of developers +like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code. + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <string.h> + +#include "dns_sd.h" + +#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY +#pragma export on +#endif + +#if defined(_WIN32) +// disable warning "conversion from <data> to uint16_t" +#pragma warning(disable:4244) +#endif + +/********************************************************************************************* + * + * Supporting Functions + * + *********************************************************************************************/ + +#define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9') + +static int DomainEndsInDot(const char *dom) + { + while (dom[0] && dom[1]) + { + if (dom[0] == '\\') // advance past escaped byte sequence + { + if (mdnsIsDigit(dom[1]) && mdnsIsDigit(dom[2]) && mdnsIsDigit(dom[3])) + dom += 4; // If "\ddd" then skip four + else dom += 2; // else if "\x" then skip two + } + else dom++; // else goto next character + } + return (dom[0] == '.'); + } + +static uint8_t *InternalTXTRecordSearch + ( + uint16_t txtLen, + const void *txtRecord, + const char *key, + unsigned long *keylen + ) + { + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + *keylen = (unsigned long) strlen(key); + while (p<e) + { + uint8_t *x = p; + p += 1 + p[0]; + if (p <= e && *keylen <= x[0] && !strncmp(key, (char*)x+1, *keylen)) + if (*keylen == x[0] || x[1+*keylen] == '=') return(x); + } + return(NULL); + } + +/********************************************************************************************* + * + * General Utility Functions + * + *********************************************************************************************/ + +int DNSSD_API DNSServiceConstructFullName + ( + char *fullName, + const char *service, /* may be NULL */ + const char *regtype, + const char *domain + ) + { + unsigned long len; + unsigned char c; + char *fn = fullName; + const char *s = service; + const char *r = regtype; + const char *d = domain; + + if (service) + { + while(*s) + { + c = (unsigned char)*s++; + if (c == '.' || (c == '\\')) *fn++ = '\\'; // escape dot and backslash literals + else if (c <= ' ') // escape non-printable characters + { + *fn++ = '\\'; + *fn++ = (char) ('0' + (c / 100)); + *fn++ = (char) ('0' + (c / 10) % 10); + c = (unsigned char)('0' + (c % 10)); + } + *fn++ = (char)c; + } + *fn++ = '.'; + } + + if (!regtype) return -1; + len = (unsigned long) strlen(regtype); + if (DomainEndsInDot(regtype)) len--; + if (len < 6) return -1; // regtype must be at least "x._udp" or "x._tcp" + if (strncmp((regtype + len - 4), "_tcp", 4) && strncmp((regtype + len - 4), "_udp", 4)) return -1; + while(*r) *fn++ = *r++; + if (!DomainEndsInDot(regtype)) *fn++ = '.'; + + if (!domain || !domain[0]) return -1; + while(*d) *fn++ = *d++; + if (!DomainEndsInDot(domain)) *fn++ = '.'; + *fn = '\0'; + return 0; + } + +/********************************************************************************************* + * + * TXT Record Construction Functions + * + *********************************************************************************************/ + +typedef struct _TXTRecordRefRealType + { + uint8_t *buffer; // Pointer to data + uint16_t buflen; // Length of buffer + uint16_t datalen; // Length currently in use + uint16_t malloced; // Non-zero if buffer was allocated via malloc() + } TXTRecordRefRealType; + +#define txtRec ((TXTRecordRefRealType*)txtRecord) + +// The opaque storage defined in the public dns_sd.h header is 16 bytes; +// make sure we don't exceed that. +struct dnssd_clientlib_CompileTimeAssertionCheck + { + char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1]; + }; + +void DNSSD_API TXTRecordCreate + ( + TXTRecordRef *txtRecord, + uint16_t bufferLen, + void *buffer + ) + { + txtRec->buffer = buffer; + txtRec->buflen = buffer ? bufferLen : (uint16_t)0; + txtRec->datalen = 0; + txtRec->malloced = 0; + } + +void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord) + { + if (txtRec->malloced) free(txtRec->buffer); + } + +DNSServiceErrorType DNSSD_API TXTRecordSetValue + ( + TXTRecordRef *txtRecord, + const char *key, + uint8_t valueSize, + const void *value + ) + { + uint8_t *start, *p; + const char *k; + unsigned long keysize, keyvalsize; + + for (k = key; *k; k++) if (*k < 0x20 || *k > 0x7E || *k == '=') return(kDNSServiceErr_Invalid); + keysize = (unsigned long)(k - key); + keyvalsize = 1 + keysize + (value ? (1 + valueSize) : 0); + if (keysize < 1 || keyvalsize > 255) return(kDNSServiceErr_Invalid); + (void)TXTRecordRemoveValue(txtRecord, key); + if (txtRec->datalen + keyvalsize > txtRec->buflen) + { + unsigned char *newbuf; + unsigned long newlen = txtRec->datalen + keyvalsize; + if (newlen > 0xFFFF) return(kDNSServiceErr_Invalid); + newbuf = malloc((size_t)newlen); + if (!newbuf) return(kDNSServiceErr_NoMemory); + memcpy(newbuf, txtRec->buffer, txtRec->datalen); + if (txtRec->malloced) free(txtRec->buffer); + txtRec->buffer = newbuf; + txtRec->buflen = (uint16_t)(newlen); + txtRec->malloced = 1; + } + start = txtRec->buffer + txtRec->datalen; + p = start + 1; + memcpy(p, key, keysize); + p += keysize; + if (value) + { + *p++ = '='; + memcpy(p, value, valueSize); + p += valueSize; + } + *start = (uint8_t)(p - start - 1); + txtRec->datalen += p - start; + return(kDNSServiceErr_NoError); + } + +DNSServiceErrorType DNSSD_API TXTRecordRemoveValue + ( + TXTRecordRef *txtRecord, + const char *key + ) + { + unsigned long keylen, itemlen, remainder; + uint8_t *item = InternalTXTRecordSearch(txtRec->datalen, txtRec->buffer, key, &keylen); + if (!item) return(kDNSServiceErr_NoSuchKey); + itemlen = (unsigned long)(1 + item[0]); + remainder = (unsigned long)((txtRec->buffer + txtRec->datalen) - (item + itemlen)); + // Use memmove because memcpy behaviour is undefined for overlapping regions + memmove(item, item + itemlen, remainder); + txtRec->datalen -= itemlen; + return(kDNSServiceErr_NoError); + } + +uint16_t DNSSD_API TXTRecordGetLength (const TXTRecordRef *txtRecord) { return(txtRec->datalen); } +const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); } + +/********************************************************************************************* + * + * TXT Record Parsing Functions + * + *********************************************************************************************/ + +int DNSSD_API TXTRecordContainsKey + ( + uint16_t txtLen, + const void *txtRecord, + const char *key + ) + { + unsigned long keylen; + return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0); + } + +const void * DNSSD_API TXTRecordGetValuePtr + ( + uint16_t txtLen, + const void *txtRecord, + const char *key, + uint8_t *valueLen + ) + { + unsigned long keylen; + uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen); + if (!item || item[0] <= keylen) return(NULL); // If key not found, or found with no value, return NULL + *valueLen = (uint8_t)(item[0] - (keylen + 1)); + return (item + 1 + keylen + 1); + } + +uint16_t DNSSD_API TXTRecordGetCount + ( + uint16_t txtLen, + const void *txtRecord + ) + { + uint16_t count = 0; + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + while (p<e) { p += 1 + p[0]; count++; } + return((p>e) ? (uint16_t)0 : count); + } + +DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex + ( + uint16_t txtLen, + const void *txtRecord, + uint16_t index, + uint16_t keyBufLen, + char *key, + uint8_t *valueLen, + const void **value + ) + { + uint16_t count = 0; + uint8_t *p = (uint8_t*)txtRecord; + uint8_t *e = p + txtLen; + while (p<e && count<index) { p += 1 + p[0]; count++; } // Find requested item + if (p<e && p + 1 + p[0] <= e) // If valid + { + uint8_t *x = p+1; + unsigned long len = 0; + e = p + 1 + p[0]; + while (x+len<e && x[len] != '=') len++; + if (len >= keyBufLen) return(kDNSServiceErr_NoMemory); + memcpy(key, x, len); + key[len] = 0; + if (x+len<e) // If we found '=' + { + *value = x + len + 1; + *valueLen = (uint8_t)(p[0] - (len + 1)); + } + else + { + *value = NULL; + *valueLen = 0; + } + return(kDNSServiceErr_NoError); + } + return(kDNSServiceErr_Invalid); + } diff --git a/usr/src/lib/libdns_sd/common/dnssd_clientstub.c b/usr/src/lib/libdns_sd/common/dnssd_clientstub.c new file mode 100644 index 0000000000..2a22a0e22f --- /dev/null +++ b/usr/src/lib/libdns_sd/common/dnssd_clientstub.c @@ -0,0 +1,1249 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Change History (most recent first): + +$Log: dnssd_clientstub.c,v $ +Revision 1.53 2006/09/07 04:43:12 herscher +Fix compile error on Win32 platform by moving inclusion of syslog.h + +Revision 1.52 2006/08/15 23:04:21 mkrochma +<rdar://problem/4090354> Client should be able to specify service name w/o callback + +Revision 1.51 2006/07/24 23:45:55 cheshire +<rdar://problem/4605276> DNSServiceReconfirmRecord() should return error code + +Revision 1.50 2006/06/28 08:22:27 cheshire +<rdar://problem/4605264> dnssd_clientstub.c needs to report unlink failures in syslog + +Revision 1.49 2006/06/28 07:58:59 cheshire +Minor textual tidying + +Revision 1.48 2005/06/30 18:01:00 shersche +<rdar://problem/4096913> Clients shouldn't wait ten seconds to connect to mDNSResponder + +Revision 1.47 2005/03/31 02:19:56 cheshire +<rdar://problem/4021486> Fix build warnings +Reviewed by: Scott Herscher + +Revision 1.46 2005/03/21 00:39:31 shersche +<rdar://problem/4021486> Fix build warnings on Win32 platform + +Revision 1.45 2005/02/01 01:25:06 shersche +Define sleep() to be Sleep() for Windows compatibility + +Revision 1.44 2005/01/27 22:57:56 cheshire +Fix compile errors on gcc4 + +Revision 1.43 2005/01/27 00:02:29 cheshire +<rdar://problem/3947461> Handle case where client runs before daemon has finished launching + +Revision 1.42 2005/01/11 02:01:02 shersche +Use dnssd_close() rather than close() for Windows compatibility + +Revision 1.41 2004/12/23 17:34:26 ksekar +<rdar://problem/3931319> Calls leak sockets if mDNSResponder is not running + +Revision 1.40 2004/11/23 03:39:47 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.39 2004/11/12 03:22:00 rpantos +rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex. + +Revision 1.38 2004/11/02 02:51:23 cheshire +<rdar://problem/3526342> Remove overly-restrictive flag checks + +Revision 1.37 2004/10/14 01:43:35 cheshire +Fix opaque port passing problem + +Revision 1.36 2004/10/06 02:22:19 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.35 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.34 2004/09/17 22:36:13 cheshire +Add comment explaining that deliver_request frees the message it sends + +Revision 1.33 2004/09/17 01:17:31 ksekar +Remove double-free of msg header, freed automatically by deliver_request() + +Revision 1.32 2004/09/17 01:08:55 cheshire +Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h + The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces + declared in that file are ONLY appropriate to single-address-space embedded applications. + For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used. + +Revision 1.31 2004/09/16 23:37:19 cheshire +Free hdr before returning + +Revision 1.30 2004/09/16 23:14:24 cheshire +Changes for Windows compatibility + +Revision 1.29 2004/09/16 21:46:38 ksekar +<rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area domain + +Revision 1.28 2004/08/11 17:10:04 cheshire +Fix signed/unsigned warnings + +Revision 1.27 2004/08/11 00:54:16 cheshire +Change "hdr->op.request_op" to just "hdr->op" + +Revision 1.26 2004/07/26 06:07:27 shersche +fix bugs when using an error socket to communicate with the daemon + +Revision 1.25 2004/07/26 05:54:02 shersche +DNSServiceProcessResult() returns NoError if socket read returns EWOULDBLOCK + +Revision 1.24 2004/07/20 06:46:21 shersche +<rdar://problem/3730123> fix endless loop in read_all() if recv returns 0 +Bug #: 3730123 + +Revision 1.23 2004/06/29 00:48:38 cheshire +Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; +use an explicit while() loop instead. + +Revision 1.22 2004/06/26 03:16:34 shersche +clean up warning messages on Win32 platform + +Submitted by: herscher + +Revision 1.21 2004/06/18 04:53:56 rpantos +Use platform layer for socket types. Introduce USE_TCP_LOOPBACK. Remove dependency on mDNSEmbeddedAPI.h. + +Revision 1.20 2004/06/12 00:50:22 cheshire +Changes for Windows compatibility + +Revision 1.19 2004/05/25 18:29:33 cheshire +Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c, +so that it's also accessible to dnssd_clientshim.c (single address space) clients. + +Revision 1.18 2004/05/18 23:51:27 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.17 2004/05/06 18:42:58 ksekar +General dns_sd.h API cleanup, including the following radars: +<rdar://problem/3592068>: Remove flags with zero value +<rdar://problem/3479569>: Passing in NULL causes a crash. + +Revision 1.16 2004/03/12 22:00:37 cheshire +Added: #include <sys/socket.h> + +Revision 1.15 2004/01/20 18:36:29 ksekar +Propagated Libinfo fix for <rdar://problem/3483971>: SU: +DNSServiceUpdateRecord() doesn't allow you to update the TXT record +into TOT mDNSResponder. + +Revision 1.14 2004/01/19 22:39:17 cheshire +Don't use "MSG_WAITALL"; it makes send() return "Invalid argument" on Linux; +use an explicit while() loop instead. (In any case, this should only make a difference +with non-blocking sockets, which we don't use on the client side right now.) + +Revision 1.13 2004/01/19 21:46:52 cheshire +Fix compiler warning + +Revision 1.12 2003/12/23 20:46:47 ksekar +<rdar://problem/3497428>: sync dnssd files between libinfo & mDNSResponder + +Revision 1.11 2003/12/08 21:11:42 rpantos +Changes necessary to support mDNSResponder on Linux. + +Revision 1.10 2003/10/13 23:50:53 ksekar +Updated dns_sd clientstub files to bring copies in synch with +top-of-tree Libinfo: A memory leak in dnssd_clientstub.c is fixed, +and comments in dns_sd.h are improved. + +Revision 1.9 2003/08/15 21:30:39 cheshire +Bring up to date with LibInfo version + +Revision 1.8 2003/08/13 23:54:52 ksekar +Bringing dnssd_clientstub.c up to date with Libinfo, per radar 3376640 + +Revision 1.7 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <errno.h> +#include <stdlib.h> + +#include "dnssd_ipc.h" + +#if defined(_WIN32) + +#include <winsock2.h> +#include <windows.h> + +#define sockaddr_mdns sockaddr_in +#define AF_MDNS AF_INET + +// disable warning: "'type cast' : from data pointer 'void *' to function pointer" +#pragma warning(disable:4055) + +// disable warning: "nonstandard extension, function/data pointer conversion in expression" +#pragma warning(disable:4152) + +extern BOOL IsSystemServiceDisabled(); + +#define sleep(X) Sleep((X) * 1000) + +static int g_initWinsock = 0; + +#else + +#include <sys/time.h> +#include <sys/socket.h> +#include <syslog.h> + +#define sockaddr_mdns sockaddr_un +#define AF_MDNS AF_LOCAL + +#endif + +// <rdar://problem/4096913> Specifies how many times we'll try and connect to the +// server. + +#define DNSSD_CLIENT_MAXTRIES 4 + +#define CTL_PATH_PREFIX "/tmp/dnssd_clippath." +// error socket (if needed) is named "dnssd_clipath.[pid].xxx:n" where xxx are the +// last 3 digits of the time (in seconds) and n is the 6-digit microsecond time + +// general utility functions +typedef struct _DNSServiceRef_t + { + dnssd_sock_t sockfd; // connected socket between client and daemon + uint32_t op; // request_op_t or reply_op_t + process_reply_callback process_reply; + void *app_callback; + void *app_context; + uint32_t max_index; //largest assigned record index - 0 if no additl. recs registered + } _DNSServiceRef_t; + +typedef struct _DNSRecordRef_t + { + void *app_context; + DNSServiceRegisterRecordReply app_callback; + DNSRecordRef recref; + uint32_t record_index; // index is unique to the ServiceDiscoveryRef + DNSServiceRef sdr; + } _DNSRecordRef_t; + +// exported functions + +// write len bytes. return 0 on success, -1 on error +static int write_all(dnssd_sock_t sd, char *buf, int len) + { + // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. + //if (send(sd, buf, len, MSG_WAITALL) != len) return -1; + while (len) + { + ssize_t num_written = send(sd, buf, len, 0); + if (num_written < 0 || num_written > len) return -1; + buf += num_written; + len -= num_written; + } + return 0; + } + +// read len bytes. return 0 on success, -1 on error +static int read_all(dnssd_sock_t sd, char *buf, int len) + { + // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead. + //if (recv(sd, buf, len, MSG_WAITALL) != len) return -1; + while (len) + { + ssize_t num_read = recv(sd, buf, len, 0); + if ((num_read == -1) && (errno == EINTR)) + continue; + if ((num_read < 0) || (num_read > len)) return -1; + // Return error -2 when no data received and errno is not set + if (num_read == 0) return -2; + buf += num_read; + len -= num_read; + } + return 0; + } + +/* create_hdr + * + * allocate and initialize an ipc message header. value of len should initially be the + * length of the data, and is set to the value of the data plus the header. data_start + * is set to point to the beginning of the data section. reuse_socket should be non-zero + * for calls that can receive an immediate error return value on their primary socket. + * if zero, the path to a control socket is appended at the beginning of the message buffer. + * data_start is set past this string. + */ + +static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int reuse_socket) + { + char *msg = NULL; + ipc_msg_hdr *hdr; + int datalen; +#if !defined(USE_TCP_LOOPBACK) + char ctrl_path[256]; +#endif + + if (!reuse_socket) + { +#if defined(USE_TCP_LOOPBACK) + *len += 2; // Allocate space for two-byte port number +#else + struct timeval time; + if (gettimeofday(&time, NULL) < 0) return NULL; + sprintf(ctrl_path, "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(), + (unsigned long)(time.tv_sec & 0xFFF), (unsigned long)(time.tv_usec)); + *len += strlen(ctrl_path) + 1; +#endif + } + + datalen = (int) *len; + *len += sizeof(ipc_msg_hdr); + + // write message to buffer + msg = malloc(*len); + if (!msg) return NULL; + + bzero(msg, *len); + hdr = (void *)msg; + hdr->datalen = datalen; + hdr->version = VERSION; + hdr->op = op; + if (reuse_socket) hdr->flags |= IPC_FLAGS_REUSE_SOCKET; + *data_start = msg + sizeof(ipc_msg_hdr); +#if defined(USE_TCP_LOOPBACK) + // Put dummy data in for the port, since we don't know what + // it is yet. The data will get filled in before we + // send the message. This happens in deliver_request(). + if (!reuse_socket) put_short(0, data_start); +#else + if (!reuse_socket) put_string(ctrl_path, data_start); +#endif + return hdr; + } + + // return a connected service ref (deallocate with DNSServiceRefDeallocate) +static DNSServiceRef connect_to_server(void) + { + dnssd_sockaddr_t saddr; + DNSServiceRef sdr; + int NumTries = 0; + +#if defined(_WIN32) + if (!g_initWinsock) + { + WSADATA wsaData; + DNSServiceErrorType err; + + g_initWinsock = 1; + + err = WSAStartup( MAKEWORD( 2, 2 ), &wsaData ); + + if (err != 0) return NULL; + } + + // <rdar://problem/4096913> If the system service is disabled, we only want to try + // to connect once + + if ( IsSystemServiceDisabled() ) + { + NumTries = DNSSD_CLIENT_MAXTRIES; + } + +#endif + + sdr = malloc(sizeof(_DNSServiceRef_t)); + if (!sdr) return(NULL); + sdr->sockfd = socket(AF_DNSSD, SOCK_STREAM, 0); + if (sdr->sockfd == dnssd_InvalidSocket) { free(sdr); return NULL; } +#if defined(USE_TCP_LOOPBACK) + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + saddr.sin_port = htons(MDNS_TCP_SERVERPORT); +#else + saddr.sun_family = AF_LOCAL; + strcpy(saddr.sun_path, MDNS_UDS_SERVERPATH); +#endif + while (1) + { + int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr)); + if (!err) break; // If we succeeded, return sdr + // If we failed, then it may be because the daemon is still launching. + // This can happen for processes that launch early in the boot process, while the + // daemon is still coming up. Rather than fail here, we'll wait a bit and try again. + // If, after four seconds, we still can't connect to the daemon, + // then we give up and return a failure code. + if (++NumTries < DNSSD_CLIENT_MAXTRIES) + sleep(1); // Sleep a bit, then try again + else + { + dnssd_close(sdr->sockfd); + sdr->sockfd = dnssd_InvalidSocket; + free(sdr); + return NULL; + } + } + return sdr; + } + +static DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd) + { + ipc_msg_hdr *hdr = msg; + uint32_t datalen = hdr->datalen; + dnssd_sockaddr_t caddr, daddr; // (client and daemon address structs) + char *const data = (char *)msg + sizeof(ipc_msg_hdr); + dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket; + int ret; + dnssd_socklen_t len = (dnssd_socklen_t) sizeof(caddr); + DNSServiceErrorType err = kDNSServiceErr_Unknown; + + if (!hdr || sdr->sockfd < 0) return kDNSServiceErr_Unknown; + + if (!reuse_sd) + { + // setup temporary error socket + if ((listenfd = socket(AF_DNSSD, SOCK_STREAM, 0)) < 0) + goto cleanup; + bzero(&caddr, sizeof(caddr)); + +#if defined(USE_TCP_LOOPBACK) + { + union { uint16_t s; u_char b[2]; } port; + caddr.sin_family = AF_INET; + caddr.sin_port = 0; + caddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR); + ret = bind(listenfd, (struct sockaddr*) &caddr, sizeof(caddr)); + if (ret < 0) goto cleanup; + if (getsockname(listenfd, (struct sockaddr*) &caddr, &len) < 0) goto cleanup; + listen(listenfd, 1); + port.s = caddr.sin_port; + data[0] = port.b[0]; // don't switch the byte order, as the + data[1] = port.b[1]; // daemon expects it in network byte order + } +#else + { + mode_t mask = umask(0); + caddr.sun_family = AF_LOCAL; +// According to Stevens (section 3.2), there is no portable way to +// determine whether sa_len is defined on a particular platform. +#ifndef NOT_HAVE_SA_LEN + caddr.sun_len = sizeof(struct sockaddr_un); +#endif + //syslog(LOG_WARNING, "deliver_request: creating UDS: %s\n", data); + strcpy(caddr.sun_path, data); + ret = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr)); + umask(mask); + if (ret < 0) goto cleanup; + listen(listenfd, 1); + } +#endif + } + + ConvertHeaderBytes(hdr); + //syslog(LOG_WARNING, "deliver_request writing %ld bytes\n", datalen + sizeof(ipc_msg_hdr)); + //syslog(LOG_WARNING, "deliver_request name is %s\n", (char *)msg + sizeof(ipc_msg_hdr)); + if (write_all(sdr->sockfd, msg, datalen + sizeof(ipc_msg_hdr)) < 0) + goto cleanup; + free(msg); + msg = NULL; + + if (reuse_sd) errsd = sdr->sockfd; + else + { + //syslog(LOG_WARNING, "deliver_request: accept\n"); + len = sizeof(daddr); + errsd = accept(listenfd, (struct sockaddr *)&daddr, &len); + //syslog(LOG_WARNING, "deliver_request: accept returned %d\n", errsd); + if (errsd < 0) goto cleanup; + } + + if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0) + err = kDNSServiceErr_Unknown; + else + err = ntohl(err); + + //syslog(LOG_WARNING, "deliver_request: retrieved error code %d\n", err); + +cleanup: + if (!reuse_sd) + { + if (listenfd > 0) dnssd_close(listenfd); + if (errsd > 0) dnssd_close(errsd); +#if !defined(USE_TCP_LOOPBACK) + // syslog(LOG_WARNING, "deliver_request: removing UDS: %s\n", data); + if (unlink(data) != 0) + syslog(LOG_WARNING, "WARNING: unlink(\"%s\") failed errno %d (%s)", data, errno, strerror(errno)); + // else syslog(LOG_WARNING, "deliver_request: removed UDS: %s\n", data); +#endif + } + if (msg) free(msg); + return err; + } + +int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) + { + if (!sdRef) return -1; + return (int) sdRef->sockfd; + } + +// handle reply from server, calling application client callback. If there is no reply +// from the daemon on the socket contained in sdRef, the call will block. +DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef) + { + ipc_msg_hdr hdr; + char *data; + int rderr; + + if (!sdRef || sdRef->sockfd < 0 || !sdRef->process_reply) + return kDNSServiceErr_BadReference; + + rderr = read_all(sdRef->sockfd, (void *)&hdr, sizeof(hdr)); + if (rderr < 0) { + // return NoError on EWOULDBLOCK. This will handle the case + // where a non-blocking socket is told there is data, but + // it was a false positive. Can check errno when error + // code returned is -1 + if ((rderr == -1) && (dnssd_errno() == dnssd_EWOULDBLOCK)) + return kDNSServiceErr_NoError; + return kDNSServiceErr_Unknown; + } + ConvertHeaderBytes(&hdr); + if (hdr.version != VERSION) + return kDNSServiceErr_Incompatible; + data = malloc(hdr.datalen); + if (!data) return kDNSServiceErr_NoMemory; + if (read_all(sdRef->sockfd, data, hdr.datalen) < 0) + return kDNSServiceErr_Unknown; + sdRef->process_reply(sdRef, &hdr, data); + free(data); + return kDNSServiceErr_NoError; + } + +void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef) + { + if (!sdRef) return; + if (sdRef->sockfd > 0) dnssd_close(sdRef->sockfd); + free(sdRef); + } + +static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + char fullname[kDNSServiceMaxDomainName]; + char target[kDNSServiceMaxDomainName]; + uint16_t txtlen; + union { uint16_t s; u_char b[2]; } port; + uint32_t ifi; + DNSServiceErrorType err; + unsigned char *txtrecord; + int str_error = 0; + (void)hdr; //unused + + flags = get_flags(&data); + ifi = get_long(&data); + err = get_error_code(&data); + if (get_string(&data, fullname, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (get_string(&data, target, kDNSServiceMaxDomainName) < 0) str_error = 1; + port.b[0] = *data++; + port.b[1] = *data++; + txtlen = get_short(&data); + txtrecord = (unsigned char *)get_rdata(&data, txtlen); + + if (!err && str_error) err = kDNSServiceErr_Unknown; + ((DNSServiceResolveReply)sdr->app_callback)(sdr, flags, ifi, err, fullname, target, port.s, txtlen, txtrecord, sdr->app_context); + } + +DNSServiceErrorType DNSSD_API DNSServiceResolve + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + DNSServiceResolveReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam; + + // calculate total message length + len = sizeof(flags); + len += sizeof(interfaceIndex); + len += strlen(name) + 1; + len += strlen(regtype) + 1; + len += strlen(domain) + 1; + + hdr = create_hdr(resolve_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (void *)hdr; + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(name, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + sdr->op = resolve_request; + sdr->process_reply = handle_resolve_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + +static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex, ttl; + DNSServiceErrorType errorCode; + char name[kDNSServiceMaxDomainName]; + uint16_t rrtype, rrclass, rdlen; + char *rdata; + int str_error = 0; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + if (get_string(&data, name, kDNSServiceMaxDomainName) < 0) str_error = 1; + rrtype = get_short(&data); + rrclass = get_short(&data); + rdlen = get_short(&data); + rdata = get_rdata(&data, rdlen); + ttl = get_long(&data); + + if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown; + ((DNSServiceQueryRecordReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, name, rrtype, rrclass, + rdlen, rdata, ttl, sdr->app_context); + return; + } + +DNSServiceErrorType DNSSD_API DNSServiceQueryRecord + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + uint16_t rrtype, + uint16_t rrclass, + DNSServiceQueryRecordReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + if (!name) name = "\0"; + + // calculate total message length + len = sizeof(flags); + len += sizeof(uint32_t); //interfaceIndex + len += strlen(name) + 1; + len += 2 * sizeof(uint16_t); // rrtype, rrclass + + hdr = create_hdr(query_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (void *)hdr; + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(name, &ptr); + put_short(rrtype, &ptr); + put_short(rrclass, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + + sdr->op = query_request; + sdr->process_reply = handle_query_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + +static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + char replyName[256], replyType[kDNSServiceMaxDomainName], + replyDomain[kDNSServiceMaxDomainName]; + int str_error = 0; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + if (get_string(&data, replyName, 256) < 0) str_error = 1; + if (get_string(&data, replyType, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (get_string(&data, replyDomain, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown; + ((DNSServiceBrowseReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, replyName, replyType, replyDomain, sdr->app_context); + } + +DNSServiceErrorType DNSSD_API DNSServiceBrowse + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *regtype, + const char *domain, + DNSServiceBrowseReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + if (!domain) domain = ""; + + len = sizeof(flags); + len += sizeof(interfaceIndex); + len += strlen(regtype) + 1; + len += strlen(domain) + 1; + + hdr = create_hdr(browse_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (char *)hdr; + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + sdr->op = browse_request; + sdr->process_reply = handle_browse_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser + ( + DNSServiceFlags flags, + const char *domain + ) + { + DNSServiceRef sdr; + DNSServiceErrorType err; + char *ptr = NULL; + size_t len = sizeof(flags) + strlen(domain) + 1; + ipc_msg_hdr *hdr = create_hdr(setdomain_request, &len, &ptr, 1); + + if (!hdr) return kDNSServiceErr_Unknown; + put_flags(flags, &ptr); + put_string(domain, &ptr); + + sdr = connect_to_server(); + if (!sdr) { free(hdr); return kDNSServiceErr_Unknown; } + err = deliver_request((char *)hdr, sdr, 1); // deliver_request frees the message for us + DNSServiceRefDeallocate(sdr); + return err; + } + + +static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName]; + int str_error = 0; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + if (get_string(&data, name, 256) < 0) str_error = 1; + if (get_string(&data, regtype, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (get_string(&data, domain, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown; + ((DNSServiceRegisterReply)sdr->app_callback)(sdr, flags, errorCode, name, regtype, domain, sdr->app_context); + } + +DNSServiceErrorType DNSSD_API DNSServiceRegister + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *name, + const char *regtype, + const char *domain, + const char *host, + uint16_t PortInNetworkByteOrder, + uint16_t txtLen, + const void *txtRecord, + DNSServiceRegisterReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + union { uint16_t s; u_char b[2]; } port = { PortInNetworkByteOrder }; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + if (!name) name = ""; + if (!regtype) return kDNSServiceErr_BadParam; + if (!domain) domain = ""; + if (!host) host = ""; + if (!txtRecord) txtRecord = (void*)""; + + // auto-name must also have auto-rename + if (!name[0] && (flags & kDNSServiceFlagsNoAutoRename)) + return kDNSServiceErr_BadParam; + + // no callback must have auto-rename + if (!callBack && (flags & kDNSServiceFlagsNoAutoRename)) return kDNSServiceErr_BadParam; + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); // interfaceIndex + len += strlen(name) + strlen(regtype) + strlen(domain) + strlen(host) + 4; + len += 2 * sizeof(uint16_t); // port, txtLen + len += txtLen; + + hdr = create_hdr(reg_service_request, &len, &ptr, 1); + if (!hdr) goto error; + if (!callBack) hdr->flags |= IPC_FLAGS_NOREPLY; + msg = (char *)hdr; + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(name, &ptr); + put_string(regtype, &ptr); + put_string(domain, &ptr); + put_string(host, &ptr); + *ptr++ = port.b[0]; + *ptr++ = port.b[1]; + put_short(txtLen, &ptr); + put_rdata(txtLen, txtRecord, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + + sdr->op = reg_service_request; + sdr->process_reply = callBack ? handle_regservice_response : NULL; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + +static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType err; + char domain[kDNSServiceMaxDomainName]; + int str_error = 0; + (void)hdr;//Unused + + flags = get_flags(&data); + interfaceIndex = get_long(&data); + err = get_error_code(&data); + if (get_string(&data, domain, kDNSServiceMaxDomainName) < 0) str_error = 1; + if (!err && str_error) err = kDNSServiceErr_Unknown; + ((DNSServiceDomainEnumReply)sdr->app_callback)(sdr, flags, interfaceIndex, err, domain, sdr->app_context); + } + +DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains + ( + DNSServiceRef *sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceDomainEnumReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef sdr; + DNSServiceErrorType err; + int f1 = (flags & kDNSServiceFlagsBrowseDomains) != 0; + int f2 = (flags & kDNSServiceFlagsRegistrationDomains) != 0; + if (f1 + f2 != 1) return kDNSServiceErr_BadParam; + + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = NULL; + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); + + hdr = create_hdr(enumeration_request, &len, &ptr, 1); + if (!hdr) goto error; + msg = (void *)hdr; + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + + sdr = connect_to_server(); + if (!sdr) goto error; + err = deliver_request(msg, sdr, 1); + if (err) + { + DNSServiceRefDeallocate(sdr); + return err; + } + + sdr->op = enumeration_request; + sdr->process_reply = handle_enumeration_response; + sdr->app_callback = callBack; + sdr->app_context = context; + *sdRef = sdr; + return err; + +error: + if (msg) free(msg); + if (*sdRef) { free(*sdRef); *sdRef = NULL; } + return kDNSServiceErr_Unknown; + } + +static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data) + { + DNSServiceFlags flags; + uint32_t interfaceIndex; + DNSServiceErrorType errorCode; + DNSRecordRef rref = hdr->client_context.context; + + if (sdr->op != connection) + { + rref->app_callback(rref->sdr, rref, 0, kDNSServiceErr_Unknown, rref->app_context); + return; + } + flags = get_flags(&data); + interfaceIndex = get_long(&data); + errorCode = get_error_code(&data); + + rref->app_callback(rref->sdr, rref, flags, errorCode, rref->app_context); + } + +DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef) + { + if (!sdRef) return kDNSServiceErr_BadParam; + *sdRef = connect_to_server(); + if (!*sdRef) + return kDNSServiceErr_Unknown; + (*sdRef)->op = connection; + (*sdRef)->process_reply = handle_regrecord_response; + return 0; + } + +DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata, + uint32_t ttl, + DNSServiceRegisterRecordReply callBack, + void *context + ) + { + char *msg = NULL, *ptr; + size_t len; + ipc_msg_hdr *hdr = NULL; + DNSServiceRef tmp = NULL; + DNSRecordRef rref = NULL; + int f1 = (flags & kDNSServiceFlagsShared) != 0; + int f2 = (flags & kDNSServiceFlagsUnique) != 0; + if (f1 + f2 != 1) return kDNSServiceErr_BadParam; + + if (!sdRef || sdRef->op != connection || sdRef->sockfd < 0) + return kDNSServiceErr_BadReference; + *RecordRef = NULL; + + len = sizeof(DNSServiceFlags); + len += 2 * sizeof(uint32_t); // interfaceIndex, ttl + len += 3 * sizeof(uint16_t); // rrtype, rrclass, rdlen + len += strlen(fullname) + 1; + len += rdlen; + + hdr = create_hdr(reg_record_request, &len, &ptr, 0); + if (!hdr) goto error; + msg = (char *)hdr; + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(fullname, &ptr); + put_short(rrtype, &ptr); + put_short(rrclass, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_long(ttl, &ptr); + + rref = malloc(sizeof(_DNSRecordRef_t)); + if (!rref) goto error; + rref->app_context = context; + rref->app_callback = callBack; + rref->record_index = sdRef->max_index++; + rref->sdr = sdRef; + *RecordRef = rref; + hdr->client_context.context = rref; + hdr->reg_index = rref->record_index; + + return deliver_request(msg, sdRef, 0); + +error: + if (rref) free(rref); + if (tmp) free(tmp); + if (hdr) free(hdr); + return kDNSServiceErr_Unknown; + } + +//sdRef returned by DNSServiceRegister() +DNSServiceErrorType DNSSD_API DNSServiceAddRecord + ( + DNSServiceRef sdRef, + DNSRecordRef *RecordRef, + DNSServiceFlags flags, + uint16_t rrtype, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ) + { + ipc_msg_hdr *hdr; + size_t len = 0; + char *ptr; + DNSRecordRef rref; + + if (!sdRef || (sdRef->op != reg_service_request) || !RecordRef) + return kDNSServiceErr_BadReference; + *RecordRef = NULL; + + len += 2 * sizeof(uint16_t); //rrtype, rdlen + len += rdlen; + len += sizeof(uint32_t); + len += sizeof(DNSServiceFlags); + + hdr = create_hdr(add_record_request, &len, &ptr, 0); + if (!hdr) return kDNSServiceErr_Unknown; + put_flags(flags, &ptr); + put_short(rrtype, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_long(ttl, &ptr); + + rref = malloc(sizeof(_DNSRecordRef_t)); + if (!rref) goto error; + rref->app_context = NULL; + rref->app_callback = NULL; + rref->record_index = sdRef->max_index++; + rref->sdr = sdRef; + *RecordRef = rref; + hdr->client_context.context = rref; + hdr->reg_index = rref->record_index; + return deliver_request((char *)hdr, sdRef, 0); + +error: + if (hdr) free(hdr); + if (rref) free(rref); + if (*RecordRef) *RecordRef = NULL; + return kDNSServiceErr_Unknown; +} + +//DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord +DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags, + uint16_t rdlen, + const void *rdata, + uint32_t ttl + ) + { + ipc_msg_hdr *hdr; + size_t len = 0; + char *ptr; + + if (!sdRef) return kDNSServiceErr_BadReference; + + len += sizeof(uint16_t); + len += rdlen; + len += sizeof(uint32_t); + len += sizeof(DNSServiceFlags); + + hdr = create_hdr(update_record_request, &len, &ptr, 0); + if (!hdr) return kDNSServiceErr_Unknown; + hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX; + put_flags(flags, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + put_long(ttl, &ptr); + return deliver_request((char *)hdr, sdRef, 0); + } + +DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord + ( + DNSServiceRef sdRef, + DNSRecordRef RecordRef, + DNSServiceFlags flags + ) + { + ipc_msg_hdr *hdr; + size_t len = 0; + char *ptr; + DNSServiceErrorType err; + + if (!sdRef || !RecordRef || !sdRef->max_index) + return kDNSServiceErr_BadReference; + + len += sizeof(flags); + hdr = create_hdr(remove_record_request, &len, &ptr, 0); + if (!hdr) return kDNSServiceErr_Unknown; + hdr->reg_index = RecordRef->record_index; + put_flags(flags, &ptr); + err = deliver_request((char *)hdr, sdRef, 0); + if (!err) free(RecordRef); + return err; + } + +DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord + ( + DNSServiceFlags flags, + uint32_t interfaceIndex, + const char *fullname, + uint16_t rrtype, + uint16_t rrclass, + uint16_t rdlen, + const void *rdata + ) + { + char *ptr; + size_t len; + ipc_msg_hdr *hdr; + DNSServiceRef tmp; + + len = sizeof(DNSServiceFlags); + len += sizeof(uint32_t); + len += strlen(fullname) + 1; + len += 3 * sizeof(uint16_t); + len += rdlen; + tmp = connect_to_server(); + if (!tmp) return(kDNSServiceErr_Unknown); + hdr = create_hdr(reconfirm_record_request, &len, &ptr, 1); + if (!hdr) return(kDNSServiceErr_Unknown); + + put_flags(flags, &ptr); + put_long(interfaceIndex, &ptr); + put_string(fullname, &ptr); + put_short(rrtype, &ptr); + put_short(rrclass, &ptr); + put_short(rdlen, &ptr); + put_rdata(rdlen, rdata, &ptr); + ConvertHeaderBytes(hdr); + write_all(tmp->sockfd, (char *)hdr, (int) len); + free(hdr); + DNSServiceRefDeallocate(tmp); + return(kDNSServiceErr_NoError); + } + diff --git a/usr/src/lib/libdns_sd/common/dnssd_ipc.c b/usr/src/lib/libdns_sd/common/dnssd_ipc.c new file mode 100644 index 0000000000..92e76bab0c --- /dev/null +++ b/usr/src/lib/libdns_sd/common/dnssd_ipc.c @@ -0,0 +1,135 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Change History (most recent first): + +$Log: dnssd_ipc.c,v $ +Revision 1.16 2006/08/14 23:05:53 cheshire +Added "tab-width" emacs header line + +Revision 1.15 2005/01/27 22:57:56 cheshire +Fix compile errors on gcc4 + +Revision 1.14 2004/10/06 02:22:20 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.13 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.12 2004/09/16 23:14:24 cheshire +Changes for Windows compatibility + +Revision 1.11 2004/06/18 04:56:09 rpantos +casting goodness + +Revision 1.10 2004/06/12 01:08:14 cheshire +Changes for Windows compatibility + +Revision 1.9 2004/05/18 23:51:27 cheshire +Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers + +Revision 1.8 2003/11/05 22:44:57 ksekar +<rdar://problem/3335230>: No bounds checking when reading data from client +Reviewed by: Stuart Cheshire + +Revision 1.7 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "dnssd_ipc.h" + +void put_long(const uint32_t l, char **ptr) + { + (*ptr)[0] = (char)((l >> 24) & 0xFF); + (*ptr)[1] = (char)((l >> 16) & 0xFF); + (*ptr)[2] = (char)((l >> 8) & 0xFF); + (*ptr)[3] = (char)((l ) & 0xFF); + *ptr += sizeof(uint32_t); + } + +uint32_t get_long(char **ptr) + { + uint8_t *p = (uint8_t*) *ptr; + *ptr += sizeof(uint32_t); + return((uint32_t) ((uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | p[3])); + } + +void put_short(uint16_t s, char **ptr) + { + (*ptr)[0] = (char)((s >> 8) & 0xFF); + (*ptr)[1] = (char)((s ) & 0xFF); + *ptr += sizeof(uint16_t); + } + +uint16_t get_short(char **ptr) + { + uint8_t *p = (uint8_t*) *ptr; + *ptr += sizeof(uint16_t); + return((uint16_t) ((uint16_t)p[0] << 8 | p[1])); + } + +int put_string(const char *str, char **ptr) + { + if (!str) str = ""; + strcpy(*ptr, str); + *ptr += strlen(str) + 1; + return 0; + } + +int get_string(char **ptr, char *buffer, int buflen) + { + int overrun = (int)strlen(*ptr) < buflen ? 0 : -1; + strncpy(buffer, *ptr, buflen - 1); + buffer[buflen - 1] = '\0'; + *ptr += strlen(buffer) + 1; + return overrun; + } + +void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr) + { + memcpy(*ptr, rdata, rdlen); + *ptr += rdlen; + } + +char *get_rdata(char **ptr, int rdlen) + { + char *rd = *ptr; + *ptr += rdlen; + return rd; + } + +void ConvertHeaderBytes(ipc_msg_hdr *hdr) + { + hdr->version = htonl(hdr->version); + hdr->datalen = htonl(hdr->datalen); + hdr->flags = htonl(hdr->flags); + hdr->op = htonl(hdr->op ); + hdr->reg_index = htonl(hdr->reg_index); + } diff --git a/usr/src/lib/libdns_sd/common/dnssd_ipc.h b/usr/src/lib/libdns_sd/common/dnssd_ipc.h new file mode 100644 index 0000000000..2f76591f9a --- /dev/null +++ b/usr/src/lib/libdns_sd/common/dnssd_ipc.h @@ -0,0 +1,253 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Change History (most recent first): + +$Log: dnssd_ipc.h,v $ +Revision 1.23 2006/08/14 23:05:53 cheshire +Added "tab-width" emacs header line + +Revision 1.22 2006/06/28 08:56:26 cheshire +Added "_op" to the end of the operation code enum values, +to differentiate them from the routines with the same names + +Revision 1.21 2005/09/29 06:38:13 herscher +Remove #define MSG_WAITALL on Windows. We don't use this macro anymore, and it's presence causes warnings to be emitted when compiling against the latest Microsoft Platform SDK. + +Revision 1.20 2005/03/21 00:39:31 shersche +<rdar://problem/4021486> Fix build warnings on Win32 platform + +Revision 1.19 2005/02/02 02:25:22 cheshire +<rdar://problem/3980388> /var/run/mDNSResponder should be /var/run/mdnsd on Linux + +Revision 1.18 2005/01/27 22:57:56 cheshire +Fix compile errors on gcc4 + +Revision 1.17 2004/11/23 03:39:47 cheshire +Let interface name/index mapping capability live directly in JNISupport.c, +instead of having to call through to the daemon via IPC to get this information. + +Revision 1.16 2004/11/12 03:21:41 rpantos +rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex. + +Revision 1.15 2004/10/06 02:22:20 cheshire +Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)" + +Revision 1.14 2004/10/01 22:15:55 rpantos +rdar://problem/3824265: Replace APSL in client lib with BSD license. + +Revision 1.13 2004/09/16 23:14:25 cheshire +Changes for Windows compatibility + +Revision 1.12 2004/09/16 21:46:38 ksekar +<rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area domain + +Revision 1.11 2004/08/10 06:24:56 cheshire +Use types with precisely defined sizes for 'op' and 'reg_index', for better +compatibility if the daemon and the client stub are built using different compilers + +Revision 1.10 2004/07/07 17:39:25 shersche +Change MDNS_SERVERPORT from 5533 to 5354. + +Revision 1.9 2004/06/25 00:26:27 rpantos +Changes to fix the Posix build on Solaris. + +Revision 1.8 2004/06/18 04:56:51 rpantos +Add layer for platform code + +Revision 1.7 2004/06/12 01:08:14 cheshire +Changes for Windows compatibility + +Revision 1.6 2003/08/12 19:56:25 cheshire +Update to APSL 2.0 + + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifndef DNSSD_IPC_H +#define DNSSD_IPC_H + +#include "dns_sd.h" + + +// +// Common cross platform services +// +#if defined(WIN32) +# include <winsock2.h> +# define dnssd_InvalidSocket INVALID_SOCKET +# define dnssd_EWOULDBLOCK WSAEWOULDBLOCK +# define dnssd_EINTR WSAEINTR +# define dnssd_sock_t SOCKET +# define dnssd_socklen_t int +# define dnssd_sockbuf_t const char* +# define dnssd_close(sock) closesocket(sock) +# define dnssd_errno() WSAGetLastError() +# define ssize_t int +# define getpid _getpid +#else +# include <sys/types.h> +# include <unistd.h> +# include <sys/un.h> +# include <string.h> +# include <stdio.h> +# include <stdlib.h> +# include <sys/stat.h> +# include <sys/socket.h> +# include <netinet/in.h> +# define dnssd_InvalidSocket -1 +# define dnssd_EWOULDBLOCK EWOULDBLOCK +# define dnssd_EINTR EINTR +# define dnssd_EPIPE EPIPE +# define dnssd_sock_t int +# define dnssd_socklen_t unsigned int +# define dnssd_sockbuf_t const char* +# define dnssd_close(sock) close(sock) +# define dnssd_errno() errno +#endif + +#if defined(USE_TCP_LOOPBACK) +# define AF_DNSSD AF_INET +# define MDNS_TCP_SERVERADDR "127.0.0.1" +# define MDNS_TCP_SERVERPORT 5354 +# define LISTENQ 5 +# define dnssd_sockaddr_t struct sockaddr_in +#else +# define AF_DNSSD AF_LOCAL +# ifndef MDNS_UDS_SERVERPATH +# define MDNS_UDS_SERVERPATH "/var/run/mDNSResponder" +# endif +# define LISTENQ 100 + // longest legal control path length +# define MAX_CTLPATH 256 +# define dnssd_sockaddr_t struct sockaddr_un +#endif + + +//#define UDSDEBUG // verbose debug output + +// Compatibility workaround +#ifndef AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif + +// General UDS constants +#define TXT_RECORD_INDEX ((uint32_t)(-1)) // record index for default text record + +// IPC data encoding constants and types +#define VERSION 1 +#define IPC_FLAGS_NOREPLY 1 // set flag if no asynchronous replies are to be sent to client +#define IPC_FLAGS_REUSE_SOCKET 2 // set flag if synchronous errors are to be sent via the primary socket + // (if not set, first string in message buffer must be path to error socket + +typedef enum + { + connection = 1, // connected socket via DNSServiceConnect() + reg_record_request, // reg/remove record only valid for connected sockets + remove_record_request, + enumeration_request, + reg_service_request, + browse_request, + resolve_request, + query_request, + reconfirm_record_request, + add_record_request, + update_record_request, + setdomain_request + } request_op_t; + +typedef enum + { + enumeration_reply_op = 64, + reg_service_reply_op, + browse_reply_op, + resolve_reply_op, + query_reply_op, + reg_record_reply_op + } reply_op_t; + +typedef struct ipc_msg_hdr_struct ipc_msg_hdr; + +// client stub callback to process message from server and deliver results to +// client application + +typedef void (*process_reply_callback) + ( + DNSServiceRef sdr, + ipc_msg_hdr *hdr, + char *msg + ); + +// allow 64-bit client to interoperate w/ 32-bit daemon +typedef union + { + void *context; + uint32_t ptr64[2]; + } client_context_t; + +typedef struct ipc_msg_hdr_struct + { + uint32_t version; + uint32_t datalen; + uint32_t flags; + uint32_t op; // request_op_t or reply_op_t + client_context_t client_context; // context passed from client, returned by server in corresponding reply + uint32_t reg_index; // identifier for a record registered via DNSServiceRegisterRecord() on a + // socket connected by DNSServiceConnect(). Must be unique in the scope of the connection, such that and + // index/socket pair uniquely identifies a record. (Used to select records for removal by DNSServiceRemoveRecord()) + uint32_t padbytes; + } ipc_msg_hdr_struct; + +// it is advanced to point to the next field, or the end of the message +// routines to write to and extract data from message buffers. +// caller responsible for bounds checking. +// ptr is the address of the pointer to the start of the field. +// it is advanced to point to the next field, or the end of the message + +void put_long(const uint32_t l, char **ptr); +uint32_t get_long(char **ptr); + +void put_short(uint16_t s, char **ptr); +uint16_t get_short(char **ptr); + +#define put_flags put_long +#define get_flags get_long + +#define put_error_code put_long +#define get_error_code get_long + +int put_string(const char *str, char **ptr); +int get_string(char **ptr, char *buffer, int buflen); + +void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr); +char *get_rdata(char **ptr, int rdlen); // return value is rdata pointed to by *ptr - + // rdata is not copied from buffer. + +void ConvertHeaderBytes(ipc_msg_hdr *hdr); + +#endif // DNSSD_IPC_H diff --git a/usr/src/lib/libdns_sd/common/llib-ldns_sd b/usr/src/lib/libdns_sd/common/llib-ldns_sd new file mode 100644 index 0000000000..464e3b9a6d --- /dev/null +++ b/usr/src/lib/libdns_sd/common/llib-ldns_sd @@ -0,0 +1,32 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* LINTLIBRARY */ +/* PROTOLIB1 */ + +#include <dns_sd.h> diff --git a/usr/src/lib/libdns_sd/common/mapfile-vers b/usr/src/lib/libdns_sd/common/mapfile-vers new file mode 100644 index 0000000000..012b730ca1 --- /dev/null +++ b/usr/src/lib/libdns_sd/common/mapfile-vers @@ -0,0 +1,56 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +SUNW_1.1 { + global: + DNSServiceAddRecord; + DNSServiceBrowse; + DNSServiceConstructFullName; + DNSServiceCreateConnection; + DNSServiceEnumerateDomains; + DNSServiceProcessResult; + DNSServiceQueryRecord; + DNSServiceReconfirmRecord; + DNSServiceRefDeallocate; + DNSServiceRefSockFD; + DNSServiceRegister; + DNSServiceRegisterRecord; + DNSServiceRemoveRecord; + DNSServiceResolve; + DNSServiceUpdateRecord; + TXTRecordContainsKey; + TXTRecordCreate; + TXTRecordDeallocate; + TXTRecordGetBytesPtr; + TXTRecordGetCount; + TXTRecordGetItemAtIndex; + TXTRecordGetLength; + TXTRecordGetValuePtr; + TXTRecordRemoveValue; + TXTRecordSetValue; + local: + *; +}; diff --git a/usr/src/lib/libdns_sd/i386/Makefile b/usr/src/lib/libdns_sd/i386/Makefile new file mode 100644 index 0000000000..9c1be5bf3a --- /dev/null +++ b/usr/src/lib/libdns_sd/i386/Makefile @@ -0,0 +1,28 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libdns_sd/java/Makefile b/usr/src/lib/libdns_sd/java/Makefile new file mode 100644 index 0000000000..5b015fc86e --- /dev/null +++ b/usr/src/lib/libdns_sd/java/Makefile @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include $(SRC)/lib/Makefile.lib + +POFILE = libjdns_sd.po + +SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET= all +clean := TARGET= clean +clobber := TARGET= clobber +install := TARGET= install +lint := TARGET= lint + +.KEEP_STATE: + +all install clean clobber lint: com/apple .WAIT $(SUBDIRS) + +com/apple $(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/lib/libdns_sd/java/Makefile.com b/usr/src/lib/libdns_sd/java/Makefile.com new file mode 100644 index 0000000000..f66463ac2f --- /dev/null +++ b/usr/src/lib/libdns_sd/java/Makefile.com @@ -0,0 +1,57 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +LIBRARY= libjdns_sd.a +VERS= .1 + +OBJECTS= JNISupport.o + +include $(SRC)/lib/Makefile.lib + +LIBS = $(DYNLIB) + +SRCDIR = ../common + +C99MODE = $(C99_ENABLE) +CPPFLAGS += -I$(JAVA_ROOT)/include -I$(JAVA_ROOT)/include/solaris +CPPFLAGS += -I../com/apple/dnssd +CPPFLAGS += -D_REENTRANT + +LDLIBS += -lc -lsocket -ldns_sd + +CLEANFILES= $(LINTOUT) $(LINTLIB) + +LINTLIB = + +.KEEP_STATE: + +lint: lintcheck + +include $(SRC)/lib/Makefile.targ + +pics/%.o: ../common/%.c + $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) diff --git a/usr/src/lib/libdns_sd/java/amd64/Makefile b/usr/src/lib/libdns_sd/java/amd64/Makefile new file mode 100644 index 0000000000..b645e1caed --- /dev/null +++ b/usr/src/lib/libdns_sd/java/amd64/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com +include ../../../Makefile.lib.64 + +all: $(LIBS) + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libdns_sd/java/com/apple/Makefile b/usr/src/lib/libdns_sd/java/com/apple/Makefile new file mode 100644 index 0000000000..8cfdc2e941 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/Makefile @@ -0,0 +1,47 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include Makefile.com + +SUBDIRS = dnssd + +all:= TARGET = all +clean:= TARGET = clean +clobber:= TARGET = clobber +install:= TARGET = install + +.KEEP_STATE: + +all clean install clobber: ${SUBDIRS} + +${SUBDIRS}: FRC + cd $@; pwd; $(MAKE) $(TARGET) + +lint: + +.WAIT: + +FRC: diff --git a/usr/src/lib/libdns_sd/java/com/apple/Makefile.com b/usr/src/lib/libdns_sd/java/com/apple/Makefile.com new file mode 100644 index 0000000000..e699ff14a4 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/Makefile.com @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include $(SRC)/lib/Makefile.lib + +ROOTDNSSDJAVAHOME = $(ROOT)/usr/share/lib/java + +.KEEP_STATE: diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java new file mode 100644 index 0000000000..689cb8ba57 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java @@ -0,0 +1,51 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: BaseListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** A base class for DNSSD listeners. */ + +public interface BaseListener +{ + /** Called to report DNSSD operation failures.<P> + + @param service + The service that encountered the failure. + <P> + @param errorCode + Indicates the failure that occurred. See {@link DNSSDException} for error codes. + */ + void operationFailed( DNSSDService service, int errorCode); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java new file mode 100644 index 0000000000..0ae9b938ba --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java @@ -0,0 +1,88 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: BrowseListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** A listener that receives results from {@link DNSSD#browse}. */ + +public interface BrowseListener extends BaseListener +{ + /** Called to report discovered services.<P> + + @param browser + The active browse service. + <P> + @param flags + Possible values are DNSSD.MORE_COMING. + <P> + @param ifIndex + The interface on which the service is advertised. This index should be passed + to {@link DNSSD#resolve} when resolving the service. + <P> + @param serviceName + The service name discovered. + <P> + @param regType + The registration type, as passed in to DNSSD.browse(). + <P> + @param domain + The domain in which the service was discovered. + */ + void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain); + + /** Called to report services which have been deregistered.<P> + + @param browser + The active browse service. + <P> + @param flags + Possible values are DNSSD.MORE_COMING. + <P> + @param ifIndex + The interface on which the service is advertised. + <P> + @param serviceName + The service name which has deregistered. + <P> + @param regType + The registration type, as passed in to DNSSD.browse(). + <P> + @param domain + The domain in which the service was discovered. + */ + void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java new file mode 100644 index 0000000000..6caf3a7791 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java @@ -0,0 +1,68 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSRecord.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/12/11 03:00:59 rpantos +<rdar://problem/3907498> Java DNSRecord API should be cleaned up + +Revision 1.1 2004/04/30 16:32:34 rpantos +First checked in. + + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** + Reference to a record returned by {@link DNSSDRegistration#addRecord}.<P> + + Note: client is responsible for serializing access to these objects if + they are shared between concurrent threads. +*/ + +public interface DNSRecord +{ + /** Update a registered resource record.<P> + The record must either be the primary txt record of a service registered via DNSSD.register(), + or a record added to a registered service via addRecord().<P> + + @param flags + Currently unused, reserved for future use. + <P> + @param rData + The new rdata to be contained in the updated resource record. + <P> + @param ttl + The time to live of the updated resource record, in seconds. + */ + void update( int flags, byte[] rData, int ttl) + throws DNSSDException; + + /** Remove a registered resource record.<P> + */ + void remove() + throws DNSSDException; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java new file mode 100644 index 0000000000..9841c9492b --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java @@ -0,0 +1,897 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSSD.java,v $ +Revision 1.11 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.10 2006/06/20 23:05:55 rpantos +<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent + +Revision 1.9 2005/10/26 01:52:24 cheshire +<rdar://problem/4316286> Race condition in Java code (doesn't work at all on Linux) + +Revision 1.8 2005/07/11 01:55:21 cheshire +<rdar://problem/4175511> Race condition in Java API + +Revision 1.7 2005/07/05 13:01:52 cheshire +<rdar://problem/4169791> If mDNSResponder daemon is stopped, Java API spins, burning CPU time + +Revision 1.6 2005/07/05 00:02:25 cheshire +Add missing comma + +Revision 1.5 2005/07/04 21:13:47 cheshire +Add missing error message strings + +Revision 1.4 2004/12/11 03:00:59 rpantos +<rdar://problem/3907498> Java DNSRecord API should be cleaned up + +Revision 1.3 2004/11/12 03:23:08 rpantos +rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex. + +Revision 1.2 2004/05/20 17:43:18 cheshire +Fix invalid UTF-8 characters in file + +Revision 1.1 2004/04/30 16:32:34 rpantos +First checked in. + + + This file declares and implements DNSSD, the central Java factory class + for doing DNS Service Discovery. It includes the mostly-abstract public + interface, as well as the Apple* implementation subclasses. + */ + +/* + * ident "%Z%%M% %I% %E% SMI" + */ + +package com.apple.dnssd; + + +/** + DNSSD provides access to DNS Service Discovery features of ZeroConf networking.<P> + + It is a factory class that is used to invoke registration and discovery-related + operations. Most operations are non-blocking; clients are called back through an interface + with the result of an operation. Callbacks are made from a separate worker thread.<P> + + For example, in this program<P> + <PRE><CODE> + class MyClient implements BrowseListener { + void lookForWebServers() { + myBrowser = DNSSD.browse("_http_.tcp", this); + } + + public void serviceFound(DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) {} + ... + }</CODE></PRE> + <CODE>MyClient.serviceFound()</CODE> would be called for every HTTP server discovered in the + default browse domain(s). +*/ + +abstract public class DNSSD +{ + /** Flag indicates to a {@link BrowseListener} that another result is + queued. Applications should not update their UI to display browse + results if the MORE_COMING flag is set; they will be called at least once + more with the flag clear. + */ + public static final int MORE_COMING = ( 1 << 0 ); + + /** If flag is set in a {@link DomainListener} callback, indicates that the result is the default domain. */ + public static final int DEFAULT = ( 1 << 2 ); + + /** If flag is set, a name conflict will trigger an exception when registering non-shared records.<P> + A name must be explicitly specified when registering a service if this bit is set + (i.e. the default name may not not be used). + */ + public static final int NO_AUTO_RENAME = ( 1 << 3 ); + + /** If flag is set, allow multiple records with this name on the network (e.g. PTR records) + when registering individual records on a {@link DNSSDRegistration}. + */ + public static final int SHARED = ( 1 << 4 ); + + /** If flag is set, records with this name must be unique on the network (e.g. SRV records). */ + public static final int UNIQUE = ( 1 << 5 ); + + /** Set flag when calling enumerateDomains() to restrict results to domains recommended for browsing. */ + public static final int BROWSE_DOMAINS = ( 1 << 6 ); + /** Set flag when calling enumerateDomains() to restrict results to domains recommended for registration. */ + public static final int REGISTRATION_DOMAINS = ( 1 << 7 ); + + /** Maximum length, in bytes, of a domain name represented as an escaped C-String. */ + public static final int MAX_DOMAIN_NAME = 1005; + + /** Pass for ifIndex to specify all available interfaces. */ + public static final int ALL_INTERFACES = 0; + + /** Pass for ifIndex to specify the localhost interface. */ + public static final int LOCALHOST_ONLY = -1; + + /** Browse for instances of a service.<P> + + Note: browsing consumes network bandwidth. Call {@link DNSSDService#stop} when you have finished browsing.<P> + + @param flags + Currently ignored, reserved for future use. + <P> + @param ifIndex + If non-zero, specifies the interface on which to browse for services + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Most applications will pass 0 to browse on all available + interfaces. Pass -1 to only browse for services provided on the local host. + <P> + @param regType + The registration type being browsed for followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + <P> + @param domain + If non-null, specifies the domain on which to browse for services. + Most applications will not specify a domain, instead browsing on the + default domain(s). + <P> + @param listener + This object will get called when instances of the service are discovered (or disappear). + <P> + @return A {@link DNSSDService} that represents the active browse operation. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDService browse( int flags, int ifIndex, String regType, String domain, BrowseListener listener) + throws DNSSDException + { return getInstance()._makeBrowser( flags, ifIndex, regType, domain, listener); } + + /** Browse for instances of a service. Use default flags, ifIndex and domain.<P> + + @param regType + The registration type being browsed for followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + <P> + @param listener + This object will get called when instances of the service are discovered (or disappear). + <P> + @return A {@link DNSSDService} that represents the active browse operation. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDService browse( String regType, BrowseListener listener) + throws DNSSDException + { return browse( 0, 0, regType, "", listener); } + + /** Resolve a service name discovered via browse() to a target host name, port number, and txt record.<P> + + Note: Applications should NOT use resolve() solely for txt record monitoring - use + queryRecord() instead, as it is more efficient for this task.<P> + + Note: When the desired results have been returned, the client MUST terminate the resolve by + calling {@link DNSSDService#stop}.<P> + + Note: resolve() behaves correctly for typical services that have a single SRV record and + a single TXT record (the TXT record may be empty.) To resolve non-standard services with + multiple SRV or TXT records, use queryRecord().<P> + + @param flags + Currently ignored, reserved for future use. + <P> + @param ifIndex + The interface on which to resolve the service. The client should + pass the interface on which the serviceName was discovered (i.e. + the ifIndex passed to the serviceFound() callback) + or 0 to resolve the named service on all available interfaces. + <P> + @param serviceName + The servicename to be resolved. + <P> + @param regType + The registration type being resolved followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + <P> + @param domain + The domain on which the service is registered, i.e. the domain passed + to the serviceFound() callback. + <P> + @param listener + This object will get called when the service is resolved. + <P> + @return A {@link DNSSDService} that represents the active resolve operation. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDService resolve( int flags, int ifIndex, String serviceName, String regType, + String domain, ResolveListener listener) + throws DNSSDException + { return getInstance()._resolve( flags, ifIndex, serviceName, regType, domain, listener); } + + /** Register a service, to be discovered via browse() and resolve() calls.<P> + @param flags + Possible values are: NO_AUTO_RENAME. + <P> + @param ifIndex + If non-zero, specifies the interface on which to register the service + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Most applications will pass 0 to register on all + available interfaces. Pass -1 to register a service only on the local + machine (service will not be visible to remote hosts). + <P> + @param serviceName + If non-null, specifies the service name to be registered. + Applications need not specify a name, in which case the + computer name is used (this name is communicated to the client via + the serviceRegistered() callback). + <P> + @param regType + The registration type being registered followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + <P> + @param domain + If non-null, specifies the domain on which to advertise the service. + Most applications will not specify a domain, instead automatically + registering in the default domain(s). + <P> + @param host + If non-null, specifies the SRV target host name. Most applications + will not specify a host, instead automatically using the machine's + default host name(s). Note that specifying a non-null host does NOT + create an address record for that host - the application is responsible + for ensuring that the appropriate address record exists, or creating it + via {@link DNSSDRegistration#addRecord}. + <P> + @param port + The port on which the service accepts connections. Pass 0 for a + "placeholder" service (i.e. a service that will not be discovered by + browsing, but will cause a name conflict if another client tries to + register that same name.) Most clients will not use placeholder services. + <P> + @param txtRecord + The txt record rdata. May be null. Note that a non-null txtRecord + MUST be a properly formatted DNS TXT record, i.e. <length byte> <data> + <length byte> <data> ... + <P> + @param listener + This object will get called when the service is registered. + <P> + @return A {@link DNSSDRegistration} that controls the active registration. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDRegistration register( int flags, int ifIndex, String serviceName, String regType, + String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener) + throws DNSSDException + { return getInstance()._register( flags, ifIndex, serviceName, regType, domain, host, port, txtRecord, listener); } + + /** Register a service, to be discovered via browse() and resolve() calls. Use default flags, ifIndex, domain, host and txtRecord.<P> + @param serviceName + If non-null, specifies the service name to be registered. + Applications need not specify a name, in which case the + computer name is used (this name is communicated to the client via + the serviceRegistered() callback). + <P> + @param regType + The registration type being registered followed by the protocol, separated by a + dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp". + <P> + @param port + The port on which the service accepts connections. Pass 0 for a + "placeholder" service (i.e. a service that will not be discovered by + browsing, but will cause a name conflict if another client tries to + register that same name.) Most clients will not use placeholder services. + <P> + @param listener + This object will get called when the service is registered. + <P> + @return A {@link DNSSDRegistration} that controls the active registration. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDRegistration register( String serviceName, String regType, int port, RegisterListener listener) + throws DNSSDException + { return register( 0, 0, serviceName, regType, null, null, port, null, listener); } + + /** Create a {@link DNSSDRecordRegistrar} allowing efficient registration of + multiple individual records.<P> + <P> + @return A {@link DNSSDRecordRegistrar} that can be used to register records. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDRecordRegistrar createRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException + { return getInstance()._createRecordRegistrar( listener); } + + /** Query for an arbitrary DNS record.<P> + @param flags + Possible values are: MORE_COMING. + <P> + @param ifIndex + If non-zero, specifies the interface on which to issue the query + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Passing 0 causes the name to be queried for on all + interfaces. Passing -1 causes the name to be queried for only on the + local host. + <P> + @param serviceName + The full domain name of the resource record to be queried for. + <P> + @param rrtype + The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc) + as defined in nameser.h. + <P> + @param rrclass + The class of the resource record, as defined in nameser.h + (usually 1 for the Internet class). + <P> + @param listener + This object will get called when the query completes. + <P> + @return A {@link DNSSDService} that controls the active query. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDService queryRecord( int flags, int ifIndex, String serviceName, int rrtype, + int rrclass, QueryListener listener) + throws DNSSDException + { return getInstance()._queryRecord( flags, ifIndex, serviceName, rrtype, rrclass, listener); } + + /** Asynchronously enumerate domains available for browsing and registration.<P> + + Currently, the only domain returned is "local.", but other domains will be returned in future.<P> + + The enumeration MUST be cancelled by calling {@link DNSSDService#stop} when no more domains + are to be found.<P> + @param flags + Possible values are: BROWSE_DOMAINS, REGISTRATION_DOMAINS. + <P> + @param ifIndex + If non-zero, specifies the interface on which to look for domains. + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Most applications will pass 0 to enumerate domains on + all interfaces. + <P> + @param listener + This object will get called when domains are found. + <P> + @return A {@link DNSSDService} that controls the active enumeration. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static DNSSDService enumerateDomains( int flags, int ifIndex, DomainListener listener) + throws DNSSDException + { return getInstance()._enumerateDomains( flags, ifIndex, listener); } + + /** Concatenate a three-part domain name (as provided to the listeners) into a + properly-escaped full domain name. Note that strings passed to listeners are + ALREADY ESCAPED where necessary.<P> + @param serviceName + The service name - any dots or slashes must NOT be escaped. + May be null (to construct a PTR record name, e.g. "_ftp._tcp.apple.com"). + <P> + @param regType + The registration type followed by the protocol, separated by a dot (e.g. "_ftp._tcp"). + <P> + @param domain + The domain name, e.g. "apple.com". Any literal dots or backslashes must be escaped. + <P> + @return The full domain name. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static String constructFullName( String serviceName, String regType, String domain) + throws DNSSDException + { return getInstance()._constructFullName( serviceName, regType, domain); } + + /** Instruct the daemon to verify the validity of a resource record that appears to + be out of date. (e.g. because tcp connection to a service's target failed.) <P> + + Causes the record to be flushed from the daemon's cache (as well as all other + daemons' caches on the network) if the record is determined to be invalid.<P> + @param flags + Currently unused, reserved for future use. + <P> + @param ifIndex + If non-zero, specifies the interface on which to reconfirm the record + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Passing 0 causes the name to be reconfirmed on all + interfaces. Passing -1 causes the name to be reconfirmed only on the + local host. + <P> + @param fullName + The resource record's full domain name. + <P> + @param rrtype + The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h. + <P> + @param rrclass + The class of the resource record, as defined in nameser.h (usually 1). + <P> + @param rdata + The raw rdata of the resource record. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static void reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + int rrclass, byte[] rdata) + { getInstance()._reconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata); } + + /** Return the canonical name of a particular interface index.<P> + @param ifIndex + A valid interface index. Must not be ALL_INTERFACES. + <P> + @return The name of the interface, which should match java.net.NetworkInterface.getName(). + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static String getNameForIfIndex( int ifIndex) + { return getInstance()._getNameForIfIndex( ifIndex); } + + /** Return the index of a named interface.<P> + @param ifName + A valid interface name. An example is java.net.NetworkInterface.getName(). + <P> + @return The interface index. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public static int getIfIndexForName( String ifName) + { return getInstance()._getIfIndexForName( ifName); } + + protected DNSSD() {} // prevent direct instantiation + + /** Return the single instance of DNSSD. */ + static protected final DNSSD getInstance() + { + SecurityManager sm = System.getSecurityManager(); + if ( sm != null) + sm.checkPermission( new RuntimePermission( "getDNSSDInstance")); + return fInstance; + } + + abstract protected DNSSDService _makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener listener) + throws DNSSDException; + + abstract protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType, + String domain, ResolveListener listener) + throws DNSSDException; + + abstract protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType, + String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener) + throws DNSSDException; + + abstract protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException; + + abstract protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype, + int rrclass, QueryListener listener) + throws DNSSDException; + + abstract protected DNSSDService _enumerateDomains( int flags, int ifIndex, DomainListener listener) + throws DNSSDException; + + abstract protected String _constructFullName( String serviceName, String regType, String domain) + throws DNSSDException; + + abstract protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + int rrclass, byte[] rdata); + + abstract protected String _getNameForIfIndex( int ifIndex); + + abstract protected int _getIfIndexForName( String ifName); + + protected static DNSSD fInstance; + + static + { + try + { + String name = System.getProperty( "com.apple.dnssd.DNSSD" ); + if( name == null ) + name = "com.apple.dnssd.AppleDNSSD"; // Fall back to Apple-provided class. + fInstance = (DNSSD) Class.forName( name ).newInstance(); + } + catch( Exception e ) + { + throw new InternalError( "cannot instantiate DNSSD" + e ); + } + } +} + + +// Concrete implementation of DNSSDException +class AppleDNSSDException extends DNSSDException +{ + public AppleDNSSDException( int errorCode) { fErrorCode = errorCode; } + + public int getErrorCode() { return fErrorCode; } + + public String getMessage() + { + final String kMessages[] = { // should probably be put into a resource or something + "UNKNOWN", + "NO_SUCH_NAME", + "NO_MEMORY", + "BAD_PARAM", + "BAD_REFERENCE", + "BAD_STATE", + "BAD_FLAGS", + "UNSUPPORTED", + "NOT_INITIALIZED", + "NO_CACHE", + "ALREADY_REGISTERED", + "NAME_CONFLICT", + "INVALID", + "FIREWALL", + "INCOMPATIBLE", + "BAD_INTERFACE_INDEX", + "REFUSED", + "NOSUCHRECORD", + "NOAUTH", + "NOSUCHKEY", + "NATTRAVERSAL", + "DOUBLENAT", + "BADTIME", + "BADSIG", + "BADKEY", + "TRANSIENT" + }; + + if ( fErrorCode <= UNKNOWN && fErrorCode > ( UNKNOWN - kMessages.length)) + { + return "DNS-SD Error " + String.valueOf( fErrorCode) + ": " + kMessages[ UNKNOWN - fErrorCode]; + } + else + return super.getMessage() + "(" + String.valueOf( fErrorCode) + ")"; + } + + protected int fErrorCode; +} + +// The concrete, default implementation. +class AppleDNSSD extends DNSSD +{ + static + { + System.loadLibrary( "jdns_sd"); + + int libInitResult = InitLibrary( 1); + + if ( libInitResult != DNSSDException.NO_ERROR) + throw new InternalError( "cannot instantiate DNSSD: " + new AppleDNSSDException( libInitResult).getMessage()); + } + + static public boolean hasAutoCallbacks; // Set by InitLibrary() to value of AUTO_CALLBACKS + + protected DNSSDService _makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client) + throws DNSSDException + { + return new AppleBrowser( flags, ifIndex, regType, domain, client); + } + + protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType, + String domain, ResolveListener client) + throws DNSSDException + { + return new AppleResolver( flags, ifIndex, serviceName, regType, domain, client); + } + + protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType, + String domain, String host, int port, TXTRecord txtRecord, RegisterListener client) + throws DNSSDException + { + return new AppleRegistration( flags, ifIndex, serviceName, regType, domain, host, port, + ( txtRecord != null) ? txtRecord.getRawBytes() : null, client); + } + + protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException + { + return new AppleRecordRegistrar( listener); + } + + protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype, + int rrclass, QueryListener client) + throws DNSSDException + { + return new AppleQuery( flags, ifIndex, serviceName, rrtype, rrclass, client); + } + + protected DNSSDService _enumerateDomains( int flags, int ifIndex, DomainListener listener) + throws DNSSDException + { + return new AppleDomainEnum( flags, ifIndex, listener); + } + + protected String _constructFullName( String serviceName, String regType, String domain) + throws DNSSDException + { + String[] responseHolder = new String[1]; // lame maneuver to get around Java's lack of reference parameters + + int rc = ConstructName( serviceName, regType, domain, responseHolder); + if ( rc != 0) + throw new AppleDNSSDException( rc); + + return responseHolder[0]; + } + + protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + int rrclass, byte[] rdata) + { + ReconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata); + } + + protected String _getNameForIfIndex( int ifIndex) + { + return GetNameForIfIndex( ifIndex); + } + + protected int _getIfIndexForName( String ifName) + { + return GetIfIndexForName( ifName); + } + + + protected native int ConstructName( String serviceName, String regType, String domain, String[] pOut); + + protected native void ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype, + int rrclass, byte[] rdata); + + protected native String GetNameForIfIndex( int ifIndex); + + protected native int GetIfIndexForName( String ifName); + + protected static native int InitLibrary( int callerVersion); +} + +class AppleService implements DNSSDService, Runnable +{ + public AppleService(BaseListener listener) { fNativeContext = 0; fListener = listener; } + + public void stop() { this.HaltOperation(); } + + /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */ + protected native int BlockForData(); + + /* Call ProcessResults when data appears on socket descriptor. */ + protected native int ProcessResults(); + + protected synchronized native void HaltOperation(); + + protected void ThrowOnErr( int rc) throws DNSSDException + { + if ( rc != 0) + throw new AppleDNSSDException( rc); + } + + protected long /* warning */ fNativeContext; // Private storage for native side + + public void run() + { + while ( true ) + { + // Note: We want to allow our DNS-SD operation to be stopped from other threads, so we have to + // block waiting for data *outside* the synchronized section. Because we're doing this unsynchronized + // we have to write some careful code. Suppose our DNS-SD operation is stopped from some other thread, + // and then immediately afterwards that thread (or some third, unrelated thread) starts a new DNS-SD + // operation. The Unix kernel always allocates the lowest available file descriptor to a new socket, + // so the same file descriptor is highly likely to be reused for the new operation, and if our old + // stale ServiceThread accidentally consumes bytes off that new socket we'll get really messed up. + // To guard against that, before calling ProcessResults we check to ensure that our + // fNativeContext has not been deleted, which is a telltale sign that our operation was stopped. + // After calling ProcessResults we check again, because it's extremely common for callback + // functions to stop their own operation and start others. For example, a resolveListener callback + // may well stop the resolve and then start a QueryRecord call to monitor the TXT record. + // + // The remaining risk is that between our checking fNativeContext and calling ProcessResults(), + // some other thread could stop the operation and start a new one using same file descriptor, and + // we wouldn't know. To prevent this, the AppleService object's HaltOperation() routine is declared + // synchronized and we perform our checks synchronized on the AppleService object, which ensures + // that HaltOperation() can't execute while we're doing it. Because Java locks are re-entrant this + // locking DOESN'T prevent the callback routine from stopping its own operation, but DOES prevent + // any other thread from stopping it until after the callback has completed and returned to us here. + + int result = BlockForData(); + synchronized (this) + { + if (fNativeContext == 0) break; // Some other thread stopped our DNSSD operation; time to terminate this thread + if (result == 0) continue; // If BlockForData() said there was no data, go back and block again + result = ProcessResults(); + if (fNativeContext == 0) break; // Event listener stopped its own DNSSD operation; terminate this thread + if (result != 0) { fListener.operationFailed(this, result); break; } // If error, notify listener + } + } + } + + protected BaseListener fListener; +} + + +class AppleBrowser extends AppleService +{ + public AppleBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client) + throws DNSSDException + { + super(client); + this.ThrowOnErr( this.CreateBrowser( flags, ifIndex, regType, domain)); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int CreateBrowser( int flags, int ifIndex, String regType, String domain); +} + +class AppleResolver extends AppleService +{ + public AppleResolver( int flags, int ifIndex, String serviceName, String regType, + String domain, ResolveListener client) + throws DNSSDException + { + super(client); + this.ThrowOnErr( this.CreateResolver( flags, ifIndex, serviceName, regType, domain)); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int CreateResolver( int flags, int ifIndex, String serviceName, String regType, + String domain); +} + +// An AppleDNSRecord is a simple wrapper around a dns_sd DNSRecord. +class AppleDNSRecord implements DNSRecord +{ + public AppleDNSRecord( AppleService owner) + { + fOwner = owner; + fRecord = 0; // record always starts out empty + } + + public void update( int flags, byte[] rData, int ttl) + throws DNSSDException + { + this.ThrowOnErr( this.Update( flags, rData, ttl)); + } + + public void remove() + throws DNSSDException + { + this.ThrowOnErr( this.Remove()); + } + + protected long fRecord; // Really a DNSRecord; sizeof(int) == sizeof(void*) ? + protected AppleService fOwner; + + protected void ThrowOnErr( int rc) throws DNSSDException + { + if ( rc != 0) + throw new AppleDNSSDException( rc); + } + + protected native int Update( int flags, byte[] rData, int ttl); + + protected native int Remove(); +} + +class AppleRegistration extends AppleService implements DNSSDRegistration +{ + public AppleRegistration( int flags, int ifIndex, String serviceName, String regType, String domain, + String host, int port, byte[] txtRecord, RegisterListener client) + throws DNSSDException + { + super(client); + this.ThrowOnErr( this.BeginRegister( ifIndex, flags, serviceName, regType, domain, host, port, txtRecord)); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + public DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl) + throws DNSSDException + { + AppleDNSRecord newRecord = new AppleDNSRecord( this); + + this.ThrowOnErr( this.AddRecord( flags, rrType, rData, ttl, newRecord)); + return newRecord; + } + + public DNSRecord getTXTRecord() + throws DNSSDException + { + return new AppleDNSRecord( this); // A record with ref 0 is understood to be primary TXT record + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int BeginRegister( int ifIndex, int flags, String serviceName, String regType, + String domain, String host, int port, byte[] txtRecord); + + // Sets fNativeContext. Returns non-zero on error. + protected native int AddRecord( int flags, int rrType, byte[] rData, int ttl, AppleDNSRecord destObj); +} + +class AppleRecordRegistrar extends AppleService implements DNSSDRecordRegistrar +{ + public AppleRecordRegistrar( RegisterRecordListener listener) + throws DNSSDException + { + super(listener); + this.ThrowOnErr( this.CreateConnection()); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype, + int rrclass, byte[] rdata, int ttl) + throws DNSSDException + { + AppleDNSRecord newRecord = new AppleDNSRecord( this); + + this.ThrowOnErr( this.RegisterRecord( flags, ifIndex, fullname, rrtype, rrclass, rdata, ttl, newRecord)); + return newRecord; + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int CreateConnection(); + + // Sets fNativeContext. Returns non-zero on error. + protected native int RegisterRecord( int flags, int ifIndex, String fullname, int rrtype, + int rrclass, byte[] rdata, int ttl, AppleDNSRecord destObj); +} + +class AppleQuery extends AppleService +{ + public AppleQuery( int flags, int ifIndex, String serviceName, int rrtype, + int rrclass, QueryListener client) + throws DNSSDException + { + super(client); + this.ThrowOnErr( this.CreateQuery( flags, ifIndex, serviceName, rrtype, rrclass)); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int CreateQuery( int flags, int ifIndex, String serviceName, int rrtype, int rrclass); +} + +class AppleDomainEnum extends AppleService +{ + public AppleDomainEnum( int flags, int ifIndex, DomainListener client) + throws DNSSDException + { + super(client); + this.ThrowOnErr( this.BeginEnum( flags, ifIndex)); + if ( !AppleDNSSD.hasAutoCallbacks) + new Thread(this).start(); + } + + // Sets fNativeContext. Returns non-zero on error. + protected native int BeginEnum( int flags, int ifIndex); +} + + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java new file mode 100644 index 0000000000..618128f31f --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java @@ -0,0 +1,76 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSSDException.java,v $ +Revision 1.4 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.3 2005/07/10 22:19:01 cheshire +Add missing error codes to list of public static final ints + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + +*/ + +package com.apple.dnssd; + + +/** + Used to report various DNS-SD-related error conditions. +*/ + +abstract public class DNSSDException extends Exception +{ + public static final int NO_ERROR = 0; + public static final int UNKNOWN = -65537; + public static final int NO_SUCH_NAME = -65538; + public static final int NO_MEMORY = -65539; + public static final int BAD_PARAM = -65540; + public static final int BAD_REFERENCE = -65541; + public static final int BAD_STATE = -65542; + public static final int BAD_FLAGS = -65543; + public static final int UNSUPPORTED = -65544; + public static final int NOT_INITIALIZED = -65545; + public static final int NO_CACHE = -65546; + public static final int ALREADY_REGISTERED = -65547; + public static final int NAME_CONFLICT = -65548; + public static final int INVALID = -65549; + public static final int FIREWALL = -65550; + public static final int INCOMPATIBLE = -65551; + public static final int BAD_INTERFACE_INDEX = -65552; + public static final int REFUSED = -65553; + public static final int NOSUCHRECORD = -65554; + public static final int NOAUTH = -65555; + public static final int NOSUCHKEY = -65556; + public static final int NATTRAVERSAL = -65557; + public static final int DOUBLENAT = -65558; + public static final int BADTIME = -65559; + public static final int BADSIG = -65560; + public static final int BADKEY = -65561; + public static final int TRANSIENT = -65562; + + /** Returns the sub-code that identifies the particular error. */ + abstract public int getErrorCode(); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java new file mode 100644 index 0000000000..366d83476b --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java @@ -0,0 +1,79 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + This file declares the public interface to DNSSDRecordRegistrar, a DNSSDService + subclass that allows efficient registration of multiple individual records. + + Change History (most recent first): + +$Log: DNSSDRecordRegistrar.java,v $ +Revision 1.2 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.1 2006/06/20 23:00:12 rpantos +<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** An object for registering records, created by {@link DNSSD#createRecordRegistrar}. */ + +public interface DNSSDRecordRegistrar extends DNSSDService +{ + /** Register an independent {@link DNSRecord}.<P> + @param flags + Possible values are SHARED or UNIQUE (see flag type definitions for details). + <P> + @param ifIndex + If non-zero, specifies the interface on which to register the record + (the index for a given interface is determined via the if_nametoindex() + family of calls.) Passing 0 causes the record to be registered on all interfaces. + <P> + @param fullname + The full domain name of the resource record. + <P> + @param rrtype + The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc) + as defined in nameser.h. + <P> + @param rrclass + The class of the resource record, as defined in nameser.h + (usually 1 for the Internet class). + <P> + @param rData + The new rdata as it is to appear in the DNS record. + <P> + @param ttl + The time to live of the resource record, in seconds. Pass 0 to use a default value. + <P> + @param listener + This object will get called when the service is registered. + <P> + @return A {@link DNSSDService} that can be used to abort the record registration. + + @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>. + @see RuntimePermission + */ + public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype, + int rrclass, byte[] rdata, int ttl) + throws DNSSDException; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java new file mode 100644 index 0000000000..4ae38036ed --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java @@ -0,0 +1,77 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSSDRegistration.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/12/11 03:01:00 rpantos +<rdar://problem/3907498> Java DNSRecord API should be cleaned up + +Revision 1.1 2004/04/30 16:32:34 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + This file declares the public interface to DNSSDRegistration, a DNSSDService + subclass that allows a client to control a service registration. + */ + + +package com.apple.dnssd; + + +/** A tracking object for a registration created by {@link DNSSD#register}. */ + +public interface DNSSDRegistration extends DNSSDService +{ + /** Get a reference to the primary TXT record of a registered service.<P> + The record can be updated by sending it an update() message.<P> + + <P> + @return A {@link DNSRecord}. + If {@link DNSSDRegistration#stop} is called, the DNSRecord is also + invalidated and may not be used further. + */ + DNSRecord getTXTRecord() + throws DNSSDException; + + /** Add a record to a registered service.<P> + The name of the record will be the same as the registered service's name.<P> + The record can be updated or deregistered by sending it an update() or remove() message.<P> + + @param flags + Currently unused, reserved for future use. + <P> + @param rrType + The type of the record (e.g. TXT, SRV, etc), as defined in nameser.h. + <P> + @param rData + The raw rdata to be contained in the added resource record. + <P> + @param ttl + The time to live of the resource record, in seconds. + <P> + @return A {@link DNSRecord} that may be passed to updateRecord() or removeRecord(). + If {@link DNSSDRegistration#stop} is called, the DNSRecord is also + invalidated and may not be used further. + */ + DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl) + throws DNSSDException; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java new file mode 100644 index 0000000000..93cc5fd2c9 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java @@ -0,0 +1,52 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DNSSDService.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:32:34 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + +/** A tracking object for a service created by {@link DNSSD}. */ + +public interface DNSSDService +{ + /** + Halt the active operation and free resources associated with the DNSSDService.<P> + + Any services or records registered with this DNSSDService will be deregistered. Any + Browse, Resolve, or Query operations associated with this reference will be terminated.<P> + + Note: if the service was initialized with DNSSD.register(), and an extra resource record was + added to the service via {@link DNSSDRegistration#addRecord}, the DNSRecord so created + is invalidated when this method is called - the DNSRecord may not be used afterward. + */ + void stop(); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java new file mode 100644 index 0000000000..276d5b539a --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java @@ -0,0 +1,75 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: DomainListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** + A listener that receives results from {@link DNSSD#enumerateDomains}. +*/ + +public interface DomainListener extends BaseListener +{ + /** Called to report discovered domains.<P> + + @param domainEnum + The active domain enumerator. + @param flags + Possible values are: DNSSD.MORE_COMING, DNSSD.DEFAULT + <P> + @param ifIndex + Specifies the interface on which the domain exists. (The index for a given + interface is determined via the if_nametoindex() family of calls.) + <P> + @param domain + The name of the domain. + */ + void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain); + + /** Called to report that a domain has disappeared.<P> + + @param domainEnum + The active domain enumerator. + @param flags + Possible values are: DNSSD.MORE_COMING, DNSSD.DEFAULT + <P> + @param ifIndex + Specifies the interface on which the domain exists. (The index for a given + interface is determined via the if_nametoindex() family of calls.) + <P> + @param domain + The name of the domain. + */ + void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/Makefile b/usr/src/lib/libdns_sd/java/com/apple/dnssd/Makefile new file mode 100644 index 0000000000..e25208d774 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/Makefile @@ -0,0 +1,150 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +DNSSD_PKG = com.apple.dnssd + +TOP = $(SRC)/lib/libdns_sd/java +JAVASRCDIR = $(TOP)/com/apple/dnssd +CLASSPATH = $(TOP):com/apple/dnssd + +JAVAFLAGS += -source 1.4 -target 1.4 +SOURCE:sh = ls *.java +CLASSES = $(SOURCE:java=class) +JNIH = DNSSD.java.h +JAR_FILE = dnssd.jar + +DOCDIR = $(JAVASRCDIR)/docs +DOCAPIDIR = $(JAVASRCDIR)/docs/api +DOCDESTDIR = $(ROOTDNSSDJAVAHOME)/javadoc/dnssd +DOCAPIDESTDIR = $(DOCDESTDIR)/api +DOCEXAMPLESDESTDIR = $(DOCDESTDIR)/examples + +EXAMPLESDIR = $(JAVASRCDIR)/docs/examples +EXAMPLESSRC = $(JAVASRCDIR)/docs/examples/src +SIMPLECHATOBJ = $(EXAMPLESDIR)/SwingBrowseListener.class \ + $(EXAMPLESDIR)/SwingQueryListener.class \ + $(EXAMPLESDIR)/SimpleChat.class +BROWSERAPPOBJ = $(EXAMPLESDIR)/SwingResolveListener.class \ + $(EXAMPLESDIR)/SwingDomainListener.class \ + $(EXAMPLESDIR)/BrowserApp.class +EXAMPLEOBJS = $(SIMPLECHATOBJ) $(BROWSERAPPOBJ) +EXAMPLEJARS = SimpleChat.jar BrowserApp.jar + +INSTALL_JAR = $(ROOTDNSSDJAVAHOME)/$(JAR_FILE) +INSTALL_EXAMPLEJARS = $(DOCEXAMPLESDESTDIR)/SimpleChat.jar \ + $(DOCEXAMPLESDESTDIR)/BrowserApp.jar + +CLEAN_FILES = *.class $(JNIH) *.jar $(EXAMPLESDIR)/*.class $(EXAMPLESDIR)/*.jar + +DEFINES= + +INCLUDES= -I${JAVA_HOME}/include \ + -I${JAVA_HOME}/include/solaris + +.KEEP_STATE: + +all: $(JNIH) $(CLASSES) $(EXAMPLEOBJS) doc + +install: $(CLASSES) $(ROOTDNSSDJAVAHOME) \ + $(DOCEXAMPLESDESTDIR) $(DOCEXAMPLESSRCDESTDIR) \ + $(JAR_FILE) $(INSTALL_JAR) $(JNIH) \ + $(EXAMPLEJARS) $(INSTALL_EXAMPLEJARS) \ + install_doc + +$(JNIH): $(CLASSES) + class="com.apple.dnssd.AppleDNSSD \ + com.apple.dnssd.AppleBrowser \ + com.apple.dnssd.AppleResolver \ + com.apple.dnssd.AppleRegistration \ + com.apple.dnssd.AppleQuery \ + com.apple.dnssd.AppleDomainEnum \ + com.apple.dnssd.AppleService"; \ + $(JAVAH) -classpath $(CLASSPATH) -jni -o $(JNIH) $$class + +clean clobber: + $(RM) $(CLEAN_FILES) + +$(JAR_FILE): $(CLASSES) + cd $(TOP); \ + $(JAR) -cvf $(TOP)/com/apple/dnssd/$(JAR_FILE) com/apple/dnssd/*.class + +$(EXAMPLESDIR)/%.class: $(EXAMPLESSRC)/%.java + $(JAVAC) $(JAVAFLAGS) $< -classpath $(CLASSPATH):$(EXAMPLESDIR) -d $(EXAMPLESDIR) + +SIMPLECHATMAN = $(EXAMPLESSRC)/SimpleChat.manifest + +SimpleChat.jar: $(SIMPLECHATOBJ) $(SIMPLECHATMAN) + cd $(EXAMPLESDIR); $(JAR) -cvfm $@ $(SIMPLECHATMAN) \ + SwingBrowseListener.class SwingQueryListener.class \ + SimpleChat.class SimpleChat\$$1.class \ + ListenerThread.class TargetListElem.class \ + TargetListModel.class src/SimpleChat.java \ + src/SimpleChat.manifest src/SwingBrowseListener.java \ + src/SwingQueryListener.java + +BROWSERAPPMAN = $(EXAMPLESSRC)/BrowserApp.manifest + +BrowserApp.jar: $(BROWSERAPPOBJ) $(BROWSERAPPMAN) + cd $(EXAMPLESDIR); $(JAR) -cvfm $@ $(BROWSERAPPMAN) \ + BrowserApp\$$1.class BrowserApp.class \ + BrowserListModel\$$BrowserListElem.class \ + BrowserListModel.class DomainListModel.class \ + ServicesBrowserListModel.class \ + SwingResolveListener.class SwingDomainListener.class \ + src/BrowserApp.java src/SwingResolveListener.java \ + src/SwingDomainListener.java src/BrowserApp.manifest + +$(ROOTDNSSDJAVAHOME): + $(INS.dir) + +$(ROOTDNSSDJAVAHOME)/%: % + $(INS.file) + +$(DOCDESTDIR): + $(INS.dir) + +$(DOCAPIDESTDIR): $(DOCDESTDIR) + $(INS.dir) + +$(DOCEXAMPLESDESTDIR): $(DOCDESTDIR) + $(INS.dir) + +$(DOCEXAMPLESDESTDIR)/%: % + $(RM) $@; $(INS) -s -m $(FILEMODE) -f $(@D) $(EXAMPLESDIR)/$< + +install_doc: $(CLASSES) $(DOCAPIDESTDIR) + -$(RM) -r $(DOCAPIDESTDIR)/* + cd $(TOP); umask 022; \ + $(JAVADOC) $(JAVASRCDIR)/*.java -classpath $(CLASSPATH) -d \ + $(DOCAPIDESTDIR) -public $(DNSSD_PKG) + +doc: + -@mkdir -p $(DOCAPIDIR) + cd $(TOP); umask 022; \ + $(JAVADOC) $(JAVASRCDIR)/*.java -classpath $(CLASSPATH) -d \ + $(DOCAPIDIR) -public $(DNSSD_PKG) diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java new file mode 100644 index 0000000000..7978dda42f --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java @@ -0,0 +1,71 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: QueryListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** A listener that receives results from {@link DNSSD#queryRecord}. */ + +public interface QueryListener extends BaseListener +{ + /** Called when a record query has been completed.<P> + + @param query + The active query object. + <P> + @param flags + Possible values are DNSSD.MORE_COMING. + <P> + @param ifIndex + The interface on which the query was resolved. (The index for a given + interface is determined via the if_nametoindex() family of calls.) + <P> + @param fullName + The resource record's full domain name. + <P> + @param rrtype + The resource record's type (e.g. PTR, SRV, etc) as defined by RFC 1035 and its updates. + <P> + @param rrclass + The class of the resource record, as defined by RFC 1035 and its updates. + <P> + @param rdata + The raw rdata of the resource record. + <P> + @param ttl + The resource record's time to live, in seconds. + */ + void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName, + int rrtype, int rrclass, byte[] rdata, int ttl); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java new file mode 100644 index 0000000000..d40b6f3e75 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java @@ -0,0 +1,64 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: RegisterListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** A listener that receives results from {@link DNSSD#register}. */ + +public interface RegisterListener extends BaseListener +{ + /** Called when a registration has been completed.<P> + + @param registration + The active registration. + <P> + @param flags + Currently unused, reserved for future use. + <P> + @param serviceName + The service name registered (if the application did not specify a name in + DNSSD.register(), this indicates what name was automatically chosen). + <P> + @param regType + The type of service registered, as it was passed to DNSSD.register(). + <P> + @param domain + The domain on which the service was registered. If the application did not + specify a domain in DNSSD.register(), this is the default domain + on which the service was registered. + */ + void serviceRegistered( DNSSDRegistration registration, int flags, String serviceName, + String regType, String domain); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java new file mode 100644 index 0000000000..6b8c757ae3 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java @@ -0,0 +1,49 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: RegisterRecordListener.java,v $ +Revision 1.2 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.1 2006/06/20 23:00:12 rpantos +<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent + +ident "%Z%%M% %I% %E% SMI" + + */ + + +package com.apple.dnssd; + + +/** A listener that receives results from {@link DNSSDRecordRegistrar#registerRecord}. */ + +public interface RegisterRecordListener extends BaseListener +{ + /** Called when a record registration succeeds.<P> + + @param record + A {@link DNSRecord}. + <P> + @param flags + Currently ignored, reserved for future use. + <P> + */ + void recordRegistered( DNSRecord record, int flags); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java new file mode 100644 index 0000000000..4589f53915 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java @@ -0,0 +1,71 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: ResolveListener.java,v $ +Revision 1.3 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + +*/ + + +package com.apple.dnssd; + + +/** A listener that receives results from {@link DNSSD#resolve}. */ + +public interface ResolveListener extends BaseListener +{ + /** Called when a service has been resolved.<P> + + @param resolver + The active resolver object. + <P> + @param flags + Currently unused, reserved for future use. + <P> + @param fullName + The full service domain name, in the form <servicename>.<protocol>.<domain>. + (Any literal dots (".") are escaped with a backslash ("\."), and literal + backslashes are escaped with a second backslash ("\\"), e.g. a web server + named "Dr. Pepper" would have the fullname "Dr\.\032Pepper._http._tcp.local."). + This is the appropriate format to pass to standard system DNS APIs such as + res_query(), or to the special-purpose functions included in this API that + take fullname parameters. + <P> + @param hostName + The target hostname of the machine providing the service. This name can + be passed to functions like queryRecord() to look up the host's IP address. + <P> + @param port + The port number on which connections are accepted for this service. + <P> + @param txtRecord + The service's primary txt record. + */ + void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord); +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java new file mode 100644 index 0000000000..c1c5f0b3b7 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java @@ -0,0 +1,313 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: TXTRecord.java,v $ +Revision 1.6 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.5 2004/08/25 21:54:36 rpantos +<rdar://problem/3773973> Fix getValue() for values containing '='. + +Revision 1.4 2004/08/04 01:04:50 rpantos +<rdar://problems/3731579&3731582> Fix set(); add remove() & toString(). + +Revision 1.3 2004/07/13 21:24:25 rpantos +Fix for <rdar://problem/3701120>. + +Revision 1.2 2004/04/30 21:48:27 rpantos +Change line endings for CVS. + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + +ident "%Z%%M% %I% %E% SMI" + + To do: + - implement remove() + - fix set() to replace existing values + */ + + +package com.apple.dnssd; + + +/** + Object used to construct and parse DNS-SD format TXT records. + For more info see <a href="http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt">DNS-Based Service Discovery</a>, section 6. +*/ + +public class TXTRecord +{ + /* + DNS-SD specifies that a TXT record corresponding to an SRV record consist of + a packed array of bytes, each preceded by a length byte. Each string + is an attribute-value pair. + + The TXTRecord object stores the entire TXT data as a single byte array, traversing it + as need be to implement its various methods. + */ + + static final protected byte kAttrSep = '='; + + protected byte[] fBytes; + + /** Constructs a new, empty TXT record. */ + public TXTRecord() + { fBytes = new byte[0]; } + + /** Constructs a new TXT record from a byte array in the standard format. */ + public TXTRecord( byte[] initBytes) + { fBytes = (byte[]) initBytes.clone(); } + + /** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P> + @param key + The key name. Must be ASCII, with no '=' characters. + <P> + @param value + Value to be encoded into bytes using the default platform character set. + */ + public void set( String key, String value) + { + byte[] valBytes = (value != null) ? value.getBytes() : null; + this.set( key, valBytes); + } + + /** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P> + @param key + The key name. Must be ASCII, with no '=' characters. + <P> + @param value + Binary representation of the value. + */ + public void set( String key, byte[] value) + { + byte[] keyBytes; + int valLen = (value != null) ? value.length : 0; + + try { + keyBytes = key.getBytes( "US-ASCII"); + } + catch ( java.io.UnsupportedEncodingException uee) { + throw new IllegalArgumentException(); + } + + for ( int i=0; i < keyBytes.length; i++) + if ( keyBytes[i] == '=') + throw new IllegalArgumentException(); + + if ( keyBytes.length + valLen >= 255) + throw new ArrayIndexOutOfBoundsException(); + + int prevLoc = this.remove( key); + if ( prevLoc == -1) + prevLoc = this.size(); + + this.insert( keyBytes, value, prevLoc); + } + + protected void insert( byte[] keyBytes, byte[] value, int index) + // Insert a key-value pair at index + { + byte[] oldBytes = fBytes; + int valLen = (value != null) ? value.length : 0; + int insertion = 0; + byte newLen, avLen; + + // locate the insertion point + for ( int i=0; i < index && insertion < fBytes.length; i++) + insertion += fBytes[ insertion] + 1; + + avLen = (byte) ( keyBytes.length + valLen + (value != null ? 1 : 0)); + newLen = (byte) ( avLen + oldBytes.length + 1); + + fBytes = new byte[ newLen]; + System.arraycopy( oldBytes, 0, fBytes, 0, insertion); + int secondHalfLen = oldBytes.length - insertion; + System.arraycopy( oldBytes, insertion, fBytes, newLen - secondHalfLen, secondHalfLen); + fBytes[ insertion] = avLen; + System.arraycopy( keyBytes, 0, fBytes, insertion + 1, keyBytes.length); + if ( value != null) + { + fBytes[ insertion + 1 + keyBytes.length] = kAttrSep; + System.arraycopy( value, 0, fBytes, insertion + keyBytes.length + 2, valLen); + } + } + + /** Remove a key/value pair from the TXT record. Returns index it was at, or -1 if not found. */ + public int remove( String key) + { + int avStart = 0; + + for ( int i=0; avStart < fBytes.length; i++) + { + int avLen = fBytes[ avStart]; + if ( key.length() <= avLen && + ( key.length() == avLen || fBytes[ avStart + key.length() + 1] == kAttrSep)) + { + String s = new String( fBytes, avStart + 1, key.length()); + if ( 0 == key.compareToIgnoreCase( s)) + { + byte[] oldBytes = fBytes; + fBytes = new byte[ oldBytes.length - avLen - 1]; + System.arraycopy( oldBytes, 0, fBytes, 0, avStart); + System.arraycopy( oldBytes, avStart + avLen + 1, fBytes, avStart, oldBytes.length - avStart - avLen - 1); + return i; + } + } + avStart += avLen + 1; + } + return -1; + } + + /** Return the number of keys in the TXT record. */ + public int size() + { + int i, avStart; + + for ( i=0, avStart=0; avStart < fBytes.length; i++) + avStart += fBytes[ avStart] + 1; + return i; + } + + /** Return true if key is present in the TXT record, false if not. */ + public boolean contains( String key) + { + String s = null; + + for ( int i=0; null != ( s = this.getKey( i)); i++) + if ( 0 == key.compareToIgnoreCase( s)) + return true; + return false; + } + + /** Return a key in the TXT record by zero-based index. Returns null if index exceeds the total number of keys. */ + public String getKey( int index) + { + int avStart = 0; + + for ( int i=0; i < index && avStart < fBytes.length; i++) + avStart += fBytes[ avStart] + 1; + + if ( avStart < fBytes.length) + { + int avLen = fBytes[ avStart]; + int aLen = 0; + + for ( aLen=0; aLen < avLen; aLen++) + if ( fBytes[ avStart + aLen + 1] == kAttrSep) + break; + return new String( fBytes, avStart + 1, aLen); + } + return null; + } + + /** + Look up a key in the TXT record by zero-based index and return its value. <P> + Returns null if index exceeds the total number of keys. + Returns null if the key is present with no value. + */ + public byte[] getValue( int index) + { + int avStart = 0; + byte[] value = null; + + for ( int i=0; i < index && avStart < fBytes.length; i++) + avStart += fBytes[ avStart] + 1; + + if ( avStart < fBytes.length) + { + int avLen = fBytes[ avStart]; + int aLen = 0; + + for ( aLen=0; aLen < avLen; aLen++) + { + if ( fBytes[ avStart + aLen + 1] == kAttrSep) + { + value = new byte[ avLen - aLen - 1]; + System.arraycopy( fBytes, avStart + aLen + 2, value, 0, avLen - aLen - 1); + break; + } + } + } + return value; + } + + /** Converts the result of getValue() to a string in the platform default character set. */ + public String getValueAsString( int index) + { + byte[] value = this.getValue( index); + return value != null ? new String( value) : null; + } + + /** Get the value associated with a key. Will be null if the key is not defined. + Array will have length 0 if the key is defined with an = but no value.<P> + + @param forKey + The left-hand side of the key-value pair. + <P> + @return The binary representation of the value. + */ + public byte[] getValue( String forKey) + { + String s = null; + int i; + + for ( i=0; null != ( s = this.getKey( i)); i++) + if ( 0 == forKey.compareToIgnoreCase( s)) + return this.getValue( i); + return null; + } + + /** Converts the result of getValue() to a string in the platform default character set.<P> + + @param forKey + The left-hand side of the key-value pair. + <P> + @return The value represented in the default platform character set. + */ + public String getValueAsString( String forKey) + { + byte[] val = this.getValue( forKey); + return val != null ? new String( val) : null; + } + + /** Return the contents of the TXT record as raw bytes. */ + public byte[] getRawBytes() { return (byte[]) fBytes.clone(); } + + /** Return a string representation of the object. */ + public String toString() + { + String a, result = null; + + for ( int i=0; null != ( a = this.getKey( i)); i++) + { + String av = String.valueOf( i) + "={" + a; + String val = this.getValueAsString( i); + if ( val != null) + av += "=" + val + "}"; + else + av += "}"; + if ( result == null) + result = av; + else + result = result + ", " + av; + } + return result != null ? result : ""; + } +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java new file mode 100644 index 0000000000..381657151b --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java @@ -0,0 +1,400 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + BrowserApp demonstrates how to use DNS-SD to browse for and resolve services. + + To do: + - display resolved TXTRecord + +ident "%Z%%M% %I% %E% SMI" + + */ + + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.text.*; +import javax.swing.*; +import javax.swing.event.*; + +import com.apple.dnssd.*; + + +class BrowserApp implements ListSelectionListener, ResolveListener +{ + static BrowserApp app; + JFrame frame; + DomainListModel domainList; + BrowserListModel servicesList, serviceList; + JList domainPane, servicesPane, servicePane; + DNSSDService servicesBrowser, serviceBrowser, domainBrowser; + JLabel hostLabel, portLabel; + + public BrowserApp() + { + frame = new JFrame("DNS-SD Service Browser"); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) {System.exit(0);} + }); + + domainList = new DomainListModel(); + servicesList = new ServicesBrowserListModel(); + serviceList = new BrowserListModel(); + + try { + domainBrowser = DNSSD.enumerateDomains( DNSSD.BROWSE_DOMAINS, 0, domainList); + + servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList); + serviceBrowser = null; + } + catch ( Exception ex) { terminateWithException( ex); } + + this.setupSubPanes( frame.getContentPane()); + frame.pack(); + frame.setVisible(true); + } + + protected void setupSubPanes( Container parent) + { + parent.setLayout( new BoxLayout( parent, BoxLayout.Y_AXIS)); + + JPanel browserRow = new JPanel(); + browserRow.setLayout( new BoxLayout( browserRow, BoxLayout.X_AXIS)); + domainPane = new JList( domainList); + domainPane.addListSelectionListener( this); + JScrollPane domainScroller = new JScrollPane( domainPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + browserRow.add( domainScroller); + servicesPane = new JList( servicesList); + servicesPane.addListSelectionListener( this); + JScrollPane servicesScroller = new JScrollPane( servicesPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + browserRow.add( servicesScroller); + servicePane = new JList( serviceList); + servicePane.addListSelectionListener( this); + JScrollPane serviceScroller = new JScrollPane( servicePane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + browserRow.add( serviceScroller); + +/* + JPanel buttonRow = new JPanel(); + buttonRow.setLayout( new BoxLayout( buttonRow, BoxLayout.X_AXIS)); + buttonRow.add( Box.createHorizontalGlue()); + JButton connectButton = new JButton( "Don't Connect"); + buttonRow.add( connectButton); + buttonRow.add( Box.createRigidArea( new Dimension( 16, 0))); +*/ + + JPanel labelRow = new JPanel(); + labelRow.setLayout( new BoxLayout( labelRow, BoxLayout.X_AXIS)); + labelRow.add( new JLabel( " Host: ")); + hostLabel = new JLabel(); + labelRow.add( hostLabel); + labelRow.add( Box.createRigidArea( new Dimension( 32, 0))); + labelRow.add( new JLabel( "Port: ")); + portLabel = new JLabel(); + labelRow.add( portLabel); + labelRow.add( Box.createHorizontalGlue()); + + parent.add( browserRow); + parent.add( Box.createRigidArea( new Dimension( 0, 8))); + parent.add( labelRow); +// parent.add( buttonRow); + parent.add( Box.createRigidArea( new Dimension( 0, 16))); + } + + public void valueChanged( ListSelectionEvent e) + { + try { + if ( e.getSource() == domainPane && !e.getValueIsAdjusting()) + { + int newSel = domainPane.getSelectedIndex(); + if ( -1 != newSel) + { + if ( serviceBrowser != null) + serviceBrowser.stop(); + serviceList.removeAllElements(); + servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList); + } + } + else if ( e.getSource() == servicesPane && !e.getValueIsAdjusting()) + { + int newSel = servicesPane.getSelectedIndex(); + if ( serviceBrowser != null) + serviceBrowser.stop(); + serviceList.removeAllElements(); + if ( -1 != newSel) + serviceBrowser = DNSSD.browse( 0, 0, servicesList.getNthRegType( newSel), "", serviceList); + } + else if ( e.getSource() == servicePane && !e.getValueIsAdjusting()) + { + int newSel = servicePane.getSelectedIndex(); + + hostLabel.setText( ""); + portLabel.setText( ""); + + if ( -1 != newSel) + { + DNSSD.resolve( 0, serviceList.getNthInterface( newSel), + serviceList.getNthServiceName( newSel), + serviceList.getNthRegType( newSel), + serviceList.getNthDomain( newSel), + new SwingResolveListener( this)); + } + } + } + catch ( Exception ex) { terminateWithException( ex); } + } + + public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord) + { + hostLabel.setText( hostName); + portLabel.setText( String.valueOf( port)); + } + + public void operationFailed( DNSSDService service, int errorCode) + { + // handle failure here + } + + protected static void terminateWithException( Exception e) + { + e.printStackTrace(); + System.exit( -1); + } + + public static void main(String s[]) + { + app = new BrowserApp(); + } +} + + +class BrowserListModel extends DefaultListModel implements BrowseListener, Runnable +{ + public BrowserListModel() + { + addCache = new Vector(); + removeCache = new Vector(); + } + + /* The Browser invokes this callback when a service is discovered. */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + addCache.add( new BrowserListElem( serviceName, domain, regType, ifIndex)); + if ( ( flags & DNSSD.MORE_COMING) == 0) + this.scheduleOnEventThread(); + } + + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + removeCache.add( serviceName); + if ( ( flags & DNSSD.MORE_COMING) == 0) + this.scheduleOnEventThread(); + } + + public void run() + { + while ( removeCache.size() > 0) + { + String serviceName = (String) removeCache.remove( removeCache.size() - 1); + int matchInd = this.findMatching( serviceName); // probably doesn't handle near-duplicates well. + if ( matchInd != -1) + this.removeElementAt( matchInd); + } + while ( addCache.size() > 0) + { + BrowserListElem elem = (BrowserListElem) addCache.remove( addCache.size() - 1); + if ( -1 == this.findMatching( elem.fServiceName)) // probably doesn't handle near-duplicates well. + this.addInSortOrder( elem); + } + } + + public void operationFailed( DNSSDService service, int errorCode) + { + // handle failure here + } + + /* The list contains BrowserListElem's */ + class BrowserListElem + { + public BrowserListElem( String serviceName, String domain, String type, int ifIndex) + { fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; } + + public String toString() { return fServiceName; } + + public String fServiceName, fDomain, fType; + public int fInt; + } + + public String getNthServiceName( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fServiceName; + } + + public String getNthRegType( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fType; + } + + public String getNthDomain( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fDomain; + } + + public int getNthInterface( int n) + { + BrowserListElem sel = (BrowserListElem) this.get( n); + return sel.fInt; + } + + protected void addInSortOrder( Object obj) + { + int i; + for ( i = 0; i < this.size(); i++) + if ( sCollator.compare( obj.toString(), this.getElementAt( i).toString()) < 0) + break; + this.add( i, obj); + } + + protected int findMatching( String match) + { + for ( int i = 0; i < this.size(); i++) + if ( match.equals( this.getElementAt( i).toString())) + return i; + return -1; + } + + protected void scheduleOnEventThread() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected Vector removeCache; // list of serviceNames to remove + protected Vector addCache; // list of BrowserListElem's to add + + protected static Collator sCollator; + + static // Initialize our static variables + { + sCollator = Collator.getInstance(); + sCollator.setStrength( Collator.PRIMARY); + } +} + + +class ServicesBrowserListModel extends BrowserListModel +{ + /* The Browser invokes this callback when a service is discovered. */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + // Overridden to stuff serviceName into regType and make serviceName human-readable. + { + regType = serviceName + ( regType.startsWith( "_udp.") ? "._udp." : "._tcp."); + super.serviceFound( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain); + } + + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + // Overridden to make serviceName human-readable. + { + super.serviceLost( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain); + } + + protected String mapTypeToName( String type) + // Convert a registration type into a human-readable string. Returns original string on no-match. + { + final String[] namedServices = { + "_afpovertcp", "Apple File Sharing", + "_http", "World Wide Web servers", + "_daap", "Digital Audio Access", + "_apple-sasl", "Apple Password Servers", + "_distcc", "Distributed Compiler nodes", + "_finger", "Finger servers", + "_ichat", "iChat clients", + "_presence", "iChat AV clients", + "_ssh", "SSH servers", + "_telnet", "Telnet servers", + "_workstation", "Macintosh Manager clients", + "_bootps", "BootP servers", + "_xserveraid", "XServe RAID devices", + "_eppc", "Remote AppleEvents", + "_ftp", "FTP services", + "_tftp", "TFTP services" + }; + + for ( int i = 0; i < namedServices.length; i+=2) + if ( namedServices[i].equals( type)) + return namedServices[i + 1]; + return type; + } +} + + +class DomainListModel extends DefaultListModel implements DomainListener +{ + /* Called when a domain is discovered. */ + public void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain) + { + if ( !this.contains( domain)) + this.addElement( domain); + } + + public void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain) + { + if ( this.contains( domain)) + this.removeElement( domain); + } + + public void operationFailed( DNSSDService service, int errorCode) + { + // handle failure here + } +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.manifest b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.manifest new file mode 100644 index 0000000000..472b17f638 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.manifest @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: BrowserApp +Class-Path: /usr/share/lib/java/dnssd.jar diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.java new file mode 100644 index 0000000000..62dd11faa5 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.java @@ -0,0 +1,336 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + SimpleChat is a simple peer-to-peer chat program that demonstrates + DNS-SD registration, browsing, resolving and record-querying. + + To do: + - implement better coloring algorithm + +ident "%Z%%M% %I% %E% SMI" + + */ + + +import java.awt.*; +import java.awt.event.*; +import java.text.*; +import java.net.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; + +import com.apple.dnssd.*; + + +class SimpleChat implements ResolveListener, RegisterListener, QueryListener, + ActionListener, ItemListener, Runnable +{ + Document textDoc; // Holds all the chat text + JTextField inputField; // Holds a pending chat response + String ourName; // name used to identify this user in chat + DNSSDService browser; // object that actively browses for other chat clients + DNSSDService resolver; // object that resolves other chat clients + DNSSDRegistration registration; // object that maintains our connection advertisement + JComboBox targetPicker; // Indicates who we're talking to + TargetListModel targetList; // and its list model + JButton sendButton; // Will send text in inputField to target + InetAddress buddyAddr; // and address + int buddyPort; // and port + DatagramPacket dataPacket; // Inbound data packet + DatagramSocket outSocket; // Outbound data socket + SimpleAttributeSet textAttribs; + + static final String kChatExampleRegType = "_p2pchat._udp"; + static final String kWireCharSet = "ISO-8859-1"; + + public SimpleChat() throws Exception + { + JFrame frame = new JFrame("SimpleChat"); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) {System.exit(0);} + }); + + ourName = System.getProperty( "user.name"); + targetList = new TargetListModel(); + textAttribs = new SimpleAttributeSet(); + DatagramSocket inSocket = new DatagramSocket(); + dataPacket = new DatagramPacket( new byte[ 4096], 4096); + outSocket = new DatagramSocket(); + + this.setupSubPanes( frame.getContentPane(), frame.getRootPane()); + frame.pack(); + frame.setVisible(true); + inputField.requestFocusInWindow(); + + browser = DNSSD.browse( 0, 0, kChatExampleRegType, "", new SwingBrowseListener( targetList)); + + registration = DNSSD.register( 0, 0, ourName, kChatExampleRegType, "", "", inSocket.getLocalPort(), null, this); + + new ListenerThread( this, inSocket, dataPacket).start(); + } + + protected void setupSubPanes( Container parent, JRootPane rootPane) + { + parent.setLayout( new BoxLayout( parent, BoxLayout.Y_AXIS)); + + JPanel textRow = new JPanel(); + textRow.setLayout( new BoxLayout( textRow, BoxLayout.X_AXIS)); + textRow.add( Box.createRigidArea( new Dimension( 16, 0))); + JEditorPane textPane = new JEditorPane( "text/html", "<BR>"); + textPane.setPreferredSize( new Dimension( 400, 300)); + textPane.setEditable( false); + JScrollPane textScroller = new JScrollPane( textPane); + textRow.add( textScroller); + textRow.add( Box.createRigidArea( new Dimension( 16, 0))); + textDoc = textPane.getDocument(); + + JPanel addressRow = new JPanel(); + addressRow.setLayout( new BoxLayout( addressRow, BoxLayout.X_AXIS)); + targetPicker = new JComboBox( targetList); + targetPicker.addItemListener( this); + addressRow.add( Box.createRigidArea( new Dimension( 16, 0))); + addressRow.add( new JLabel( "Talk to: ")); + addressRow.add( targetPicker); + addressRow.add( Box.createHorizontalGlue()); + + JPanel buttonRow = new JPanel(); + buttonRow.setLayout( new BoxLayout( buttonRow, BoxLayout.X_AXIS)); + buttonRow.add( Box.createRigidArea( new Dimension( 16, 0))); + inputField = new JTextField(); + // prevent inputField from hijacking <Enter> key + inputField.getKeymap().removeKeyStrokeBinding( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0)); + buttonRow.add( inputField); + sendButton = new JButton( "Send"); + buttonRow.add( Box.createRigidArea( new Dimension( 8, 0))); + buttonRow.add( sendButton); + buttonRow.add( Box.createRigidArea( new Dimension( 16, 0))); + rootPane.setDefaultButton( sendButton); + sendButton.addActionListener( this); + sendButton.setEnabled( false); + + parent.add( Box.createRigidArea( new Dimension( 0, 16))); + parent.add( textRow); + parent.add( Box.createRigidArea( new Dimension( 0, 8))); + parent.add( addressRow); + parent.add( Box.createRigidArea( new Dimension( 0, 8))); + parent.add( buttonRow); + parent.add( Box.createRigidArea( new Dimension( 0, 16))); + } + + public void serviceRegistered( DNSSDRegistration registration, int flags, + String serviceName, String regType, String domain) + { + ourName = serviceName; // might have been renamed on collision + } + + public void operationFailed( DNSSDService service, int errorCode) + { + System.out.println( "Service reported error " + String.valueOf( errorCode)); + } + + public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord) + { + buddyPort = port; + try { + // Start a record query to obtain IP address from hostname + DNSSD.queryRecord( 0, ifIndex, hostName, 1 /* ns_t_a */, 1 /* ns_c_in */, + new SwingQueryListener( this)); + } + catch ( Exception e) { terminateWithException( e); } + resolver.stop(); + } + + public void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName, + int rrtype, int rrclass, byte[] rdata, int ttl) + { + try { + buddyAddr = InetAddress.getByAddress( rdata); + } + catch ( Exception e) { terminateWithException( e); } + sendButton.setEnabled( true); + } + + public void actionPerformed( ActionEvent e) // invoked when Send button is hit + { + try + { + String sendString = ourName + ": " + inputField.getText(); + byte[] sendData = sendString.getBytes( kWireCharSet); + outSocket.send( new DatagramPacket( sendData, sendData.length, buddyAddr, buddyPort)); + StyleConstants.setForeground( textAttribs, Color.black); + textDoc.insertString( textDoc.getLength(), inputField.getText() + "\n", textAttribs); + inputField.setText( ""); + } + catch ( Exception exception) { terminateWithException( exception); } + } + + public void itemStateChanged( ItemEvent e) // invoked when Target selection changes + { + sendButton.setEnabled( false); + if ( e.getStateChange() == ItemEvent.SELECTED) + { + try { + TargetListElem sel = (TargetListElem) targetList.getSelectedItem(); + resolver = DNSSD.resolve( 0, sel.fInt, sel.fServiceName, sel.fType, sel.fDomain, this); + } + catch ( Exception exception) { terminateWithException( exception); } + } + } + + public void run() // invoked on event thread when inbound packet arrives + { + try + { + String inMessage = new String( dataPacket.getData(), 0, dataPacket.getLength(), kWireCharSet); + StyleConstants.setForeground( textAttribs, this.getColorFor( dataPacket.getData(), dataPacket.getLength())); + textDoc.insertString( textDoc.getLength(), inMessage + "\n", textAttribs); + } + catch ( Exception e) { terminateWithException( e); } + } + + protected Color getColorFor( byte[] chars, int length) + // Produce a mapping from a string to a color, suitable for text display + { + int rgb = 0; + for ( int i=0; i < length && chars[i] != ':'; i++) + rgb = rgb ^ ( (int) chars[i] << (i%3+2) * 8); + return new Color( rgb & 0x007F7FFF); // mask off high bits so it is a dark color + +// for ( int i=0; i < length && chars[i] != ':'; i++) + + } + + protected static void terminateWithException( Exception e) + { + e.printStackTrace(); + System.exit( -1); + } + + public static void main(String s[]) + { + try { + new SimpleChat(); + } + catch ( Exception e) { terminateWithException( e); } + } +} + + + +class TargetListElem +{ + public TargetListElem( String serviceName, String domain, String type, int ifIndex) + { fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; } + + public String toString() { return fServiceName; } + + public String fServiceName, fDomain, fType; + public int fInt; +} + +class TargetListModel extends DefaultComboBoxModel implements BrowseListener +{ + /* The Browser invokes this callback when a service is discovered. */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + TargetListElem match = this.findMatching( serviceName); // probably doesn't handle near-duplicates well. + + if ( match == null) + this.addElement( new TargetListElem( serviceName, domain, regType, ifIndex)); + } + + /* The Browser invokes this callback when a service disappears. */ + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + TargetListElem match = this.findMatching( serviceName); // probably doesn't handle near-duplicates well. + + if ( match != null) + this.removeElement( match); + } + + /* The Browser invokes this callback when a service disappears. */ + public void operationFailed( DNSSDService service, int errorCode) + { + System.out.println( "Service reported error " + String.valueOf( errorCode)); + } + + protected TargetListElem findMatching( String match) + { + for ( int i = 0; i < this.getSize(); i++) + if ( match.equals( this.getElementAt( i).toString())) + return (TargetListElem) this.getElementAt( i); + return null; + } + +} + + +// A ListenerThread runs its owner when datagram packet p appears on socket s. +class ListenerThread extends Thread +{ + public ListenerThread( Runnable owner, DatagramSocket s, DatagramPacket p) + { fOwner = owner; fSocket = s; fPacket = p; } + + public void run() + { + while ( true ) + { + try + { + fSocket.receive( fPacket); + SwingUtilities.invokeAndWait( fOwner); // process data on main thread + } + catch( Exception e) + { + break; // terminate thread + } + } + } + + protected Runnable fOwner; + protected DatagramSocket fSocket; + protected DatagramPacket fPacket; +} + + + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.manifest b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.manifest new file mode 100644 index 0000000000..71ca6a754e --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.manifest @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: SimpleChat +Class-Path: /usr/share/lib/java/dnssd.jar diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingBrowseListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingBrowseListener.java new file mode 100644 index 0000000000..c8ff1e6ca1 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingBrowseListener.java @@ -0,0 +1,126 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ident "%Z%%M% %I% %E% SMI" + + */ + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule BrowseListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingBrowseListener implements Runnable, BrowseListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingBrowseListener( BrowseListener listener) + { fListener = listener; fErrorCode = 0; } + + /** (Clients should not call this method directly.) */ + public void operationFailed( DNSSDService service, int errorCode) + { + fBrowser = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void serviceFound( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + + { + fBrowser = browser; + fIsAdd = true; + fFlags = flags; + fIndex = ifIndex; + fService = serviceName; + fRegType = regType; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void serviceLost( DNSSDService browser, int flags, int ifIndex, + String serviceName, String regType, String domain) + { + fBrowser = browser; + fIsAdd = false; + fFlags = flags; + fIndex = ifIndex; + fService = serviceName; + fRegType = regType; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fBrowser, fErrorCode); + else if ( fIsAdd) + fListener.serviceFound( fBrowser, fFlags, fIndex, fService, fRegType, fDomain); + else + fListener.serviceLost( fBrowser, fFlags, fIndex, fService, fRegType, fDomain); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected BrowseListener fListener; + + protected boolean fIsAdd; + protected DNSSDService fBrowser; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fService; + protected String fRegType; + protected String fDomain; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingDomainListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingDomainListener.java new file mode 100644 index 0000000000..988591ccfc --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingDomainListener.java @@ -0,0 +1,119 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule DomainListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingDomainListener implements Runnable, DomainListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingDomainListener( DomainListener listener) + { fListener = listener; fErrorCode = 0; } + + /** (Clients should not call this method directly.) */ + public void operationFailed( DNSSDService service, int errorCode) + { + fEnumerator = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain) + + { + fEnumerator = domainEnum; + fIsAdd = true; + fFlags = flags; + fIndex = ifIndex; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain) + { + fEnumerator = domainEnum; + fIsAdd = false; + fFlags = flags; + fIndex = ifIndex; + fDomain = domain; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fEnumerator, fErrorCode); + else if ( fIsAdd) + fListener.domainFound( fEnumerator, fFlags, fIndex, fDomain); + else + fListener.domainLost( fEnumerator, fFlags, fIndex, fDomain); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected DomainListener fListener; + + protected boolean fIsAdd; + protected DNSSDService fEnumerator; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fDomain; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingQueryListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingQueryListener.java new file mode 100644 index 0000000000..1b301ce8e9 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingQueryListener.java @@ -0,0 +1,111 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule QueryListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingQueryListener implements Runnable, QueryListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingQueryListener( QueryListener listener) + { fListener = listener; } + + public void operationFailed( DNSSDService service, int errorCode) + { + fQuery = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName, + int rrtype, int rrclass, byte[] rdata, int ttl) + { + fQuery = query; + fFlags = flags; + fIndex = ifIndex; + fFullName = fullName; + fType = rrtype; + fClass = rrclass; + fData = rdata; + fTTL = ttl; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fQuery, fErrorCode); + else + fListener.queryAnswered( fQuery, fFlags, fIndex, fFullName, fType, fClass, fData, fTTL); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected QueryListener fListener; + + protected DNSSDService fQuery; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fFullName; + protected int fType; + protected int fClass; + protected byte[] fData; + protected int fTTL; +} + diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingResolveListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingResolveListener.java new file mode 100644 index 0000000000..499da0707d --- /dev/null +++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingResolveListener.java @@ -0,0 +1,109 @@ +/* -*- Mode: Java; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and subject + * to these terms, Apple grants you a personal, non-exclusive license, under Apple's + * copyrights in this original Apple software (the "Apple Software"), to use, + * reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions of + * the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from the + * Apple Software without specific prior written permission from Apple. Except as + * expressly stated in this notice, no other rights or licenses, express or implied, + * are granted by Apple herein, including but not limited to any patent rights that + * may be infringed by your derivative works or by other works in which the Apple + * Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ident "%Z%%M% %I% %E% SMI" + + */ + + +import javax.swing.*; +import com.apple.dnssd.*; + + +/** Use this to schedule ResolveListener callbacks via SwingUtilities.invokeAndWait(). */ + +public class SwingResolveListener implements Runnable, ResolveListener +{ + /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */ + public SwingResolveListener( ResolveListener listener) + { fListener = listener; } + + public void operationFailed( DNSSDService service, int errorCode) + { + fResolver = service; + fErrorCode = errorCode; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName, + String hostName, int port, TXTRecord txtRecord) + { + fResolver = resolver; + fFlags = flags; + fIndex = ifIndex; + fFullName = fullName; + fHostName = hostName; + fPort = port; + fTXTRecord = txtRecord; + this.schedule(); + } + + /** (Clients should not call this method directly.) */ + public void run() + { + if ( fErrorCode != 0) + fListener.operationFailed( fResolver, fErrorCode); + else + fListener.serviceResolved( fResolver, fFlags, fIndex, fFullName, fHostName, fPort, fTXTRecord); + } + + protected void schedule() + { + try { + SwingUtilities.invokeAndWait( this); + } + catch ( Exception e) + { + e.printStackTrace(); + } + } + + protected ResolveListener fListener; + + protected DNSSDService fResolver; + protected int fFlags; + protected int fIndex; + protected int fErrorCode; + protected String fFullName; + protected String fHostName; + protected int fPort; + protected TXTRecord fTXTRecord; +} + diff --git a/usr/src/lib/libdns_sd/java/common/JNISupport.c b/usr/src/lib/libdns_sd/java/common/JNISupport.c new file mode 100644 index 0000000000..bdf453475d --- /dev/null +++ b/usr/src/lib/libdns_sd/java/common/JNISupport.c @@ -0,0 +1,1107 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + + Change History (most recent first): + +$Log: JNISupport.c,v $ +Revision 1.17 2006/08/14 23:25:08 cheshire +Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0 + +Revision 1.16 2006/07/14 02:35:47 cheshire +Added (commented out) syslog debugging messages + +Revision 1.15 2006/06/27 19:34:43 cheshire +<rdar://problem/4430023> txtRecord parameter of DNSServiceResolveReply() should be unsigned char * + +Revision 1.14 2006/06/20 23:03:35 rpantos +<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent + +Revision 1.13 2005/10/26 01:52:24 cheshire +<rdar://problem/4316286> Race condition in Java code (doesn't work at all on Linux) + +Revision 1.12 2005/07/13 19:20:32 cheshire +<rdar://problem/4175511> Race condition in Java API +Additional cleanup suggested by Roger -- NewContext() doesn't need ownerClass parameter any more + +Revision 1.11 2005/07/11 01:55:21 cheshire +<rdar://problem/4175511> Race condition in Java API + +Revision 1.10 2005/07/05 13:01:52 cheshire +<rdar://problem/4169791> If mDNSResponder daemon is stopped, Java API spins, burning CPU time + +Revision 1.9 2004/12/11 03:01:00 rpantos +<rdar://problem/3907498> Java DNSRecord API should be cleaned up + +Revision 1.8 2004/11/30 23:51:05 cheshire +Remove double semicolons + +Revision 1.7 2004/11/23 08:12:04 shersche +Implement if_nametoindex and if_indextoname for Win32 platforms + +Revision 1.6 2004/11/23 03:41:14 cheshire +Change JNISupport.c to call if_indextoname & if_nametoindex directly. +(May require some additional glue code to work on Windows.) + +Revision 1.5 2004/11/17 17:07:44 cheshire +Updated comment about AUTO_CALLBACKS + +Revision 1.4 2004/11/12 03:23:09 rpantos +rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex. + +Revision 1.3 2004/06/18 04:44:17 rpantos +Adapt to API unification on Windows + +Revision 1.2 2004/05/28 23:34:42 ksekar +<rdar://problem/3672903>: Java project build errors + +Revision 1.1 2004/04/30 16:29:35 rpantos +First checked in. + + + This file contains the platform support for DNSSD and related Java classes. + It is used to shim through to the underlying <dns_sd.h> API. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +// AUTO_CALLBACKS should be set to 1 if the underlying mDNS implementation fires response +// callbacks automatically (as in the early Windows prototypes). +// AUTO_CALLBACKS should be set to 0 if the client must call DNSServiceProcessResult() to +// invoke response callbacks (as is true on Mac OS X, Posix, Windows, etc.). +// (Invoking callbacks automatically on a different thread sounds attractive, but while +// the client gains by not needing to add an event source to its main event loop, it loses +// by being forced to deal with concurrency and locking, which can be a bigger burden.) +#ifndef AUTO_CALLBACKS +#define AUTO_CALLBACKS 0 +#endif + +#if !AUTO_CALLBACKS +#ifdef _WIN32 +#include <winsock2.h> +#else //_WIN32 +#include <sys/types.h> +#include <sys/select.h> +#endif // _WIN32 +#endif // AUTO_CALLBACKS + +#include <dns_sd.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef _WIN32 +#include <winsock2.h> +#include <iphlpapi.h> +static char * if_indextoname( DWORD ifIndex, char * nameBuff); +static DWORD if_nametoindex( const char * nameStr ); +#define IF_NAMESIZE MAX_ADAPTER_NAME_LENGTH +#else // _WIN32 +#include <sys/socket.h> +#include <net/if.h> +#endif // _WIN32 +#include <jni.h> + +#include "DNSSD.java.h" + +//#include <syslog.h> + +// convenience definition +#ifdef __GNUC__ +#define _UNUSED __attribute__ ((unused)) +#else +#define _UNUSED +#endif + +enum { + kInterfaceVersion = 1 // Must match version in .jar file +}; + +typedef struct OpContext OpContext; + +struct OpContext +{ + DNSServiceRef ServiceRef; + JNIEnv *Env; + jobject JavaObj; + jobject ClientObj; + jmethodID Callback; + jmethodID Callback2; +}; + +// For AUTO_CALLBACKS, we must attach the callback thread to the Java VM prior to upcall. +#if AUTO_CALLBACKS +JavaVM *gJavaVM = NULL; +#endif + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_InitLibrary( JNIEnv *pEnv, jclass cls, + jint callerVersion) +{ + /* Ensure that caller & interface versions match. */ + if ( callerVersion != kInterfaceVersion) + return kDNSServiceErr_Incompatible; + +#if AUTO_CALLBACKS + { + jsize numVMs; + + if ( 0 != JNI_GetCreatedJavaVMs( &gJavaVM, 1, &numVMs)) + return kDNSServiceErr_BadState; + } +#endif + + // Set AppleDNSSD.hasAutoCallbacks + { +#if AUTO_CALLBACKS + jboolean hasAutoC = JNI_TRUE; +#else + jboolean hasAutoC = JNI_FALSE; +#endif + jfieldID hasAutoCField = (*pEnv)->GetStaticFieldID( pEnv, cls, "hasAutoCallbacks", "Z"); + (*pEnv)->SetStaticBooleanField( pEnv, cls, hasAutoCField, hasAutoC); + } + + return kDNSServiceErr_NoError; +} + + +static const char* SafeGetUTFChars( JNIEnv *pEnv, jstring str) +// Wrapper for JNI GetStringUTFChars() that returns NULL for null str. +{ + return str != NULL ? (*pEnv)->GetStringUTFChars( pEnv, str, 0) : NULL; +} + +static void SafeReleaseUTFChars( JNIEnv *pEnv, jstring str, const char *buff) +// Wrapper for JNI GetStringUTFChars() that handles null str. +{ + if ( str != NULL) + (*pEnv)->ReleaseStringUTFChars( pEnv, str, buff); +} + + +#if AUTO_CALLBACKS +static void SetupCallbackState( JNIEnv **ppEnv) +{ + (*gJavaVM)->AttachCurrentThread( gJavaVM, (void**) ppEnv, NULL); +} + +static void TeardownCallbackState( void ) +{ + (*gJavaVM)->DetachCurrentThread( gJavaVM); +} + +#else // AUTO_CALLBACKS + +static void SetupCallbackState( JNIEnv **ppEnv _UNUSED) +{ + // No setup necessary if ProcessResults() has been called +} + +static void TeardownCallbackState( void ) +{ + // No teardown necessary if ProcessResults() has been called +} +#endif // AUTO_CALLBACKS + + +static OpContext *NewContext( JNIEnv *pEnv, jobject owner, + const char *callbackName, const char *callbackSig) +// Create and initialize a new OpContext. +{ + OpContext *pContext = (OpContext*) malloc( sizeof *pContext); + + if ( pContext != NULL) + { + jfieldID clientField = (*pEnv)->GetFieldID( pEnv, (*pEnv)->GetObjectClass( pEnv, owner), + "fListener", "Lcom/apple/dnssd/BaseListener;"); + + pContext->JavaObj = (*pEnv)->NewWeakGlobalRef( pEnv, owner); // must convert local ref to global to cache; + pContext->ClientObj = (*pEnv)->GetObjectField( pEnv, owner, clientField); + pContext->ClientObj = (*pEnv)->NewWeakGlobalRef( pEnv, pContext->ClientObj); // must convert local ref to global to cache + pContext->Callback = (*pEnv)->GetMethodID( pEnv, + (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), + callbackName, callbackSig); + pContext->Callback2 = NULL; // not always used + } + + return pContext; +} + + +static void ReportError( JNIEnv *pEnv, jobject target, jobject service, DNSServiceErrorType err) +// Invoke operationFailed() method on target with err. +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, target); + jmethodID opFailed = (*pEnv)->GetMethodID( pEnv, cls, "operationFailed", + "(Lcom/apple/dnssd/DNSSDService;I)V"); + + (*pEnv)->CallVoidMethod( pEnv, target, opFailed, service, err); +} + +JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleService_HaltOperation( JNIEnv *pEnv, jobject pThis) +/* Deallocate the dns_sd service browser and set the Java object's fNativeContext field to 0. */ +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + + if ( contextField != 0) + { + OpContext *pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField); + if ( pContext != NULL) + { + // MUST clear fNativeContext first, BEFORE calling DNSServiceRefDeallocate() + (*pEnv)->SetLongField( pEnv, pThis, contextField, 0); + if ( pContext->ServiceRef != NULL) + DNSServiceRefDeallocate( pContext->ServiceRef); + + (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->JavaObj); + (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->ClientObj); + free( pContext); + } + } +} + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_BlockForData( JNIEnv *pEnv, jobject pThis) +/* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */ +{ +// BlockForData() not supported with AUTO_CALLBACKS +#if !AUTO_CALLBACKS + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + + if ( contextField != 0) + { + OpContext *pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField); + if ( pContext != NULL) + { + fd_set readFDs; + int sd = DNSServiceRefSockFD( pContext->ServiceRef); + struct timeval timeout = { 1, 0 }; + FD_ZERO( &readFDs); + FD_SET( sd, &readFDs); + + // Q: Why do we poll here? + // A: Because there's no other thread-safe way to do it. + // Mac OS X terminates a select() call if you close one of the sockets it's listening on, but Linux does not, + // and arguably Linux is correct (See <http://www.ussg.iu.edu/hypermail/linux/kernel/0405.1/0418.html>) + // The problem is that the Mac OS X behaviour assumes that it's okay for one thread to close a socket while + // some other thread is monitoring that socket in select(), but the difficulty is that there's no general way + // to make that thread-safe, because there's no atomic way to enter select() and release a lock simultaneously. + // If we try to do this without holding any lock, then right as we jump to the select() routine, + // some other thread could stop our operation (thereby closing the socket), + // and then that thread (or even some third, unrelated thread) + // could do some other DNS-SD operation (or some other operation that opens a new file descriptor) + // and then we'd blindly resume our fall into the select() call, now blocking on a file descriptor + // that may coincidentally have the same numerical value, but is semantically unrelated + // to the true file descriptor we thought we were blocking on. + // We can't stop this race condition from happening, but at least if we wake up once a second we can detect + // when fNativeContext has gone to zero, and thereby discover that we were blocking on the wrong fd. + + if (select( sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout) == 1) return(1); + } + } +#endif // !AUTO_CALLBACKS + return(0); +} + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_ProcessResults( JNIEnv *pEnv, jobject pThis) +/* Call through to DNSServiceProcessResult() while data remains on socket. */ +{ +#if !AUTO_CALLBACKS // ProcessResults() not supported with AUTO_CALLBACKS + + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField); + DNSServiceErrorType err = kDNSServiceErr_BadState; + + if ( pContext != NULL) + { + int sd = DNSServiceRefSockFD( pContext->ServiceRef); + fd_set readFDs; + struct timeval zeroTimeout = { 0, 0 }; + + pContext->Env = pEnv; + + FD_ZERO( &readFDs); + FD_SET( sd, &readFDs); + + err = kDNSServiceErr_NoError; + if (0 < select(sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout)) + { + err = DNSServiceProcessResult(pContext->ServiceRef); + // Use caution here! + // We cannot touch any data structures associated with this operation! + // The DNSServiceProcessResult() routine should have invoked our callback, + // and our callback could have terminated the operation with op.stop(); + // and that means HaltOperation() will have been called, which frees pContext. + // Basically, from here we just have to get out without touching any stale + // data structures that could blow up on us! Particularly, any attempt + // to loop here reading more results from the file descriptor is unsafe. + } + } + return err; +#endif // AUTO_CALLBACKS +} + + +static void DNSSD_API ServiceBrowseReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, + const char *replyDomain, void *context) +{ + OpContext *pContext = (OpContext*) context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, + ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), + (*pContext->Env)->NewStringUTF( pContext->Env, regtype), + (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain)); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleBrowser_CreateBrowser( JNIEnv *pEnv, jobject pThis, + jint flags, jint ifIndex, jstring regType, jstring domain) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "serviceFound", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *regStr = SafeGetUTFChars( pEnv, regType); + const char *domainStr = SafeGetUTFChars( pEnv, domain); + + pContext->Callback2 = (*pEnv)->GetMethodID( pEnv, + (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), + "serviceLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + + err = DNSServiceBrowse( &pContext->ServiceRef, flags, ifIndex, regStr, domainStr, ServiceBrowseReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + + SafeReleaseUTFChars( pEnv, regType, regStr); + SafeReleaseUTFChars( pEnv, domain, domainStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + + +static void DNSSD_API ServiceResolveReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, + uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context) +{ + OpContext *pContext = (OpContext*) context; + jclass txtCls; + jmethodID txtCtor; + jbyteArray txtBytes; + jobject txtObj; + jbyte *pBytes; + + SetupCallbackState( &pContext->Env); + + txtCls = (*pContext->Env)->FindClass( pContext->Env, "com/apple/dnssd/TXTRecord"); + txtCtor = (*pContext->Env)->GetMethodID( pContext->Env, txtCls, "<init>", "([B)V"); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL && txtCtor != NULL && + NULL != ( txtBytes = (*pContext->Env)->NewByteArray( pContext->Env, txtLen))) + { + if ( errorCode == kDNSServiceErr_NoError) + { + // Since Java ints are defined to be big-endian, we canonicalize 'port' from a 16-bit + // pattern into a number here. + port = ( ((unsigned char*) &port)[0] << 8) | ((unsigned char*) &port)[1]; + + // Initialize txtBytes with contents of txtRecord + pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, txtBytes, NULL); + memcpy( pBytes, txtRecord, txtLen); + (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, txtBytes, pBytes, JNI_COMMIT); + + // Construct txtObj with txtBytes + txtObj = (*pContext->Env)->NewObject( pContext->Env, txtCls, txtCtor, txtBytes); + (*pContext->Env)->DeleteLocalRef( pContext->Env, txtBytes); + + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, fullname), + (*pContext->Env)->NewStringUTF( pContext->Env, hosttarget), + port, txtObj); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleResolver_CreateResolver( JNIEnv *pEnv, jobject pThis, + jint flags, jint ifIndex, jstring serviceName, jstring regType, jstring domain) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "serviceResolved", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;ILcom/apple/dnssd/TXTRecord;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *servStr = SafeGetUTFChars( pEnv, serviceName); + const char *regStr = SafeGetUTFChars( pEnv, regType); + const char *domainStr = SafeGetUTFChars( pEnv, domain); + + err = DNSServiceResolve( &pContext->ServiceRef, flags, ifIndex, + servStr, regStr, domainStr, ServiceResolveReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + + SafeReleaseUTFChars( pEnv, serviceName, servStr); + SafeReleaseUTFChars( pEnv, regType, regStr); + SafeReleaseUTFChars( pEnv, domain, domainStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + + +static void DNSSD_API ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, + DNSServiceErrorType errorCode, const char *serviceName, + const char *regType, const char *domain, void *context) +{ + OpContext *pContext = (OpContext*) context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + pContext->JavaObj, flags, + (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), + (*pContext->Env)->NewStringUTF( pContext->Env, regType), + (*pContext->Env)->NewStringUTF( pContext->Env, domain)); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_BeginRegister( JNIEnv *pEnv, jobject pThis, + jint ifIndex, jint flags, jstring serviceName, jstring regType, + jstring domain, jstring host, jint port, jbyteArray txtRecord) +{ + //syslog(LOG_ERR, "BR"); + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + + //syslog(LOG_ERR, "BR: contextField %d", contextField); + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "serviceRegistered", + "(Lcom/apple/dnssd/DNSSDRegistration;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *servStr = SafeGetUTFChars( pEnv, serviceName); + const char *regStr = SafeGetUTFChars( pEnv, regType); + const char *domainStr = SafeGetUTFChars( pEnv, domain); + const char *hostStr = SafeGetUTFChars( pEnv, host); + + //syslog(LOG_ERR, "BR: regStr %s", regStr); + + // Since Java ints are defined to be big-endian, we de-canonicalize 'port' from a + // big-endian number into a 16-bit pattern here. + uint16_t portBits = port; + portBits = ( ((unsigned char*) &portBits)[0] << 8) | ((unsigned char*) &portBits)[1]; + + pBytes = txtRecord ? (*pEnv)->GetByteArrayElements( pEnv, txtRecord, NULL) : NULL; + numBytes = txtRecord ? (*pEnv)->GetArrayLength( pEnv, txtRecord) : 0; + + err = DNSServiceRegister( &pContext->ServiceRef, flags, ifIndex, servStr, regStr, + domainStr, hostStr, portBits, + numBytes, pBytes, ServiceRegisterReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, txtRecord, pBytes, 0); + + SafeReleaseUTFChars( pEnv, serviceName, servStr); + SafeReleaseUTFChars( pEnv, regType, regStr); + SafeReleaseUTFChars( pEnv, domain, domainStr); + SafeReleaseUTFChars( pEnv, host, hostStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_AddRecord( JNIEnv *pEnv, jobject pThis, + jint flags, jint rrType, jbyteArray rData, jint ttl, jobject destObj) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + DNSRecordRef recRef; + + if ( contextField != 0) + pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField); + if ( pContext == NULL || pContext->ServiceRef == NULL) + return kDNSServiceErr_BadParam; + + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rData); + + err = DNSServiceAddRecord( pContext->ServiceRef, &recRef, flags, rrType, numBytes, pBytes, ttl); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, destObj, recField, (jlong) recRef); + } + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); + + return err; +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Update( JNIEnv *pEnv, jobject pThis, + jint flags, jbyteArray rData, jint ttl) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + DNSRecordRef recRef = NULL; + + if ( ownerField != 0) + { + jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); + jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J"); + if ( contextField != 0) + pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, ownerObj, contextField); + } + if ( recField != 0) + recRef = (DNSRecordRef) (*pEnv)->GetLongField( pEnv, pThis, recField); + if ( pContext == NULL || pContext->ServiceRef == NULL) + return kDNSServiceErr_BadParam; + + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rData); + + err = DNSServiceUpdateRecord( pContext->ServiceRef, recRef, flags, numBytes, pBytes, ttl); + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); + + return err; +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Remove( JNIEnv *pEnv, jobject pThis) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;"); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + DNSRecordRef recRef = NULL; + + if ( ownerField != 0) + { + jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField); + jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J"); + if ( contextField != 0) + pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, ownerObj, contextField); + } + if ( recField != 0) + recRef = (DNSRecordRef) (*pEnv)->GetLongField( pEnv, pThis, recField); + if ( pContext == NULL || pContext->ServiceRef == NULL) + return kDNSServiceErr_BadParam; + + err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, 0); + + return err; +} + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_CreateConnection( JNIEnv *pEnv, jobject pThis) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "recordRegistered", "(Lcom/apple/dnssd/DNSRecord;I)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + err = DNSServiceCreateConnection( &pContext->ServiceRef); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + +struct RecordRegistrationRef +{ + OpContext *Context; + jobject RecordObj; +}; +typedef struct RecordRegistrationRef RecordRegistrationRef; + +static void DNSSD_API RegisterRecordReply( DNSServiceRef sdRef _UNUSED, + DNSRecordRef recordRef _UNUSED, DNSServiceFlags flags, + DNSServiceErrorType errorCode, void *context) +{ + RecordRegistrationRef *regEnvelope = (RecordRegistrationRef*) context; + OpContext *pContext = regEnvelope->Context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + regEnvelope->RecordObj, flags); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + + (*pContext->Env)->DeleteWeakGlobalRef( pContext->Env, regEnvelope->RecordObj); + free( regEnvelope); + + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord( JNIEnv *pEnv, jobject pThis, + jint flags, jint ifIndex, jstring fullname, jint rrType, jint rrClass, + jbyteArray rData, jint ttl, jobject destObj) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj); + jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J"); + const char *nameStr = SafeGetUTFChars( pEnv, fullname); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + jbyte *pBytes; + jsize numBytes; + DNSRecordRef recRef; + RecordRegistrationRef *regEnvelope; + + if ( contextField != 0) + pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField); + if ( pContext == NULL || pContext->ServiceRef == NULL || nameStr == NULL) + return kDNSServiceErr_BadParam; + + regEnvelope = calloc( 1, sizeof *regEnvelope); + if ( regEnvelope == NULL) + return kDNSServiceErr_NoMemory; + regEnvelope->Context = pContext; + regEnvelope->RecordObj = (*pEnv)->NewWeakGlobalRef( pEnv, destObj); // must convert local ref to global to cache + + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rData); + + err = DNSServiceRegisterRecord( pContext->ServiceRef, &recRef, flags, ifIndex, + nameStr, rrType, rrClass, numBytes, pBytes, ttl, + RegisterRecordReply, regEnvelope); + + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, destObj, recField, (jlong) recRef); + } + else + { + if ( regEnvelope->RecordObj != NULL) + (*pEnv)->DeleteWeakGlobalRef( pEnv, regEnvelope->RecordObj); + free( regEnvelope); + } + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0); + + SafeReleaseUTFChars( pEnv, fullname, nameStr); + + return err; +} + + +static void DNSSD_API ServiceQueryReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *serviceName, + uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, + const void *rdata, uint32_t ttl, void *context) +{ + OpContext *pContext = (OpContext*) context; + jbyteArray rDataObj; + jbyte *pBytes; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL && + NULL != ( rDataObj = (*pContext->Env)->NewByteArray( pContext->Env, rdlen))) + { + if ( errorCode == kDNSServiceErr_NoError) + { + // Initialize rDataObj with contents of rdata + pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, rDataObj, NULL); + memcpy( pBytes, rdata, rdlen); + (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, rDataObj, pBytes, JNI_COMMIT); + + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, serviceName), + rrtype, rrclass, rDataObj, ttl); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleQuery_CreateQuery( JNIEnv *pEnv, jobject pThis, + jint flags, jint ifIndex, jstring serviceName, jint rrtype, jint rrclass) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "queryAnswered", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;II[BI)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + const char *servStr = SafeGetUTFChars( pEnv, serviceName); + + err = DNSServiceQueryRecord( &pContext->ServiceRef, flags, ifIndex, servStr, + rrtype, rrclass, ServiceQueryReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + + SafeReleaseUTFChars( pEnv, serviceName, servStr); + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + + +static void DNSSD_API DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context) +{ + OpContext *pContext = (OpContext*) context; + + SetupCallbackState( &pContext->Env); + + if ( pContext->ClientObj != NULL && pContext->Callback != NULL) + { + if ( errorCode == kDNSServiceErr_NoError) + { + (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, + ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2, + pContext->JavaObj, flags, interfaceIndex, + (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain)); + } + else + ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode); + } + TeardownCallbackState(); +} + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDomainEnum_BeginEnum( JNIEnv *pEnv, jobject pThis, + jint flags, jint ifIndex) +{ + jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis); + jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J"); + OpContext *pContext = NULL; + DNSServiceErrorType err = kDNSServiceErr_NoError; + + if ( contextField != 0) + pContext = NewContext( pEnv, pThis, "domainFound", + "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V"); + else + err = kDNSServiceErr_BadParam; + + if ( pContext != NULL) + { + pContext->Callback2 = (*pEnv)->GetMethodID( pEnv, + (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj), + "domainLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V"); + + err = DNSServiceEnumerateDomains( &pContext->ServiceRef, flags, ifIndex, + DomainEnumReply, pContext); + if ( err == kDNSServiceErr_NoError) + { + (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext); + } + } + else + err = kDNSServiceErr_NoMemory; + + return err; +} + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_ConstructName( JNIEnv *pEnv, jobject pThis _UNUSED, + jstring serviceName, jstring regtype, jstring domain, jobjectArray pOut) +{ + DNSServiceErrorType err = kDNSServiceErr_NoError; + const char *nameStr = SafeGetUTFChars( pEnv, serviceName); + const char *regStr = SafeGetUTFChars( pEnv, regtype); + const char *domStr = SafeGetUTFChars( pEnv, domain); + char buff[ kDNSServiceMaxDomainName + 1]; + + err = DNSServiceConstructFullName( buff, nameStr, regStr, domStr); + + if ( err == kDNSServiceErr_NoError) + { + // pOut is expected to be a String[1] array. + (*pEnv)->SetObjectArrayElement( pEnv, pOut, 0, (*pEnv)->NewStringUTF( pEnv, buff)); + } + + SafeReleaseUTFChars( pEnv, serviceName, nameStr); + SafeReleaseUTFChars( pEnv, regtype, regStr); + SafeReleaseUTFChars( pEnv, domain, domStr); + + return err; +} + +JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord( JNIEnv *pEnv, jobject pThis _UNUSED, + jint flags, jint ifIndex, jstring fullName, + jint rrtype, jint rrclass, jbyteArray rdata) +{ + jbyte *pBytes; + jsize numBytes; + const char *nameStr = SafeGetUTFChars( pEnv, fullName); + + pBytes = (*pEnv)->GetByteArrayElements( pEnv, rdata, NULL); + numBytes = (*pEnv)->GetArrayLength( pEnv, rdata); + + DNSServiceReconfirmRecord( flags, ifIndex, nameStr, rrtype, rrclass, numBytes, pBytes); + + if ( pBytes != NULL) + (*pEnv)->ReleaseByteArrayElements( pEnv, rdata, pBytes, 0); + + SafeReleaseUTFChars( pEnv, fullName, nameStr); +} + +#define LOCAL_ONLY_NAME "loo" + +JNIEXPORT jstring JNICALL Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex( JNIEnv *pEnv, jobject pThis _UNUSED, + jint ifIndex) +{ + char *p = LOCAL_ONLY_NAME, nameBuff[IF_NAMESIZE]; + + if (ifIndex != (jint) kDNSServiceInterfaceIndexLocalOnly) + p = if_indextoname( ifIndex, nameBuff ); + + return (*pEnv)->NewStringUTF( pEnv, p); +} + + +JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName( JNIEnv *pEnv, jobject pThis _UNUSED, + jstring ifName) +{ + uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly; + const char *nameStr = SafeGetUTFChars( pEnv, ifName); + + if (strcmp(nameStr, LOCAL_ONLY_NAME)) + ifIndex = if_nametoindex( nameStr); + + SafeReleaseUTFChars( pEnv, ifName, nameStr); + + return ifIndex; +} + + +#if defined(_WIN32) +static char* +if_indextoname( DWORD ifIndex, char * nameBuff) +{ + PIP_ADAPTER_INFO pAdapterInfo = NULL; + PIP_ADAPTER_INFO pAdapter = NULL; + DWORD dwRetVal = 0; + char * ifName = NULL; + ULONG ulOutBufLen = 0; + + if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW) + { + goto exit; + } + + pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); + + if (pAdapterInfo == NULL) + { + goto exit; + } + + dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen ); + + if (dwRetVal != NO_ERROR) + { + goto exit; + } + + pAdapter = pAdapterInfo; + while (pAdapter) + { + if (pAdapter->Index == ifIndex) + { + // It would be better if we passed in the length of nameBuff to this + // function, so we would have absolute certainty that no buffer + // overflows would occur. Buffer overflows *shouldn't* occur because + // nameBuff is of size MAX_ADAPTER_NAME_LENGTH. + strcpy( nameBuff, pAdapter->AdapterName ); + ifName = nameBuff; + break; + } + + pAdapter = pAdapter->Next; + } + +exit: + + if (pAdapterInfo != NULL) + { + free( pAdapterInfo ); + pAdapterInfo = NULL; + } + + return ifName; +} + + +static DWORD +if_nametoindex( const char * nameStr ) +{ + PIP_ADAPTER_INFO pAdapterInfo = NULL; + PIP_ADAPTER_INFO pAdapter = NULL; + DWORD dwRetVal = 0; + DWORD ifIndex = 0; + ULONG ulOutBufLen = 0; + + if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW) + { + goto exit; + } + + pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); + + if (pAdapterInfo == NULL) + { + goto exit; + } + + dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen ); + + if (dwRetVal != NO_ERROR) + { + goto exit; + } + + pAdapter = pAdapterInfo; + while (pAdapter) + { + if (strcmp(pAdapter->AdapterName, nameStr) == 0) + { + ifIndex = pAdapter->Index; + break; + } + + pAdapter = pAdapter->Next; + } + +exit: + + if (pAdapterInfo != NULL) + { + free( pAdapterInfo ); + pAdapterInfo = NULL; + } + + return ifIndex; +} +#endif diff --git a/usr/src/lib/libdns_sd/java/common/mapfile-vers b/usr/src/lib/libdns_sd/java/common/mapfile-vers new file mode 100644 index 0000000000..0ae50ad6ff --- /dev/null +++ b/usr/src/lib/libdns_sd/java/common/mapfile-vers @@ -0,0 +1,48 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +SUNW_1.1 { + global: + Java_com_apple_dnssd_AppleBrowser_CreateBrowser; + Java_com_apple_dnssd_AppleDNSRecord_Remove; + Java_com_apple_dnssd_AppleDNSRecord_Update; + Java_com_apple_dnssd_AppleDNSSD_ConstructName; + Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName; + Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex; + Java_com_apple_dnssd_AppleDNSSD_InitLibrary; + Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord; + Java_com_apple_dnssd_AppleDomainEnum_BeginEnum; + Java_com_apple_dnssd_AppleQuery_CreateQuery; + Java_com_apple_dnssd_AppleRecordRegistrar_CreateConnection; + Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord; + Java_com_apple_dnssd_AppleRegistration_AddRecord; + Java_com_apple_dnssd_AppleRegistration_BeginRegister; + Java_com_apple_dnssd_AppleResolver_CreateResolver; + Java_com_apple_dnssd_AppleService_BlockForData; + Java_com_apple_dnssd_AppleService_HaltOperation; + Java_com_apple_dnssd_AppleService_ProcessResults; + local: + *; +}; diff --git a/usr/src/lib/libdns_sd/java/i386/Makefile b/usr/src/lib/libdns_sd/java/i386/Makefile new file mode 100644 index 0000000000..3b6c2a1943 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/i386/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +.KEEP_STATE: + +CPPFLAGS += -_gcc=-Wno-pointer-to-int-cast -_gcc=-Wno-int-to-pointer-cast + +all: $(LIBS) + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/libdns_sd/java/sparc/Makefile b/usr/src/lib/libdns_sd/java/sparc/Makefile new file mode 100644 index 0000000000..3b6c2a1943 --- /dev/null +++ b/usr/src/lib/libdns_sd/java/sparc/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +.KEEP_STATE: + +CPPFLAGS += -_gcc=-Wno-pointer-to-int-cast -_gcc=-Wno-int-to-pointer-cast + +all: $(LIBS) + +install: all $(ROOTLIBS) $(ROOTLINKS) diff --git a/usr/src/lib/libdns_sd/java/sparcv9/Makefile b/usr/src/lib/libdns_sd/java/sparcv9/Makefile new file mode 100644 index 0000000000..b645e1caed --- /dev/null +++ b/usr/src/lib/libdns_sd/java/sparcv9/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com +include ../../../Makefile.lib.64 + +all: $(LIBS) + +install: all $(ROOTLIBS64) $(ROOTLINKS64) diff --git a/usr/src/lib/libdns_sd/sparc/Makefile b/usr/src/lib/libdns_sd/sparc/Makefile new file mode 100644 index 0000000000..9c1be5bf3a --- /dev/null +++ b/usr/src/lib/libdns_sd/sparc/Makefile @@ -0,0 +1,28 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com + +install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT) diff --git a/usr/src/lib/libdns_sd/sparcv9/Makefile b/usr/src/lib/libdns_sd/sparcv9/Makefile new file mode 100644 index 0000000000..abae92c693 --- /dev/null +++ b/usr/src/lib/libdns_sd/sparcv9/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" + +include ../Makefile.com +include ../../Makefile.lib.64 + +CFLAGS64 += -erroff=E_ASSIGNMENT_TYPE_MISMATCH + +.KEEP_STATE: + +all: $(LIBS) + +install: all $(ROOTLIBS64) $(ROOTLINKS64) |