/* -*- 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
* 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
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
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
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>
// disable warning: "'type cast' : from data pointer 'void *' to function pointer"
extern BOOL IsSystemServiceDisabled();
static int g_initWinsock = 0;
#else
#include <syslog.h>
#endif
// <rdar://problem/4096913> Specifies how many times we'll try and connect to the
// server.
// 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
{
void *app_callback;
void *app_context;
typedef struct _DNSRecordRef_t
{
void *app_context;
// exported functions
// write len bytes. return 0 on success, -1 on error
{
// 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)
{
buf += num_written;
len -= num_written;
}
return 0;
}
// read len bytes. return 0 on success, -1 on error
{
// 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)
{
continue;
// Return error -2 when no data received and errno is not set
if (num_read == 0) return -2;
}
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.
*/
{
int datalen;
#if !defined(USE_TCP_LOOPBACK)
#endif
if (!reuse_socket)
{
#if defined(USE_TCP_LOOPBACK)
#else
#endif
}
*len += sizeof(ipc_msg_hdr);
// write message to buffer
#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().
#else
#endif
return hdr;
}
// return a connected service ref (deallocate with DNSServiceRefDeallocate)
{
int NumTries = 0;
#if defined(_WIN32)
if (!g_initWinsock)
{
g_initWinsock = 1;
}
// <rdar://problem/4096913> If the system service is disabled, we only want to try
// to connect once
if ( IsSystemServiceDisabled() )
{
}
#endif
#if defined(USE_TCP_LOOPBACK)
#else
#endif
while (1)
{
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)
else
{
return NULL;
}
}
return sdr;
}
{
int ret;
if (!reuse_sd)
{
// setup temporary error socket
goto cleanup;
#if defined(USE_TCP_LOOPBACK)
{
}
#else
{
// 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
#endif
//syslog(LOG_WARNING, "deliver_request: creating UDS: %s\n", data);
}
#endif
}
//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));
goto cleanup;
else
{
//syslog(LOG_WARNING, "deliver_request: accept\n");
//syslog(LOG_WARNING, "deliver_request: accept returned %d\n", errsd);
}
else
//syslog(LOG_WARNING, "deliver_request: retrieved error code %d\n", err);
if (!reuse_sd)
{
#if !defined(USE_TCP_LOOPBACK)
// syslog(LOG_WARNING, "deliver_request: removing UDS: %s\n", data);
// else syslog(LOG_WARNING, "deliver_request: removed UDS: %s\n", data);
#endif
}
return err;
}
{
if (!sdRef) return -1;
}
// 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.
{
char *data;
int rderr;
return kDNSServiceErr_BadReference;
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
return kDNSServiceErr_NoError;
return kDNSServiceErr_Unknown;
}
return kDNSServiceErr_Incompatible;
if (!data) return kDNSServiceErr_NoMemory;
return kDNSServiceErr_Unknown;
return kDNSServiceErr_NoError;
}
{
if (!sdRef) return;
}
{
unsigned char *txtrecord;
int str_error = 0;
(void)hdr; //unused
((DNSServiceResolveReply)sdr->app_callback)(sdr, flags, ifi, err, fullname, target, port.s, txtlen, txtrecord, sdr->app_context);
}
(
const char *name,
const char *regtype,
const char *domain,
void *context
)
{
if (!sdRef) return kDNSServiceErr_BadParam;
// calculate total message length
len += sizeof(interfaceIndex);
sdr = connect_to_server();
if (err)
{
return err;
}
return err;
return kDNSServiceErr_Unknown;
}
{
char *rdata;
int str_error = 0;
(void)hdr;//Unused
((DNSServiceQueryRecordReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, name, rrtype, rrclass,
return;
}
(
const char *name,
void *context
)
{
if (!sdRef) return kDNSServiceErr_BadParam;
// calculate total message length
sdr = connect_to_server();
if (err)
{
return err;
}
return err;
return kDNSServiceErr_Unknown;
}
{
int str_error = 0;
(void)hdr;//Unused
((DNSServiceBrowseReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, replyName, replyType, replyDomain, sdr->app_context);
}
(
const char *regtype,
const char *domain,
void *context
)
{
if (!sdRef) return kDNSServiceErr_BadParam;
len += sizeof(interfaceIndex);
sdr = connect_to_server();
if (err)
{
return err;
}
return err;
return kDNSServiceErr_Unknown;
}
(
const char *domain
)
{
if (!hdr) return kDNSServiceErr_Unknown;
sdr = connect_to_server();
return err;
}
{
int str_error = 0;
(void)hdr;//Unused
((DNSServiceRegisterReply)sdr->app_callback)(sdr, flags, errorCode, name, regtype, domain, sdr->app_context);
}
(
const char *name,
const char *regtype,
const char *domain,
const char *host,
const void *txtRecord,
void *context
)
{
if (!sdRef) return kDNSServiceErr_BadParam;
if (!regtype) return kDNSServiceErr_BadParam;
// auto-name must also have auto-rename
return kDNSServiceErr_BadParam;
// no callback must have auto-rename
len = sizeof(DNSServiceFlags);
sdr = connect_to_server();
if (err)
{
return err;
}
return err;
return kDNSServiceErr_Unknown;
}
{
int str_error = 0;
(void)hdr;//Unused
((DNSServiceDomainEnumReply)sdr->app_callback)(sdr, flags, interfaceIndex, err, domain, sdr->app_context);
}
(
void *context
)
{
if (!sdRef) return kDNSServiceErr_BadParam;
len = sizeof(DNSServiceFlags);
sdr = connect_to_server();
if (err)
{
return err;
}
return err;
return kDNSServiceErr_Unknown;
}
{
{
return;
}
}
{
if (!sdRef) return kDNSServiceErr_BadParam;
*sdRef = connect_to_server();
if (!*sdRef)
return kDNSServiceErr_Unknown;
return 0;
}
(
const char *fullname,
const void *rdata,
void *context
)
{
return kDNSServiceErr_BadReference;
len = sizeof(DNSServiceFlags);
return kDNSServiceErr_Unknown;
}
//sdRef returned by DNSServiceRegister()
(
const void *rdata,
)
{
char *ptr;
return kDNSServiceErr_BadReference;
len += sizeof(DNSServiceFlags);
if (!hdr) return kDNSServiceErr_Unknown;
return kDNSServiceErr_Unknown;
}
//DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord
(
const void *rdata,
)
{
char *ptr;
if (!sdRef) return kDNSServiceErr_BadReference;
len += sizeof(DNSServiceFlags);
if (!hdr) return kDNSServiceErr_Unknown;
}
(
)
{
char *ptr;
return kDNSServiceErr_BadReference;
if (!hdr) return kDNSServiceErr_Unknown;
return err;
}
(
const char *fullname,
const void *rdata
)
{
char *ptr;
len = sizeof(DNSServiceFlags);
tmp = connect_to_server();
if (!tmp) return(kDNSServiceErr_Unknown);
if (!hdr) return(kDNSServiceErr_Unknown);
return(kDNSServiceErr_NoError);
}