blob: e6297320014a7250e90d83c2aa3dd4420a4ee7bd [file] [log] [blame]
/* -*- Mode: C; tab-width: 4 -*-
*
* Copyright (c) 2002 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.
*/
// Suppress "warning: 'DNSServiceDiscoveryMachPort' is deprecated" messages -- we already know this code is building the deprecated API
// Since we compile with all warnings treated as errors, we have to turn off the warnings here or the project won't compile
#include <AvailabilityMacros.h>
#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED
#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED
#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
#include "DNSServiceDiscovery.h"
#include "DNSServiceDiscoveryDefines.h"
#include "DNSServiceDiscoveryReplyServer.h"
#include <stdlib.h>
#include <stdio.h>
#include <servers/bootstrap.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <pthread.h>
#include <netinet/in.h>
extern boolean_t DNSServiceDiscoveryReply_server(
mach_msg_header_t *InHeadP,
mach_msg_header_t *OutHeadP);
extern
kern_return_t DNSServiceBrowserCreate_rpc
(
mach_port_t server,
mach_port_t client,
DNSCString regtype,
DNSCString domain
);
extern
kern_return_t DNSServiceDomainEnumerationCreate_rpc
(
mach_port_t server,
mach_port_t client,
int registrationDomains
);
extern
kern_return_t DNSServiceRegistrationCreate_rpc
(
mach_port_t server,
mach_port_t client,
DNSCString name,
DNSCString regtype,
DNSCString domain,
IPPort port,
DNSCString txtRecord
);
extern
kern_return_t DNSServiceResolverResolve_rpc
(
mach_port_t server,
mach_port_t client,
DNSCString name,
DNSCString regtype,
DNSCString domain
);
extern
kern_return_t DNSServiceRegistrationAddRecord_rpc
(
mach_port_t server,
mach_port_t client,
int type,
record_data_t data,
mach_msg_type_number_t record_dataCnt,
uint32_t ttl,
natural_t *reference
);
extern
int DNSServiceRegistrationUpdateRecord_rpc
(
mach_port_t server,
mach_port_t client,
natural_t reference,
record_data_t data,
mach_msg_type_number_t record_dataCnt,
uint32_t ttl
);
extern
kern_return_t DNSServiceRegistrationRemoveRecord_rpc
(
mach_port_t server,
mach_port_t client,
natural_t reference
);
struct a_requests {
struct a_requests *next;
mach_port_t client_port;
union {
DNSServiceBrowserReply browserCallback;
DNSServiceDomainEnumerationReply enumCallback;
DNSServiceRegistrationReply regCallback;
DNSServiceResolverReply resolveCallback;
} callout;
void *context;
};
static struct a_requests *a_requests = NULL;
static pthread_mutex_t a_requests_lock = PTHREAD_MUTEX_INITIALIZER;
typedef struct _dns_service_discovery_t {
mach_port_t port;
} dns_service_discovery_t;
static mach_port_t DNSServiceDiscoveryLookupServer(void)
{
static mach_port_t sndPort = MACH_PORT_NULL;
kern_return_t result;
if (sndPort != MACH_PORT_NULL) {
return sndPort;
}
result = bootstrap_look_up(bootstrap_port, DNS_SERVICE_DISCOVERY_SERVER, &sndPort);
if (result != KERN_SUCCESS) {
printf("%s(): {%s:%d} bootstrap_look_up() failed: $%x\n", __FUNCTION__, __FILE__, __LINE__, (int) result);
sndPort = MACH_PORT_NULL;
}
return sndPort;
}
static void _increaseQueueLengthOnPort(mach_port_t port)
{
mach_port_limits_t qlimits;
kern_return_t result;
qlimits.mpl_qlimit = 16;
result = mach_port_set_attributes(mach_task_self(), port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&qlimits, MACH_PORT_LIMITS_INFO_COUNT);
if (result != KERN_SUCCESS) {
printf("%s(): {%s:%d} mach_port_set_attributes() failed: $%x %s\n", __FUNCTION__, __FILE__, __LINE__, (int) result, mach_error_string(result));
}
}
dns_service_discovery_ref DNSServiceBrowserCreate (const char *regtype, const char *domain, DNSServiceBrowserReply callBack,void *context)
{
mach_port_t serverPort = DNSServiceDiscoveryLookupServer();
mach_port_t clientPort;
kern_return_t result;
dns_service_discovery_ref return_t;
struct a_requests *request;
if (!serverPort) {
return NULL;
}
result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort);
if (result != KERN_SUCCESS) {
printf("Mach port receive creation failed, %s\n", mach_error_string(result));
return NULL;
}
result = mach_port_insert_right(mach_task_self(), clientPort, clientPort, MACH_MSG_TYPE_MAKE_SEND);
if (result != KERN_SUCCESS) {
printf("Mach port send creation failed, %s\n", mach_error_string(result));
mach_port_destroy(mach_task_self(), clientPort);
return NULL;
}
_increaseQueueLengthOnPort(clientPort);
return_t = malloc(sizeof(dns_service_discovery_t));
return_t->port = clientPort;
request = malloc(sizeof(struct a_requests));
request->client_port = clientPort;
request->context = context;
request->callout.browserCallback = callBack;
result = DNSServiceBrowserCreate_rpc(serverPort, clientPort, (char *)regtype, (char *)domain);
if (result != KERN_SUCCESS) {
printf("There was an error creating a browser, %s\n", mach_error_string(result));
free(request);
return NULL;
}
pthread_mutex_lock(&a_requests_lock);
request->next = a_requests;
a_requests = request;
pthread_mutex_unlock(&a_requests_lock);
return return_t;
}
/* Service Enumeration */
dns_service_discovery_ref DNSServiceDomainEnumerationCreate (int registrationDomains, DNSServiceDomainEnumerationReply callBack, void *context)
{
mach_port_t serverPort = DNSServiceDiscoveryLookupServer();
mach_port_t clientPort;
kern_return_t result;
dns_service_discovery_ref return_t;
struct a_requests *request;
if (!serverPort) {
return NULL;
}
result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort);
if (result != KERN_SUCCESS) {
printf("Mach port receive creation failed, %s\n", mach_error_string(result));
return NULL;
}
result = mach_port_insert_right(mach_task_self(), clientPort, clientPort, MACH_MSG_TYPE_MAKE_SEND);
if (result != KERN_SUCCESS) {
printf("Mach port send creation failed, %s\n", mach_error_string(result));
mach_port_destroy(mach_task_self(), clientPort);
return NULL;
}
_increaseQueueLengthOnPort(clientPort);
return_t = malloc(sizeof(dns_service_discovery_t));
return_t->port = clientPort;
request = malloc(sizeof(struct a_requests));
request->client_port = clientPort;
request->context = context;
request->callout.enumCallback = callBack;
result = DNSServiceDomainEnumerationCreate_rpc(serverPort, clientPort, registrationDomains);
if (result != KERN_SUCCESS) {
printf("There was an error creating an enumerator, %s\n", mach_error_string(result));
free(request);
return NULL;
}
pthread_mutex_lock(&a_requests_lock);
request->next = a_requests;
a_requests = request;
pthread_mutex_unlock(&a_requests_lock);
return return_t;
}
/* Service Registration */
dns_service_discovery_ref DNSServiceRegistrationCreate
(const char *name, const char *regtype, const char *domain, uint16_t port, const char *txtRecord, DNSServiceRegistrationReply callBack, void *context)
{
mach_port_t serverPort = DNSServiceDiscoveryLookupServer();
mach_port_t clientPort;
kern_return_t result;
dns_service_discovery_ref return_t;
struct a_requests *request;
IPPort IpPort;
char *portptr = (char *)&port;
if (!serverPort) {
return NULL;
}
if (!txtRecord) {
txtRecord = "";
}
result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort);
if (result != KERN_SUCCESS) {
printf("Mach port receive creation failed, %s\n", mach_error_string(result));
return NULL;
}
result = mach_port_insert_right(mach_task_self(), clientPort, clientPort, MACH_MSG_TYPE_MAKE_SEND);
if (result != KERN_SUCCESS) {
printf("Mach port send creation failed, %s\n", mach_error_string(result));
mach_port_destroy(mach_task_self(), clientPort);
return NULL;
}
_increaseQueueLengthOnPort(clientPort);
return_t = malloc(sizeof(dns_service_discovery_t));
return_t->port = clientPort;
request = malloc(sizeof(struct a_requests));
request->client_port = clientPort;
request->context = context;
request->callout.regCallback = callBack;
// older versions of this code passed the port via mach IPC as an int.
// we continue to pass it as 4 bytes to maintain binary compatibility,
// but now ensure that the network byte order is preserved by using a struct
IpPort.bytes[0] = 0;
IpPort.bytes[1] = 0;
IpPort.bytes[2] = portptr[0];
IpPort.bytes[3] = portptr[1];
result = DNSServiceRegistrationCreate_rpc(serverPort, clientPort, (char *)name, (char *)regtype, (char *)domain, IpPort, (char *)txtRecord);
if (result != KERN_SUCCESS) {
printf("There was an error creating a resolve, %s\n", mach_error_string(result));
free(request);
return NULL;
}
pthread_mutex_lock(&a_requests_lock);
request->next = a_requests;
a_requests = request;
pthread_mutex_unlock(&a_requests_lock);
return return_t;
}
/* Resolver requests */
dns_service_discovery_ref DNSServiceResolverResolve(const char *name, const char *regtype, const char *domain, DNSServiceResolverReply callBack, void *context)
{
mach_port_t serverPort = DNSServiceDiscoveryLookupServer();
mach_port_t clientPort;
kern_return_t result;
dns_service_discovery_ref return_t;
struct a_requests *request;
if (!serverPort) {
return NULL;
}
result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort);
if (result != KERN_SUCCESS) {
printf("Mach port receive creation failed, %s\n", mach_error_string(result));
return NULL;
}
result = mach_port_insert_right(mach_task_self(), clientPort, clientPort, MACH_MSG_TYPE_MAKE_SEND);
if (result != KERN_SUCCESS) {
printf("Mach port send creation failed, %s\n", mach_error_string(result));
mach_port_destroy(mach_task_self(), clientPort);
return NULL;
}
_increaseQueueLengthOnPort(clientPort);
return_t = malloc(sizeof(dns_service_discovery_t));
return_t->port = clientPort;
request = malloc(sizeof(struct a_requests));
request->client_port = clientPort;
request->context = context;
request->callout.resolveCallback = callBack;
DNSServiceResolverResolve_rpc(serverPort, clientPort, (char *)name, (char *)regtype, (char *)domain);
pthread_mutex_lock(&a_requests_lock);
request->next = a_requests;
a_requests = request;
pthread_mutex_unlock(&a_requests_lock);
return return_t;
}
DNSRecordReference DNSServiceRegistrationAddRecord(dns_service_discovery_ref ref, uint16_t rrtype, uint16_t rdlen, const char *rdata, uint32_t ttl)
{
mach_port_t serverPort = DNSServiceDiscoveryLookupServer();
mach_port_t clientPort;
natural_t reference = 0;
kern_return_t result = KERN_SUCCESS;
if (!serverPort) {
return kDNSServiceDiscoveryUnknownErr;
}
clientPort = DNSServiceDiscoveryMachPort(ref);
if (!clientPort) {
return kDNSServiceDiscoveryUnknownErr;
}
result = DNSServiceRegistrationAddRecord_rpc(serverPort, clientPort, rrtype, (record_data_t)rdata, rdlen, ttl, &reference);
if (result != KERN_SUCCESS) {
printf("The result of the registration was not successful. Error %d, result %s\n", result, mach_error_string(result));
}
return reference;
}
DNSServiceRegistrationReplyErrorType DNSServiceRegistrationUpdateRecord(dns_service_discovery_ref ref, DNSRecordReference reference, uint16_t rdlen, const char *rdata, uint32_t ttl)
{
mach_port_t serverPort = DNSServiceDiscoveryLookupServer();
mach_port_t clientPort;
kern_return_t result = KERN_SUCCESS;
if (!serverPort) {
return kDNSServiceDiscoveryUnknownErr;
}
clientPort = DNSServiceDiscoveryMachPort(ref);
if (!clientPort) {
return kDNSServiceDiscoveryUnknownErr;
}
result = DNSServiceRegistrationUpdateRecord_rpc(serverPort, clientPort, (natural_t)reference, (record_data_t)rdata, rdlen, ttl);
if (result != KERN_SUCCESS) {
printf("The result of the registration was not successful. Error %d, result %s\n", result, mach_error_string(result));
return result;
}
return kDNSServiceDiscoveryNoError;
}
DNSServiceRegistrationReplyErrorType DNSServiceRegistrationRemoveRecord(dns_service_discovery_ref ref, DNSRecordReference reference)
{
mach_port_t serverPort = DNSServiceDiscoveryLookupServer();
mach_port_t clientPort;
kern_return_t result = KERN_SUCCESS;
if (!serverPort) {
return kDNSServiceDiscoveryUnknownErr;
}
clientPort = DNSServiceDiscoveryMachPort(ref);
if (!clientPort) {
return kDNSServiceDiscoveryUnknownErr;
}
result = DNSServiceRegistrationRemoveRecord_rpc(serverPort, clientPort, (natural_t)reference);
if (result != KERN_SUCCESS) {
printf("The result of the registration was not successful. Error %d, result %s\n", result, mach_error_string(result));
return result;
}
return kDNSServiceDiscoveryNoError;
}
void DNSServiceDiscovery_handleReply(void *replyMsg)
{
unsigned long result = 0xFFFFFFFF;
mach_msg_header_t * msgSendBufPtr;
mach_msg_header_t * receivedMessage;
unsigned msgSendBufLength;
msgSendBufLength = internal_DNSServiceDiscoveryReply_subsystem.maxsize;
msgSendBufPtr = (mach_msg_header_t *) malloc(msgSendBufLength);
receivedMessage = ( mach_msg_header_t * ) replyMsg;
// Call DNSServiceDiscoveryReply_server to change mig-generated message into a
// genuine mach message. It will then cause the callback to get called.
result = DNSServiceDiscoveryReply_server ( receivedMessage, msgSendBufPtr );
( void ) mach_msg_send ( msgSendBufPtr );
free(msgSendBufPtr);
}
mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDiscovery)
{
return dnsServiceDiscovery->port;
}
void DNSServiceDiscoveryDeallocate(dns_service_discovery_ref dnsServiceDiscovery)
{
struct a_requests *request0, *request;
mach_port_t reply = dnsServiceDiscovery->port;
if (dnsServiceDiscovery->port) {
pthread_mutex_lock(&a_requests_lock);
request0 = NULL;
request = a_requests;
while (request) {
if (request->client_port == reply) {
/* request info found, remove from list */
if (request0) {
request0->next = request->next;
} else {
a_requests = request->next;
}
break;
} else {
/* not info for this request, skip to next */
request0 = request;
request = request->next;
}
}
pthread_mutex_unlock(&a_requests_lock);
free(request);
mach_port_destroy(mach_task_self(), dnsServiceDiscovery->port);
free(dnsServiceDiscovery);
}
return;
}
// reply functions, calls the users setup callbacks with function pointers
kern_return_t internal_DNSServiceDomainEnumerationReply_rpc
(
mach_port_t reply,
int resultType,
DNSCString replyDomain,
int flags
)
{
struct a_requests *request;
void *requestContext = NULL;
DNSServiceDomainEnumerationReply callback = NULL;
pthread_mutex_lock(&a_requests_lock);
request = a_requests;
while (request) {
if (request->client_port == reply) {
break;
}
request = request->next;
}
if (request != NULL) {
callback = (*request->callout.enumCallback);
requestContext = request->context;
}
pthread_mutex_unlock(&a_requests_lock);
if (request != NULL) {
(callback)(resultType, replyDomain, flags, requestContext);
}
return KERN_SUCCESS;
}
kern_return_t internal_DNSServiceBrowserReply_rpc
(
mach_port_t reply,
int resultType,
DNSCString replyName,
DNSCString replyType,
DNSCString replyDomain,
int flags
)
{
struct a_requests *request;
void *requestContext = NULL;
DNSServiceBrowserReply callback = NULL;
pthread_mutex_lock(&a_requests_lock);
request = a_requests;
while (request) {
if (request->client_port == reply) {
break;
}
request = request->next;
}
if (request != NULL) {
callback = (*request->callout.browserCallback);
requestContext = request->context;
}
pthread_mutex_unlock(&a_requests_lock);
if (request != NULL) {
(callback)(resultType, replyName, replyType, replyDomain, flags, requestContext);
}
return KERN_SUCCESS;
}
kern_return_t internal_DNSServiceRegistrationReply_rpc
(
mach_port_t reply,
int resultType
)
{
struct a_requests *request;
void *requestContext = NULL;
DNSServiceRegistrationReply callback = NULL;
pthread_mutex_lock(&a_requests_lock);
request = a_requests;
while (request) {
if (request->client_port == reply) {
break;
}
request = request->next;
}
if (request != NULL) {
callback = (*request->callout.regCallback);
requestContext = request->context;
}
pthread_mutex_unlock(&a_requests_lock);
if (request != NULL) {
(callback)(resultType, requestContext);
}
return KERN_SUCCESS;
}
kern_return_t internal_DNSServiceResolverReply_rpc
(
mach_port_t reply,
sockaddr_t interface,
sockaddr_t address,
DNSCString txtRecord,
int flags
)
{
struct sockaddr *interface_storage = NULL;
struct sockaddr *address_storage = NULL;
struct a_requests *request;
void *requestContext = NULL;
DNSServiceResolverReply callback = NULL;
if (interface) {
int len = ((struct sockaddr *)interface)->sa_len;
interface_storage = (struct sockaddr *)malloc(len);
memcpy(interface_storage, interface, len);
}
if (address) {
int len = ((struct sockaddr *)address)->sa_len;
address_storage = (struct sockaddr *)malloc(len);
memcpy(address_storage, address, len);
}
pthread_mutex_lock(&a_requests_lock);
request = a_requests;
while (request) {
if (request->client_port == reply) {
break;
}
request = request->next;
}
if (request != NULL) {
callback = (*request->callout.resolveCallback);
requestContext = request->context;
}
pthread_mutex_unlock(&a_requests_lock);
if (request != NULL) {
(callback)(interface_storage, address_storage, txtRecord, flags, requestContext);
}
if (interface) {
free(interface_storage);
}
if (address) {
free(address_storage);
}
return KERN_SUCCESS;
}